天天看點

Android rom開發:第三方app開放全部權限+支援靜默安裝解除安裝

開發平台:

高通 android 7.1

需求:

app不帶系統簽名,僅僅以反射的方式調用PackageManager類的installPackage、deletePackage方法,實作靜默安裝解除安裝。

背景知識:

正常情況下,靜默安裝解除安裝需要以下2個系統級權限+系統簽名

<uses-permission android:name="android.permission.DELETE_PACKAGES" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
           

解決思路如下:

首先,沒有系統簽名的情況下,安裝app的時候這2個權限不會放開,是以先修改PackageManagerService.java->grantPermissionsLPw方法

+        //允許app全部權限
+        mPkgList.add("com.android.testa");
+        mPkgList.add("com.android.testb");
+        if (mPkgList.contains(pkg.packageName)) {
+            final int permsSize = pkg.requestedPermissions.size();
+            for (int i = 0; i < permsSize; i++) {
+                final String name = pkg.requestedPermissions.get(i);
+                final BasePermission bp = mSettings.mPermissions.get(name);
+                if (null != bp && permissionsState.grantInstallPermission(bp) != PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                    Slog.d(TAG, "grant permission " + name + " to package " + pkg.packageName);
+                    changedInstallPermission = true;
+                }
+            }
+        }
+
         if ((changedInstallPermission || replace) && !ps.installPermissionsFixed &&
                 !isSystemApp(ps) || isUpdatedSystemApp(ps)){
             // This is the first that we have heard about this package, so the
           

這一步修改後,mmm frameworks/base/services,然後重新替換/system/framework/service.jar驗證,靜默安裝ok,靜默解除安裝不ok,但是log裡面沒有顯示權限問題,說明權限實際上已經具備,其他地方出了問題。

繼續讀源碼,追蹤代碼繼承關系和調用邏輯:

1.PackageManagerService.java繼承IPackageManager.Stub

2.ApplicationPackageManager.java繼承PackageManager.java

3.反射調用PackageManager.java的installPackage deletePackage方法,實際上是調用ApplicationPackageManager.java裡面的方法

ApplicationPackageManager.java裡面的對應源碼:
/** @hide */
public class ApplicationPackageManager extends PackageManager {

//install
    @Override
    public void installPackage(Uri packageURI, PackageInstallObserver observer,
            int flags, String installerPackageName) {
        installCommon(packageURI, observer, flags, installerPackageName, mContext.getUserId());
    }

    private void installCommon(Uri packageURI,
            PackageInstallObserver observer, int flags, String installerPackageName,
            int userId) {
        if (!"file".equals(packageURI.getScheme())) {
            throw new UnsupportedOperationException("Only file:// URIs are supported");
        }

        final String originPath = packageURI.getPath();
        try {
            mPM.installPackageAsUser(originPath, observer.getBinder(), flags, installerPackageName,
                    userId);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

//delete
    @Override
    public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) {
        deletePackageAsUser(packageName, observer, flags, mContext.getUserId());
    }

    @Override
    public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer, int flags,
            int userId) {
        try {
            mPM.deletePackageAsUser(packageName, observer, userId, flags);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

//實際上是通過aidl調用mPM的方法,mPM是一個IPackageManager對象,在建立ApplicationPackageManager對象的時候指派
    ApplicationPackageManager(ContextImpl context,
                              IPackageManager pm) {
        mContext = context;
        mPM = pm;
    }
           

4.從ApplicationPackageManager源碼可以看到,安裝解除安裝是通過aidl調用mPM的方法,mPM是一個IPackageManager對象,在建立ApplicationPackageManager對象的時候指派。

5.PackageManagerService.java繼承IPackageManager.Stub,實際上就調用了PackageManagerService.java裡面的installPackageAsUser和deletePackageAsUser方法,是以本質上靜默安裝解除安裝都是在PackageManagerService.java裡面實作的。

6.仔細檢視PackageManagerService.java解除安裝的代碼邏輯,注意看isCallerAllowedToSilentlyUninstall方法,從命名就可以看出,這裡是判斷是否允許調用者執行靜默解除安裝,顯然第三方app在預設情況下是傳回的false,是以無法繼續執行靜默解除安裝的邏輯,并且從PMS源碼看,不會抛出什麼異常資訊,實測加列印确實如此。

@Override
public void deletePackage(final String packageName,
        final IPackageDeleteObserver2 observer, final int userId, final int deleteFlags) {
    //如果app的清單檔案裡面沒有申明解除安裝權限,那麼這一句會抛出異常,提示沒有權限
    mContext.enforceCallingOrSelfPermission(
            android.Manifest.permission.DELETE_PACKAGES, null);
    Preconditions.checkNotNull(packageName);
    Preconditions.checkNotNull(observer);
    final int uid = Binder.getCallingUid();
    if (!isOrphaned(packageName)
            && !isCallerAllowedToSilentlyUninstall(uid, packageName)) {
        try {
            final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
            intent.setData(Uri.fromParts(PACKAGE_SCHEME, packageName, null));
            intent.putExtra(PackageInstaller.EXTRA_CALLBACK, observer.asBinder());
            observer.onUserActionRequired(intent);
        } catch (RemoteException re) {
        }
        return;
    }
   ...省略
       
private boolean isCallerAllowedToSilentlyUninstall(int callingUid, String pkgName) {
    if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID
          || callingUid == Process.SYSTEM_UID) {
        return true;
    }
    final int callingUserId = UserHandle.getUserId(callingUid);
    // If the caller installed the pkgName, then allow it to silently uninstall.
    if (callingUid == getPackageUid(getInstallerPackageName(pkgName), 0, callingUserId)) {
        return true;
    }

    // Allow package verifier to silently uninstall.
    if (mRequiredVerifierPackage != null &&
            callingUid == getPackageUid(mRequiredVerifierPackage, 0, callingUserId)) {
        return true;
    }

    // Allow package uninstaller to silently uninstall.
    if (mRequiredUninstallerPackage != null &&
            callingUid == getPackageUid(mRequiredUninstallerPackage, 0, callingUserId)) {
        return true;
    }

    // Allow storage manager to silently uninstall.
    if (mStorageManagerPackage != null &&
            callingUid == getPackageUid(mStorageManagerPackage, 0, callingUserId)) {
        return true;
    }
    return false;
}
           

7.在isCallerAllowedToSilentlyUninstall方法裡面,增加特殊處理

private boolean isCallerAllowedToSilentlyUninstall(int callingUid, String pkgName) {
+        if (mPkgList.size() > 0) {
+            try {
+                if (mPkgList.contains(AppGlobals.getPackageManager().getNameForUid(callingUid))) {
+                    return true;
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+                Slog.e(TAG, "getNameForUid error is " + e.getMessage());
+            }
+        }
           

加上以上修改之後實測,解除安裝正常。

還是得多讀源碼呀。。。RTFSC!!!

PS:

測試代碼如下:

//AndroidManiFest.xml裡面聲明權限
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />

mPackageDeleteObserver = new PackageDeleteObserver();
mPackageInstallObserver = new PackageInstallObserver();

//解除安裝
try {
    Class<?>[] uninstalltypes = new Class[]{String.class, IPackageDeleteObserver.class, int.class};
    PackageManager pm = mContext.getPackageManager();
    Method uninstall = pm.getClass().getMethod("deletePackage", uninstalltypes);
    uninstall.invoke(pm, new Object[]{"cache.wind.nfc", mPackageDeleteObserver, 0});
    Log.d(TAG, "invoke uninstall app>>>>>>>");
} catch (Exception e) {
    Log.e(TAG, "error " + e.getMessage());
    e.printStackTrace();
}

//安裝
try {
    Class<?>[] installtypes = new Class[]{Uri.class, IPackageInstallObserver.class, int.class, String.class};
    PackageManager pm = mContext.getPackageManager();
    Method install = pm.getClass().getMethod("installPackage", installtypes);
    int installFlags = 0;
    installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
    install.invoke(pm, new Object[]{Uri.fromFile(new File("/sdcard/NfcReader.apk")), mPackageInstallObserver, installFlags, null});
    Log.d(TAG, "invoke install app>>>>>>>");
} catch (Exception e) {
    Log.e(TAG, "error " + e.getMessage());
    e.printStackTrace();
}

class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
    @Override
    public void packageDeleted(String s, int i) throws RemoteException {
        Log.d(TAG, "packageDeleted s is " + s + " return code is " + i);
    }
}

class PackageInstallObserver extends IPackageInstallObserver.Stub {
    @Override
    public void packageInstalled(String s, int i) throws RemoteException {
        Log.d(TAG, "packageInstalled s is " + s + " return code is " + i);
    }
}
           

繼續閱讀