目录
解决低版本apk预制到高版本系统中出现丢失so库的问题,以及默认给予第三方apk添加特殊应用权限

前段时间在做一个学习平板的项目,客户的apk让我预制到系统中,但是客户的apk版本又很低,让我放到十三的系统中,用常规的方法(通过在package中的Android.mk文件预制)预制到系统中会出现丢失so库,导致客户的apk用不了,客户又不愿意改apk版本,让我自己想办法解决,这个bug搞了好久,现在有一种解决的办法,就是通过静默安装的方式可以实现这个需求,但是又因为安卓十三不适用以前的静默安装的方法,后面通过io流去静默安装的apk,解决逻辑就是这样,下面看实现方法。
首先是自己在init.rc文件中添加一个设备节点,这个节点的作用是在机器的内部存储路径创建一个自定义的文件夹

plaintext
复制代码
diff --git a/device/mediatek/mt6765/init.mt6765.rc b/device/mediatek/mt6765/init.mt6765.rc
index adc0aaf1e02..fd1081707fa 100755
--- a/device/mediatek/mt6765/init.mt6765.rc
+++ b/device/mediatek/mt6765/init.mt6765.rc
@@ -220,6 +220,11 @@ on post-fs-data
     mkdir /mnt/vendor/cct
     chown root system /mnt/vendor/cct
     chmod 0771 /mnt/vendor/cct
+       
+       #Create Apks DIR
+    mkdir /storage/emulated/0/Apks
+    chown root system /storage/emulated/0/Apks
+    chmod 0777 /storage/emulated/0/Apks
 
     #Create flash folder
     mkdir /data/vendor/flash

注:因为我使用的是mt6765的机器,这个根据自己所用的机器型号为准
然后再添加一个开机拷贝和静默安装的服务,如下:

