由于android 11以上版本对于读写权限的进一步限制,安装的apk获取权限的流程又加了一步,对于客户来说多半步也算复杂,Android 保存/读取本地SD卡文件(兼容Android 13)
一、打开全部权限
根据之前的经验,在安装流程后打开全部权限即可。
Index: frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java
===================================================================
--- frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java (版本 2518)
+++ frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java (工作副本)
@@ -221,6 +221,8 @@
import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl;
// @}
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
+
final class InstallPackageHelper {
@@ -281,6 +283,7 @@
private final ViewCompiler mViewCompiler;
private final SharedLibrariesImpl mSharedLibraries;
private final PackageManagerServiceInjector mInjector;
+ private final DefaultPermissionGrantPolicy mDefaultPermissionPolicy;
// TODO(b/198166813): remove PMS dependency
InstallPackageHelper(PackageManagerService pm, AppDataHelper appDataHelper) {
@@ -310,6 +313,7 @@
mAlwaysAllowInstallApks = mContext.getResources().getStringArray(
com.android.internal.R.array.config_always_allowed_install_apks);
+ mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(mContext);
}
InstallPackageHelper(PackageManagerService pm) {
@@ -388,6 +392,19 @@
// Attempt the transparent shared UID migration
mPm.mSettings.convertSharedUserSettingsLPw(sharedUserSetting);
}
+ } else {
+ final Object obj = mPm.mSettings.getSettingLPr(pkgSetting.getAppId());
+ if (obj instanceof SharedUserSetting) {
+ try {
+ synchronized (mPm.mLock) {
+ mPm.mSettings.registerAppIdLPw(pkgSetting, true);
+ Slog.d(TAG, "The AppId of " + pkgSetting.getName() + " changes from "
+ + ((SharedUserSetting)obj).getAppId() + " to " + pkgSetting.getAppId());
+ }
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "register new app id for " + pkgSetting + " error:", e);
+ }
+ }
}
if (reconciledPkg.mInstallArgs != null
&& reconciledPkg.mInstallArgs.mForceQueryableOverride) {
@@ -2386,9 +2403,25 @@
// The caller explicitly specified INSTALL_ALL_USERS flag.
// Thus, updating the settings to install the app for all users.
for (int currentUserId : allUsers) {
- ps.setInstalled(true, currentUserId);
- ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId,
+ // If the app is already installed for the currentUser,
+ // keep it as installed as we might be updating the app at this place.
+ // If not currently installed, check if the currentUser is restricted by
+ // DISALLOW_INSTALL_APPS or DISALLOW_DEBUGGING_FEATURES device policy.
+ // Install / update the app if the user isn't restricted. Skip otherwise.
+ final boolean installedForCurrentUser = ArrayUtils.contains(
+ installedForUsers, currentUserId);
+ final boolean restrictedByPolicy =
+ mPm.isUserRestricted(currentUserId,
+ UserManager.DISALLOW_INSTALL_APPS)
+ || mPm.isUserRestricted(currentUserId,
+ UserManager.DISALLOW_DEBUGGING_FEATURES);
+ if (installedForCurrentUser || !restrictedByPolicy) {
+ ps.setInstalled(true, currentUserId);
+ ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, currentUserId,
installerPackageName);
+ } else {
+ ps.setInstalled(false, currentUserId);
+ }
}
}
@@ -2505,6 +2538,18 @@
}
}
+ private void enableManageExternalStorage(String pkgName, int appId) {
+ final AppOpsManager appOpsManager = mPm.mContext.getSystemService(AppOpsManager.class);
+ final int[] allUsersList = mPm.mUserManager.getUserIds();
+ for (int userId : allUsersList) {
+ final int uid = UserHandle.getUid(userId, appId);
+ appOpsManager.setMode(AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE,
+ uid,
+ pkgName,
+ AppOpsManager.MODE_ALLOWED);
+ }
+ }
+
/**
* On successful install, executes remaining steps after commit completes and the package lock
* is released. These are typically more expensive or require calls to installd, which often
@@ -2938,6 +2983,11 @@
final boolean removedBeforeUpdate = (pkgSetting == null)
|| (pkgSetting.isSystem() && !pkgSetting.getPath().getPath().equals(
res.mPkg.getPath()));
+ mDefaultPermissionPolicy.grantAppRuntimePermissions(packageName);
+ int resAppId = UserHandle.getAppId(res.mUid);
+ enableManageExternalStorage(packageName, resAppId);
if (succeeded && removedBeforeUpdate) {
Slog.e(TAG, packageName + " was removed before handlePackagePostInstall "
+ "could be executed");
Index: frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
===================================================================
--- frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java (版本 2518)
+++ frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java (工作副本)
@@ -287,6 +287,8 @@
import com.android.server.pm.pkg.component.ParsedUsesPermission;
// @}
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
+
/**
* Keep track of all those APKs everywhere.
* <p>
@@ -864,7 +866,7 @@
// Internal interface for permission manager
final PermissionManagerServiceInternal mPermissionManager;
-
+ final DefaultPermissionGrantPolicy mDefaultPermissionPolicy;
@Watched
final ComponentResolver mComponentResolver;
@@ -1649,6 +1651,7 @@
mLock = injector.getLock();
mPackageStateWriteLock = mLock;
mPermissionManager = injector.getPermissionManagerServiceInternal();
+ mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(mContext);
mSettings = injector.getSettings();
mUserManager = injector.getUserManagerService();
mUserNeedsBadging = new UserNeedsBadgingCache(mUserManager);
@@ -1780,6 +1783,7 @@
mUserNeedsBadging = new UserNeedsBadgingCache(mUserManager);
mComponentResolver = injector.getComponentResolver();
mPermissionManager = injector.getPermissionManagerServiceInternal();
+ mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(mContext);
mSettings = injector.getSettings();
mIncrementalManager = mInjector.getIncrementalManager();
mDefaultAppProvider = mInjector.getDefaultAppProvider();
@@ -4485,6 +4489,10 @@
final Message msg = mHandler.obtainMessage(PackageManagerService.POST_INSTALL, token,
didLaunch ? 1 : 0);
mHandler.sendMessage(msg);
+ PostInstallData data = mRunningInstalls.get(msg.arg1);
+ InstallArgs args = data.args;
+ String packageName = args.mInstallSource.installerPackageName;
+ mDefaultPermissionPolicy.grantAppRuntimePermissions(packageName);
}
void checkPackageStartable(@NonNull Computer snapshot, @NonNull String packageName,
Index: frameworks/base/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
===================================================================
--- frameworks/base/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java (版本 2518)
+++ frameworks/base/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java (工作副本)
@@ -87,6 +87,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import android.os.Binder;
// CTA Feature: add permission @{
import android.cta.CtaPermFactory;
@@ -104,7 +105,7 @@
* to have an interface defined in the package manager but have the impl next to other
* policy stuff like PhoneWindowManager
*/
-final class DefaultPermissionGrantPolicy {
+final public class DefaultPermissionGrantPolicy {
private static final String TAG = "DefaultPermGrantPolicy"; // must be <= 23 chars
private static final boolean DEBUG = false;
@@ -336,7 +337,7 @@
}
};
- DefaultPermissionGrantPolicy(@NonNull Context context) {
+ public DefaultPermissionGrantPolicy(@NonNull Context context) {
mContext = context;
HandlerThread handlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
@@ -437,6 +438,43 @@
grantRuntimePermissionsForSystemPackage(pm, userId, pkg, null);
}
+ private void grantRuntimePermissionsForPackage(int userId, PackageInfo pkg) {
+ Set<String> permissions = new ArraySet<>();
+ DelayingPackageManagerCache pm = new DelayingPackageManagerCache();
+ String [] pkgPermissions = pkg.requestedPermissions;
+ if(pkgPermissions== null || pkgPermissions.length==0)return;
+ for (String permission : pkgPermissions) {
+ final PermissionInfo perm = pm.getPermissionInfo(permission);
+ if (perm == null) {
+ continue;
+ }
+ if (perm.isRuntime()) {
+ Log.i(TAG, "Granting run permission="+permission);
+ permissions.add(permission);
+ }
+ }
+ if (!permissions.isEmpty()) {
+ grantRuntimePermissions(pm,pkg, permissions, true, userId);
+ }
+ pm.apply();
+ }
+
+ public void grantAppRuntimePermissions(String pkgName) {
+ Log.i(TAG, "grantAppRuntimePermissions--pkgName:"+pkgName);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ final PackageInfo pkg = NO_PM_CACHE.getPackageInfo(pkgName);
+ Log.i(TAG, "pkg=" + pkg);
+ if (pkg != null) {
+ grantRuntimePermissionsForPackage(UserHandle.USER_SYSTEM, pkg);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
private void grantRuntimePermissionsForSystemPackage(PackageManagerWrapper pm,
int userId, PackageInfo pkg, Set<String> filterPermissions) {
if (ArrayUtils.isEmpty(pkg.requestedPermissions)) {
@@ -497,13 +535,13 @@
pm.addPackageInfo(pkg.packageName, pkg);
- if (!pm.isSysComponentOrPersistentPlatformSignedPrivApp(pkg)
- || !doesPackageSupportRuntimePermissions(pkg)
- || ArrayUtils.isEmpty(pkg.requestedPermissions)) {
+ if (/*!pm.isSysComponentOrPersistentPlatformSignedPrivApp(pkg)
+ || !doesPackageSupportRuntimePermissions(pkg)
+ || */ArrayUtils.isEmpty(pkg.requestedPermissions)) {
continue;
}
验证结果显示:
mDefaultPermissionPolicy.grantAppRuntimePermissions(packageName);为获取除android.permission.MANAGE_EXTERNAL_STORAGE外的所有权限
为了解决android.permission.MANAGE_EXTERNAL_STORAGE,需要额外添加enableManageExternalStorage(packageName, resAppId);
二、针对性打开
仅打开MANAGE_EXTERNAL_STORAGE权限
Index: frameworks/base/core/java/android/app/AppOpsManager.java
===================================================================
--- frameworks/base/core/java/android/app/AppOpsManager.java (版本 2390)
+++ frameworks/base/core/java/android/app/AppOpsManager.java (工作副本)
@@ -2856,7 +2856,7 @@
AppOpsManager.MODE_ERRORED, // READ_DEVICE_IDENTIFIERS
AppOpsManager.MODE_ALLOWED, // ALLOW_MEDIA_LOCATION
AppOpsManager.MODE_DEFAULT, // QUERY_ALL_PACKAGES
- AppOpsManager.MODE_DEFAULT, // MANAGE_EXTERNAL_STORAGE
+ AppOpsManager.MODE_ALLOWED, // MANAGE_EXTERNAL_STORAGE
AppOpsManager.MODE_DEFAULT, // INTERACT_ACROSS_PROFILES
AppOpsManager.MODE_IGNORED, // ACTIVATE_PLATFORM_VPN
AppOpsManager.MODE_DEFAULT, // LOADER_USAGE_STATS
三、验证DEMO
MainActivity.java
package com.basewin.permissiontest;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.Settings;
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static final String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
private boolean havePermission = false;
private AlertDialog dialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onResume() {
super.onResume();
checkPermission();
}
private void checkPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (!Environment.isExternalStorageManager()) {
if (dialog != null) {
dialog.dismiss();
dialog = null;
}
dialog = new AlertDialog.Builder(this)
.setTitle("HINT")
.setMessage("Please open permission!")
.setNegativeButton("cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.setPositiveButton("confirm", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.setData(Uri.parse("package:"+MainActivity.this.getPackageName()));
startActivity(intent);
}
}).create();
dialog.show();
} else {
havePermission = true;
}
} else {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
if (dialog != null) {
dialog.dismiss();
dialog = null;
}
dialog = new AlertDialog.Builder(this)
.setTitle("HINT")
.setMessage("Please open permission!")
.setPositiveButton("confirm", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
}
}).create();
dialog.show();
} else {
havePermission = true;
}
} else {
havePermission = true;
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST_EXTERNAL_STORAGE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
havePermission = true;
} else {
havePermission = false;
}
return;
}
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.basewin.permissiontest">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.PermissionTest"
android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>