plaintext
复制代码
diff --git a/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/CopyFileService.java b/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/CopyFileService.java
new file mode 100755
index 00000000000..e7d23a32cb1
--- /dev/null
+++ b/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/CopyFileService.java
@@ -0,0 +1,165 @@
+package com.android.settings; 
+ 
+import android.app.Service; 
+import android.content.Intent; 
+import android.os.Environment; 
+import android.os.IBinder; 
+import android.util.Log; 
+import java.io.File; 
+import java.io.FileInputStream; 
+import java.io.FileOutputStream; 
+import java.io.IOException; 
+import java.io.InputStream; 
+import android.net.Uri; 
+import android.content.Context; 
+import android.media.MediaScannerConnection; 
+ 
+public class CopyFileService extends Service { 
+ 
+    private final static String TAG = "Monkey_CopyFileService"; 
+    private static String PRELOAD_SRC = Environment.getRootDirectory()+"/media/apks"; 
+    private static String PRELOAD_DEST = "/storage/emulated/0/Apks"; 
+    private static Context mContext; 
+ 
+    private CopyThread mCopyThread; 
+ 
+    @Override 
+    public IBinder onBind(Intent intent) { 
+        return null; 
+    } 
+ 
+    @Override 
+    public void onCreate() { 
+        Log.d(TAG,"CopyFileService---------------"); 
+        super.onCreate(); 
+    } 
+ 
+    @Override 
+    public int onStartCommand(Intent intent, int flags, int startId) { 
+        Log.d(TAG,"onStartCommand---------------"); 
+        mContext = getApplicationContext(); 
+        if (mCopyThread == null) { 
+            mCopyThread = new CopyThread(); 
+            mCopyThread.start(); 
+        } 
+        return Service.START_REDELIVER_INTENT; 
+    } 
+ 
+    public class CopyThread extends Thread { 
+        @Override 
+        public void run() { 
+            if (copyFolder(new File(PRELOAD_SRC), new File(PRELOAD_DEST))) { 
+                Log.e(TAG, "doPreloadMedia success !"); 
+            } else { 
+                Log.e(TAG, "doPreloadMedia failed 。"); 
+            } 
+            Log.e(TAG, "PRELOAD_SRC:"+PRELOAD_SRC); 
+            Log.e(TAG, "PRELOAD_DEST:"+PRELOAD_DEST); 
+            //fileScan(PRELOAD_DEST); 
+            updateMedia(PRELOAD_DEST); 
+            Intent intent = new Intent(); 
+            intent.setAction("android.intent.action.installapk"); 
+            mContext.sendBroadcast(intent); 
+            Log.d(TAG, "Send installapk intent!"); 
+            stopSelf(); 
+        } 
+    } 
+ 
+    public static void updateMedia(String filename){ 
+        MediaScannerConnection.scanFile(mContext, 
+                new String[] { filename }, null, 
+                new MediaScannerConnection.OnScanCompletedListener() { 
+                    public void onScanCompleted(String path, Uri uri) { 
+                    } 
+                }); 
+    } 
+ 
+    public void fileScan(String file) { 
+        Uri data = Uri.parse("file://" + file); 
+        Log.d("TAG", "file:" + file); 
+        sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, data)); 
+    } 
+ 
+    public boolean copyFolder(File srcFile, File destFile) { 
+        if (!srcFile.isDirectory()) { 
+            Log.i(TAG, "srcFile no isDirectory :"+srcFile.getName()); 
+            return false; 
+        } 
+        if (!srcFile.canRead()) { 
+            Log.i(TAG, "no canRead :"+srcFile.getName()); 
+        } 
+        if (!srcFile.canWrite()) { 
+            Log.i(TAG, "no canWrite :"+srcFile.getName()); 
+        } 
+ 
+        if (!destFile.exists()) { 
+            boolean r = false; 
+            synchronized (this) { 
+                r = destFile.mkdirs(); 
+            } 
+            Log.i(TAG, "destFile:exists " + r); 
+        } 
+        boolean result = true; 
+        File[] list = srcFile.listFiles(); 
+ 
+        if (list == null) { 
+            Log.i(TAG, "copyFolder: list null =" + srcFile.getName()); 
+            return false; 
+        } 
+        for (File f : list) { 
+            Log.i(TAG,"f.getName():"+f.getName()); 
+            if (f.isDirectory()) { 
+                result &= copyFolder(f, new File(destFile, f.getName())); 
+            } else if(f.isFile()){ 
+                result &= copyFile(f, new File(destFile, f.getName())); 
+            } 
+        } 
+        Log.i(TAG,"result:"+result); 
+        return result; 
+    } 
+ 
+    public boolean copyFile(File srcFile, File destFile) { 
+        boolean result = false; 
+        try { 
+            InputStream in = new FileInputStream(srcFile); 
+            try { 
+                result = copyToFile(in, destFile); 
+            } finally { 
+                in.close(); 
+            } 
+        } catch (IOException e) { 
+            result = false; 
+        } 
+        return result; 
+    } 
+ 
+    /** 
+     * Copy data from a source stream to destFile. 
+     * Return true if succeed, return false if failed. 
+     */ 
+    public boolean copyToFile(InputStream inputStream, File destFile) { 
+        try { 
+            if (destFile.exists()) { 
+                destFile.delete(); 
+            } 
+            FileOutputStream out = new FileOutputStream(destFile); 
+            try { 
+                byte[] buffer = new byte[4096]; 
+                int bytesRead; 
+                while ((bytesRead = inputStream.read(buffer)) >= 0) { 
+                    out.write(buffer, 0, bytesRead); 
+                } 
+            } finally { 
+                out.flush(); 
+                try { 
+                    out.getFD().sync(); 
+                } catch (IOException e) { 
+                } 
+                out.close(); 
+            } 
+            return true; 
+        } catch (IOException e) { 
+            return false; 
+        } 
+    } 
+}

注:拷贝完成的标志是:updateMedia(PRELOAD_DEST);在拷贝完成后发送一个静默安装的广播

plaintext
复制代码
diff --git a/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/InstallApkService.java b/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/InstallApkService.java
new file mode 100755
index 00000000000..b8b7783d568
--- /dev/null
+++ b/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/InstallApkService.java
@@ -0,0 +1,270 @@
+package com.android.settings;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Environment;
+import android.os.IBinder;
+import android.util.Log;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import android.net.Uri;
+import android.content.Context;
+import android.media.MediaScannerConnection;
+
+import android.content.SharedPreferences;
+import android.os.Build;
+import android.content.pm.PackageManager;
+import android.os.SystemProperties;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.text.TextUtils;
+
+import android.app.PendingIntent;
+import android.content.pm.PackageInstaller;
+
+public class InstallApkService extends Service {
+
+    private final static String TAG = "InstallApkService";
+    private static String apksPath = "/storage/emulated/0/Apks/";
+    private static Context mContext;
+    private InstallThread mInstallThread;
+    private PackageManager mPackageManager;
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public void onCreate() {
+        Log.d(TAG,"InstallApkService---------------");
+        super.onCreate();
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.d(TAG,"onStartCommand---------------");
+        mContext = getApplicationContext();
+        if (mInstallThread == null) {
+            mInstallThread = new InstallThread();
+            mInstallThread.start();
+        }
+        return Service.START_REDELIVER_INTENT;
+    }
+
+    public class InstallThread extends Thread {
+        @Override
+        public void run() {
+            try{
+                File apksFile = new File(apksPath);
+                mPackageManager = mContext.getPackageManager();
+                Log.d(TAG, "  apksFile = " + apksFile);
+                if (apksFile.exists()) {
+                    File[] files = apksFile.listFiles();
+                    for (File f:files) {//遍历路径下所有文件
+                        String filename = f.getName();
+                        String type = filename.substring(filename.lastIndexOf(".")+1);
+                        Log.d(TAG, "filename : " + filename + "  type : " + type);
+                        if(type != null && type.equals("apk")) {
+                            String packageName = getApkInfo(mContext, apksPath + filename);
+                            Log.d(TAG, "packageName : " + packageName);
+                            if (!isInstallApp(mContext, packageName)) {
+                                String apk = apksPath + filename;
+                                Log.d(TAG, "apk =" + apk);
+                                installApp(mContext, apk);
+                            }
+                        }
+                    }
+                }
+                /* String packageName = getApkInfo(mContext, "/storage/emulated/0/Apks/lazyxxmk.apk");
+                Log.d(TAG, "packageName : " + packageName);
+                if(!isInstallApp(mContext, packageName)) {
+                    String apk = "/storage/emulated/0/Apks/lazyxxmk.apk";
+                    Log.d(TAG, "apk =" + apk);
+                    installApp(mContext, apk);
+                } */
+            }catch(Exception e){
+                Log.d(TAG,"onReceive:install failed:" + e);
+            }
+            stopSelf();
+        }
+    }
+
+    /*
+    路径权限
+     */
+    private void updateDataFilePermission(String path) {
+        String[] command = {"chmod", "777", path};
+        ProcessBuilder builder = new ProcessBuilder(command);
+        try {
+            builder.start();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static void installApp(Context mContext, String apkFilePath) {
+        int mSessionId = -1;
+        Log.d(TAG, "installApp()------->" + apkFilePath);
+        File apkFile = new File(apkFilePath);
+        if (!apkFile.exists()) {
+            Log.d(TAG, "file not exit");
+        }
+        PackageInfo packageInfo = mContext.getPackageManager().getPackageArchiveInfo(apkFilePath, PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES);
+        if (packageInfo != null) {
+            String packageName = packageInfo.packageName;
+            int versionCode = packageInfo.versionCode;
+            String versionName = packageInfo.versionName;
+            Log.d(TAG, "packageName=" + packageName + ", versionCode=" + versionCode + ", versionName=" + versionName);
+        }
+        PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
+        PackageInstaller.SessionParams sessionParams
+                = new PackageInstaller.SessionParams(PackageInstaller
+                .SessionParams.MODE_FULL_INSTALL);
+        Log.d(TAG, "apkFile length = " + apkFile.length());
+        sessionParams.setSize(apkFile.length());
+        try {
+            mSessionId = packageInstaller.createSession(sessionParams);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        Log.d(TAG, "sessionId---->" + mSessionId);
+        if (mSessionId != -1) {
+            boolean copySuccess = onTransfesApkFile(mContext, apkFilePath, mSessionId);
+            Log.d(TAG, "copySuccess---->" + copySuccess);
+            if (copySuccess) {
+                execInstallAPP(mContext, mSessionId);
+            }
+        }
+    }
+
+    /**
+     * 执行安装并通知安装结果
+     */
+    private static void execInstallAPP(Context mContext, int mSessionId) {
+        Log.d(TAG, "--------------------->execInstallAPP()<------------------");
+        PackageInstaller.Session session = null;
+        try {
+            session = mContext.getPackageManager().getPackageInstaller().openSession(mSessionId);
+            Intent intent = new Intent();
+            PendingIntent pendingIntent;
+            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
+                Log.d(TAG, "--------------FLAG_IMMUTABLE");
+                pendingIntent = PendingIntent.getBroadcast(mContext, 1, intent, PendingIntent.FLAG_IMMUTABLE);
+            } else {
+                Log.d(TAG, "--------------FLAG_UPDATE_CURRENT");
+                pendingIntent = PendingIntent.getBroadcast(mContext, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+            }
+            Log.d(TAG, "-----------pendingIntent");
+            session.commit(pendingIntent.getIntentSender());
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            if (null != session) {
+                session.close();
+            }
+        }
+    }
+
+    /**
+     * 通过文件流传输apk
+     *
+     * @param apkFilePath
+     * @return
+     */
+    private static boolean onTransfesApkFile(Context mContext, String apkFilePath, int mSessionId) {
+        Log.d(TAG, "---------->onTransfesApkFile()<---------------------");
+        InputStream in = null;
+        OutputStream out = null;
+        PackageInstaller.Session session = null;
+        boolean success = false;
+        try {
+            File apkFile = new File(apkFilePath);
+            session = mContext.getPackageManager().getPackageInstaller().openSession(mSessionId);
+            out = session.openWrite("base.apk", 0, apkFile.length());
+            in = new FileInputStream(apkFile);
+            int total = 0, c;
+            byte[] buffer = new byte[1024 * 1024];
+            while ((c = in.read(buffer)) != -1) {
+                total += c;
+                out.write(buffer, 0, c);
+            }
+            session.fsync(out);
+            Log.d(TAG, "streamed " + total + " bytes");
+            success = true;
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            if (null != session) {
+                session.close();
+            }
+            try {
+                if (null != out) {
+                    out.close();
+                }
+                if (null != in) {
+                    in.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return success;
+    }
+
+    public static void uninstall(Context mContext, String packageName) {
+
+        Intent broadcastIntent = new Intent();
+        PendingIntent pendingIntent;
+        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
+            Log.d(TAG, "--------------FLAG_IMMUTABLE");
+            pendingIntent = PendingIntent.getBroadcast(mContext, 1, broadcastIntent, PendingIntent.FLAG_IMMUTABLE);
+        } else {
+            Log.d(TAG, "--------------FLAG_UPDATE_CURRENT");
+            pendingIntent = PendingIntent.getBroadcast(mContext, 1, broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+        }
+        PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
+        packageInstaller.uninstall(packageName, pendingIntent.getIntentSender());
+    }
+
+    public static String getExternalStoragePath() {
+        String storagePath = Environment.getExternalStorageDirectory().getAbsolutePath();
+        try {
+            Log.d(TAG, "getExternalStoragePath path=" + storagePath);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "IllegalArgumentException when getExternalStoragePath:" + e);
+        }
+        Log.d(TAG, "getExternalStoragePath path=" + storagePath);
+        return storagePath ;
+    }
+
+    public static String getApkInfo(Context context, String apkPath) {
+        PackageManager pm = context.getPackageManager();
+        PackageInfo info = pm.getPackageArchiveInfo(apkPath, PackageManager.GET_ACTIVITIES);
+        if (info != null) {
+            ApplicationInfo appInfo = info.applicationInfo;
+            String packageName = appInfo.packageName;
+            try {
+                return packageName;
+            } catch (OutOfMemoryError e) {
+
+            }
+        }
+        return null;
+    }
+
+    boolean isInstallApp(Context context,String packageName){
+        try {
+            mPackageManager.getApplicationInfo(packageName,PackageManager.GET_UNINSTALLED_PACKAGES);
+            return true;
+        } catch (NameNotFoundException e) {
+            // TODO: handle exception
+            return false;
+        }
+    }
+}

在AndroidManifest去注册一下:

plaintext
复制代码
diff --git a/vendor/mediatek/proprietary/packages/apps/MtkSettings/AndroidManifest.xml b/vendor/mediatek/proprietary/packages/apps/MtkSettings/AndroidManifest.xml
index 1ef5cb72af6..d75fd575810 100755
--- a/vendor/mediatek/proprietary/packages/apps/MtkSettings/AndroidManifest.xml
+++ b/vendor/mediatek/proprietary/packages/apps/MtkSettings/AndroidManifest.xml
@@ -708,6 +708,12 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
+               
+                <service android:name="com.android.settings.CopyFileService"
+            android:exported="true"/>
+ 
+        <service android:name="com.android.settings.InstallApkService"
+            android:exported="true"/>
 
         <service android:name=".wifi.tether.TetherService"
             android:exported="true"

这两个服务写完,还有一个问题需要解决,就是怎么让他开机的时候去拷贝,方法如下:

plaintext
复制代码
diff --git a/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigReceiver.java b/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigReceiver.java
old mode 100644
new mode 100755
index 369e61382db..ea69f01f8fd
--- a/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigReceiver.java
+++ b/vendor/mediatek/proprietary/packages/apps/MtkSettings/src/com/android/settings/fuelgauge/batterytip/AnomalyConfigReceiver.java
@@ -21,6 +21,12 @@ import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.util.Log;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+
+import static com.android.settingslib.Utils.updateLocationEnabled;
+import android.provider.Settings;
+import android.os.UserHandle;
 
 /**
  * Receive broadcast when {@link StatsManager} restart, then check the anomaly config and
@@ -31,6 +37,16 @@ public class AnomalyConfigReceiver extends BroadcastReceiver {
 
     @Override
     public void onReceive(Context context, Intent intent) {
+               SharedPreferences sharedPreferences = context.getSharedPreferences("first_boot",Context.MODE_PRIVATE);
+        boolean first_boot= sharedPreferences.getBoolean("first_boot", true);
+        if (first_boot) {
+            //SharedPreferences.Editor editor1 = sharedPreferences.edit();
+            //editor1.putBoolean("first_boot", false);
+            //editor1.apply();
+            Intent activityIntent = new Intent();
+            activityIntent.setClassName("com.android.settings", "com.android.settings.CopyFileService");
+            context.startService(activityIntent);
+        }
         if (StatsManager.ACTION_STATSD_STARTED.equals(intent.getAction())
                 || Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
             final StatsManager statsManager = context.getSystemService(StatsManager.class);

以及还要添加一个静默安装的广播接收者:

plaintext
复制代码
diff --git a/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java b/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 94e0307df92..12b14aa1914 100755
--- a/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2081,6 +2081,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
         filter = new IntentFilter();
         filter.addAction(Intent.ACTION_DREAMING_STARTED);
         filter.addAction(Intent.ACTION_DREAMING_STOPPED);
+               filter.addAction("android.intent.action.installapk");
         context.registerReceiver(mDreamReceiver, filter);
 
         // register for multiuser-relevant broadcasts
@@ -4518,6 +4519,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                 if (mKeyguardDelegate != null) {
                     mKeyguardDelegate.onDreamingStopped();
                 }
+                       } else if ("android.intent.action.installapk".equals(intent.getAction())) {
+                Intent apkInstallIntent = new Intent();
+                apkInstallIntent.setClassName("com.android.settings", "com.android.settings.InstallApkService");
+                context.startService(apkInstallIntent);
             }
         }
     };

最后把需要安装的apk放在device/mediatek/common/mid/common/system/media/apks/路径下就可以了,在安卓源码编译中,放在这个路径下,它在编译完以后是在out/target/product/mssi_t_64_cn/system/media/apks/下面,我在拷贝服务里面的路径写的就是这个路径,在机器开机以后会把这个路径下的apk拷贝到/storage/emulated/0/Apks下面,然后就是静默安装了。
第三方apk添加特殊应用权限,在安卓十三的版本中不是在frameworks/base/core/java/android/app/AppOpsManager.java中,我改了好多次都没啥用,后面我问了公司的同事,他说是在alps_mssi/frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java这个类中,方法如下:

plaintext
复制代码
diff --git a/alps_mssi/frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java b/alps_mssi/frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java
old mode 100644
new mode 100755
index e3c5e1d7d28..83a646c90cd
--- a/alps_mssi/frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/alps_mssi/frameworks/base/services/core/java/com/android/server/appop/AppOpsService.java
@@ -4823,6 +4823,7 @@ public class AppOpsService extends IAppOpsService.Stub {
             return null;
         }
 
+        edit = true;
         if (uidState.pkgOps == null) {
             if (!edit) {
                 return null;
@@ -4836,6 +4837,17 @@ public class AppOpsService extends IAppOpsService.Stub {
                 return null;
             }
             ops = new Ops(packageName, uidState);
+            Op op = new Op(uidState, packageName, AppOpsManager.OP_SYSTEM_ALERT_WINDOW, uidState.uid);
+            op.mode = AppOpsManager.MODE_ALLOWED;
+            ops.put(op.op, op);
+                       
+            Op op1 = new Op(uidState, packageName, AppOpsManager.OP_GET_USAGE_STATS, uidState.uid);
+            op1.mode = AppOpsManager.MODE_ALLOWED;
+            ops.put(op1.op, op1);
+                       
+            Op op2 = new Op(uidState, packageName, AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE, uidState.uid);
+            op2.mode = AppOpsManager.MODE_ALLOWED;
+            ops.put(op2.op, op2);
             uidState.pkgOps.put(packageName, ops);
         }

这三个权限是:所有文件访问权限、显示在其他应用上层、使用情况访问权限,具体加什么看自己的需求,那个拷贝的方法不单单可以拷贝apk,视频、音频文件都可以拷贝,以及静默安装的方法也是支持多个apk静默安装的

本文由 春风不识路 原创发布于 阳光沙滩 , 未经作者授权,禁止转载
评论
0 / 1024
用户头像
大佬出品必属精品
2023-07-20 12:39回复
回复@断点: 我是菜鸡
2023-07-21 03:38回复
回复@春风不识路: [doge]
2023-07-21 06:35回复
推荐文章
一篇文章讲清楚雅思、托福、GRE、SAT、GMAT:它们是什么?有什么区别?应该考哪个?
面对IELTS、TOEFL、GRE、SAT和GMAT,你是否感到困惑?本文深度解析五大考试的核心区别:从语言证明到学术能力评估,明确各自适用人群与场景。特别更新2026年托福改革及最新评分体系,通过对比表格与真实申请案例,助你精准定位目标考试,避免无效备考,高效规划海外升学之路。
一篇文章讲清楚 Android 签名:PK8、X509.PEM、JKS 是什么?它们有什么关系?
深入解析Android应用与系统签名的核心文件:PK8、X509.PEM与JKS。本文从非对称加密原理出发,厘清私钥、证书与密钥库的本质区别及相互转换方法,涵盖AOSP平台签名与普通应用发布的实际场景。通过对比V1-V4签名方案及解答常见疑难,助你掌握保障APK完整性与身份认证的关键技术,规避签名丢失或冲突风险。
系统监控运维系统
你是否也深受证书过期、磁盘满载等运维痛点的折磨?排查耗时,影响线上体验。本文分享一款自研运维系统面板,集成证书统一监测、资源预警及WebSSH等功能,实现问题快速定位与处理。附代码共享链接,欢迎参考或二次开发,提升运维效率。
断点说搞个万年历,怎么搞?
想快速拥有一个专属的程序员日历?只需一篇清晰的设计文档,剩下的交给AI!本文带你从零搭建“sob-calendar”项目,集成农历、节气及独特的程序员运势功能。通过实际案例展示如何利用AI高效生成代码并部署上线,让繁琐的开发工作变得轻松有趣,快来体验科技与传统历法的奇妙碰撞吧!
linux服务器的日志、容器清理
本手册提供了一套全面的Linux服务器磁盘清理与排查方法,帮助用户高效管理磁盘空间。通过查看磁盘使用情况、分析Docker占用、清理无用容器和日志,确保系统运行流畅。适合需要优化服务器性能的运维人员参考。
记给网站接入图形验证码
本文分享了如何为个人站点添加图形验证码以提升安全性,详细介绍了使用第三方接口实现验证码功能的过程,并通过代码示例展示了具体实现方式。对于关注网站安全的开发者来说,这篇文章提供了实用的参考价值。
Java开发环境搭建
本文是一份详细的Java开发环境搭建指南,涵盖了Java的发展历史、版本选择及安装配置过程。无论你是初学者还是有经验的开发者,都能从中找到实用的信息和步骤,帮助你快速上手Java开发。
Vue3.js项目的创建
本文详细介绍了如何使用Vue3创建项目,包括Node.js的安装和配置,以及项目的初始化过程。适合初学者学习如何快速搭建Vue3开发环境,并推荐了相关课程供进一步学习。
Network Tools : 您的运维“助手”升级了
Network Tools是一款专为解决运维难题而设计的软件,集设备管理、配置备份、性能监控和告警处理于一体。通过统一的工作台,帮助运维人员更高效地管理资产、定位故障并提升团队协作效率。无论是设备在线状态还是具体问题排查,都能得到全面支持。
Ubuntu 软件安装与彻底卸载指南
本文详细介绍了在 Ubuntu 系统中如何规范安装和彻底卸载 .deb 格式软件,包括依赖修复、残留清理以及图形化工具推荐,适合需要维护系统环境的用户。
JavaWeb开发客户程序更新特殊环境下的骚操作
本文分享了在无外网环境下,如何利用Docker和docker-compose实现高效部署的实战经验。通过基础镜像和本地jar包挂载的方式,解决了内网环境下的依赖问题,适合需要灵活部署方案的技术人员参考。
Linux 防火墙完全指南:从原理到多发行版实战
本文全面解析Linux防火墙的底层原理与主流工具配置,涵盖UFW、firewalld、iptables、nftables等方案,适合不同发行版用户。从基础概念到生产环境加固建议,助你掌握核心安全策略,确保服务器与网络的安全运行。
水上派对英语单词篇
探索FLOATOPIA水上派对的完整指南,涵盖签到流程、场地指引、安全须知、活动安排及夜场舞会等实用英语词汇与对话。无论是初学者还是老手,都能轻松掌握关键术语,享受完美水上体验。
麒麟操作系统下使用 virt-customize 重置虚拟机 root 密码实战
本文详细介绍了在麒麟操作系统环境下使用 virt-customize 工具重置 KVM 虚拟机 root 密码的完整流程,包含安装步骤、常见问题及解决方案,帮助运维人员高效处理密码遗忘或批量初始化场景,避免数据丢失。
浅析JS预编译-函数篇
本文详细讲解了JavaScript中函数的预编译过程,包括AO对象的创建、变量和函数声明的提升,以及执行阶段的变化。适合初学者了解JS执行机制,帮助深入理解代码运行逻辑。
访问Github的另一种姿势
本文介绍了如何通过Watt ToolKit等工具加速访问Github,适合对网络技术感兴趣的读者。内容涵盖工具介绍、下载和使用方法,以及一些实用技巧,值得一看。
使用Gulp压缩JS
本文详细介绍了如何使用Gulp压缩JavaScript文件,特别是支持ES6语法的处理方法。通过实际操作和效果对比,帮助开发者提升代码性能,适合对前端自动化工具感兴趣的读者。
使用Gulp压缩CSS
本文详细介绍了如何使用Gulp工具对CSS文件进行压缩,提升网页加载速度。适合需要优化静态资源的开发者,提供清晰的步骤和示例,帮助快速上手。
从0部署Nuxtjs项目
本文详细介绍了如何将Nuxt.js项目部署到公网,涵盖Node.js安装、环境配置、源码上传及使用PM2启动服务的全过程,适合开发者学习和实践。
Linux 命令行美化利器:tree 命令完全指南(安装 + 全部参数详解)
掌握 Linux 下的 `tree` 命令,快速直观地查看目录结构。本文详细讲解安装方法、参数含义及实战场景,适合开发者和系统管理员提升工作效率。
Linux常用命令(一)
本文详细介绍了Linux基础命令,包括文件操作、系统显示、网络状态、软件包管理等,适合初学者学习和查阅,是掌握Linux系统的实用指南。
大模型推理参数解码:温度、Top-p 与核采样如何塑造生成文本的灵魂
探索大语言模型的采样参数如何塑造生成内容,从温度到Top-p,从惩罚机制到熵控制,揭示背后的信息论原理与实践策略。了解如何通过参数调优,让AI既保持知识的严谨,又拥有创造力的自由。
记从0使用Claude code写代码
本文介绍了如何使用Claude Code搭配DeepSeek进行编程,详细讲解了安装Node.js、全局安装Claude、配置模型以及使用方法。对于开发者来说,这是一份实用的技术教程,尤其适合对AI编程工具感兴趣的读者。
Android-Studio-Gradle同步失败-Library为null的通用排查指南
遇到 Android Studio Gradle Sync 失败时,不要轻易删除全局缓存。本文详细解析了错误原因,并提供了一系列精准的排查步骤和解决方案,帮助开发者快速定位并解决问题,提升开发效率。
PHP实现密码加密
本文详细介绍了Bcrypt密码加密技术及其在PHP中的应用,帮助开发者提升用户密码的安全性。通过实际代码示例,展示了如何使用password_hash和password_verify函数进行密码加密与验证,是学习网络安全知识的实用指南。
WordCloud效果,滚动标签
本文详细介绍了如何在Vue项目中集成WordCloud组件,包括依赖安装、属性配置和使用方法。适合开发者学习如何实现数据可视化功能,提升项目交互体验。
从0使用WordPress搭建一个优美的网站
本文详细介绍了如何使用WordPress搭建一个美观的网站,从安装到主题配置和功能拓展,为读者提供了实用的操作指南。无论是初学者还是有一定经验的开发者,都能从中获得有价值的参考。
JS实现在网站底部添加运行时间
想知道如何在网站底部显示运行时间?本文详细讲解了通过JavaScript实现这一功能的方法,包括时间计算逻辑和代码实现。适合前端开发者学习参考,轻松为网站添加实用功能。
在Vercel上部署Hexo博客
本文详细介绍了如何在Vercel上快速部署Hexo博客,无需后端服务即可实现高效发布。相比传统方式,省去了手动生成和上传静态文件的步骤,更加便捷。适合想要搭建个人博客的开发者参考。
从0搭建一个Hexo博客
本文详细介绍了Hexo博客框架的使用方法,从安装到部署全流程讲解,适合想快速搭建个人博客的技术爱好者。内容清晰易懂,是入门Hexo的理想指南。