Android N SettingsProvider的数据保存

本文深入探讨了Android系统中SettingsProvider组件如何存储其数据。从AndroidM版本开始,数据不再存储于传统的SQLite数据库,而是转存为XML文件。文章详细分析了数据插入流程,并确认了最终数据存储位置为/data/system/users/0下的settings_global.xml、settings_secure.xml和settings_system.xml文件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android M之前SettingProvider数据储存的位置在手机目录/data/data/com.Android.providers.settings/databases下

不过目前的Android版本虽然也有这个目录,其中却没有对应的文件在,本文是看下当前的数据到底存储在哪里。

代码在在frameworks/base/packages/SettingsProvider

插入流程分析

从插入代码为线索分析:

frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.Java

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public Uri insert(Uri uri, ContentValues values) {  
  2.     ...  
  3.     switch (table) {  
  4.         ...  
  5.         case TABLE_SYSTEM: {  
  6.             if (insertSystemSetting(name, value, UserHandle.getCallingUserId())) {  
  7.                 return Uri.withAppendedPath(Settings.System.CONTENT_URI, name);  
  8.             }  
  9.         } break;  
  10.     ...  
  11. }  
只看system表的插入:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. private boolean insertSystemSetting(String name, String value, int requestingUserId) {  
  2.     ...  
  3.     return mutateSystemSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT);  
  4. }  
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. private boolean mutateSystemSetting(String name, String value, int runAsUserId,  
  2.          int operation) {  
  3.       ...  
  4.              case MUTATION_OPERATION_INSERT: {  
  5.                  validateSystemSettingValue(name, value);  
  6.                  return mSettingsRegistry  
  7.                          .insertSettingLocked(SettingsRegistry.SETTINGS_TYPE_SYSTEM,  
  8.                                  owningUserId, name, value, getCallingPackage());  
  9.              }  
  10.       ...  
  11.  }  
SettingsRegistry是内部类

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public boolean insertSettingLocked(int type, int userId, String name, String value,  
  2.     ...  
  3.     final boolean success = settingsState.insertSettingLocked(name, value, packageName);  
  4.     ...  
  5. }  

SettingsState是另一个文件

frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public boolean insertSettingLocked(String name, String value, String packageName) {  
  2.     ...  
  3.     updateMemoryUsagePerPackageLocked(packageName, oldValue, value);           
  4.     scheduleWriteIfNeededLocked();  
  5.     ...  
  6. }  

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. private void scheduleWriteIfNeededLocked() {  
  2.     if (!mDirty) {  
  3.         mDirty = true;  
  4.         writeStateAsyncLocked();  
  5.     }  
  6. }  
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. private void writeStateAsyncLocked() {  
  2.     final long currentTimeMillis = SystemClock.uptimeMillis();  
  3.   
  4.     if (mWriteScheduled) {  
  5.         mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS);  
  6.   
  7.         // If enough time passed, write without holding off anymore.  
  8.         final long timeSinceLastNotWrittenMutationMillis = currentTimeMillis  
  9.                 - mLastNotWrittenMutationTimeMillis;  
  10.         if (timeSinceLastNotWrittenMutationMillis >= MAX_WRITE_SETTINGS_DELAY_MILLIS) {  
  11.             mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS).sendToTarget();  
  12.             return;  
  13.         }  
  14.   
  15.         // Hold off a bit more as settings are frequently changing.  
  16.         final long maxDelayMillis = Math.max(mLastNotWrittenMutationTimeMillis  
  17.                 + MAX_WRITE_SETTINGS_DELAY_MILLIS - currentTimeMillis, 0);  
  18.         final long writeDelayMillis = Math.min(WRITE_SETTINGS_DELAY_MILLIS, maxDelayMillis);  
  19.   
  20.         Message message = mHandler.obtainMessage(MyHandler.MSG_PERSIST_SETTINGS);  
  21.         mHandler.sendMessageDelayed(message, writeDelayMillis);  
  22.     }  
  23.     ...  
  24. }  
两条分支,不过最终都是发送了个消息,消息处理中会调用doWriteState

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. private void doWriteState() {  
  2.      ...  
  3.      AtomicFile destination = new AtomicFile(mStatePersistFile);  
  4.      ...  
  5.      FileOutputStream out = null;  
  6.      try {  
  7.          out = destination.startWrite();  
  8.   
  9.          XmlSerializer serializer = Xml.newSerializer();  
  10.          serializer.setOutput(out, StandardCharsets.UTF_8.name());  
  11.          serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output"true);  
  12.          serializer.startDocument(nulltrue);  
  13.          serializer.startTag(null, TAG_SETTINGS);  
  14.          serializer.attribute(null, ATTR_VERSION, String.valueOf(version));  
  15.   
  16.          final int settingCount = settings.size();  
  17.          for (int i = 0; i < settingCount; i++) {  
  18.              Setting setting = settings.valueAt(i);  
  19.   
  20.              writeSingleSetting(mVersion, serializer, setting.getId(), setting.getName(),  
  21.                      setting.getValue(), setting.getPackageName());  
  22.   
  23.              if (DEBUG_PERSISTENCE) {  
  24.                  Slog.i(LOG_TAG, "[PERSISTED]" + setting.getName() + "=" + setting.getValue());  
  25.              }  
  26.          }  
  27.   
  28.          serializer.endTag(null, TAG_SETTINGS);  
  29.          serializer.endDocument();  
  30.          destination.finishWrite(out);  
  31.          ...  
  32.  }  
一看到xml就终于知道,是写到了一个xml文件中,并不是写到sqlite中。

文件地址确认

SettingsState中的成员

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. private final File mStatePersistFile;  
这个成员是xml的写入文件

在SettingsProvider中

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. private void ensureSettingsStateLocked(int key) {  
  2.     if (mSettingsStates.get(key) == null) {  
  3.         final int maxBytesPerPackage = getMaxBytesPerPackageForType(getTypeFromKey(key));  
  4.         SettingsState settingsState = new SettingsState(mLock, getSettingsFile(key), key,  
  5.                 maxBytesPerPackage);  
  6.         mSettingsStates.put(key, settingsState);  
  7.     }  
  8. }  
这个方法负责SettingsState的初始化

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. private File getSettingsFile(int key) {  
  2.     if (isGlobalSettingsKey(key)) {  
  3.         final int userId = getUserIdFromKey(key);  
  4.         return new File(Environment.getUserSystemDirectory(userId),  
  5.                 SETTINGS_FILE_GLOBAL);  
  6.     } else if (isSystemSettingsKey(key)) {  
  7.         final int userId = getUserIdFromKey(key);  
  8.         return new File(Environment.getUserSystemDirectory(userId),  
  9.                 SETTINGS_FILE_SYSTEM);  
  10.     } else if (isSecureSettingsKey(key)) {  
  11.         final int userId = getUserIdFromKey(key);  
  12.         return new File(Environment.getUserSystemDirectory(userId),  
  13.                 SETTINGS_FILE_SECURE);  
  14.     } else {  
  15.         throw new IllegalArgumentException("Invalid settings key:" + key);  
  16.     }  
  17. }  
从getSettingsFile中可以看到SettingsProvider三个表global,system和secure的文件,它们的路径是相同的,通过Environment.getUserSystemDirectory确立

frameworks/base/core/java/android/os/Environment.java

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public static File getUserSystemDirectory(int userId) {  
  2.     return new File(new File(getSystemSecureDirectory(), "users"), Integer.toString(userId));  
  3. }  
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public static File getSystemSecureDirectory() {  
  2.     if (isEncryptedFilesystemEnabled()) { //加密文件系统分支,默认是关闭的  
  3.         return new File(SECURE_DATA_DIRECTORY, "system");  
  4.     } else {  
  5.         return new File(DATA_DIRECTORY, "system");  
  6.     }  
  7. }  
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. private static final File DATA_DIRECTORY  
  2.         = getDirectory("ANDROID_DATA""/data");  
依据ANDROID_DATA环境变量获取路径,获取不到的话就默认为/data。

获取到DATA_DIRECTORY后,后续还要添加"/ " + "system" + "/" + "users" + "/" + userId的值

例如我测试的手机上是/data/system/users/0,该目录的settings_global.xml,settings_secure.xml和settings_system.xml三个xml文件就是SettingsProvider中的数据文件。

<think>我们正在讨论的是在Android 13中实现共享APK文件并静默安装。静默安装通常需要系统权限(如系统应用)或设备已root。普通应用无法直接静默安装APK,因为Android系统出于安全考虑禁止此操作。但是,我们可以通过以下两种方式实现: ### 方案一:使用系统权限(仅适用于系统应用或定制ROM) 系统应用可以通过`PackageInstaller`的隐藏API进行静默安装。此方法需要应用具有`INSTALL_PACKAGES`权限,且该权限仅授予系统应用或通过特定签名认证的应用。 **步骤:** 1. 在AndroidManifest.xml中声明权限: ```xml <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> ``` 2. 使用`PackageInstaller`安装APK: ```java public void silentInstall(Context context, File apkFile) { PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller(); PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); int sessionId = packageInstaller.createSession(params); PackageInstaller.Session session = packageInstaller.openSession(sessionId); try (InputStream in = new FileInputStream(apkFile); OutputStream out = session.openWrite("package", 0, -1)) { byte[] buffer = new byte[65536]; int len; while ((len = in.read(buffer)) != -1) { out.write(buffer, 0, len); } session.fsync(out); } catch (IOException e) { e.printStackTrace(); } // 创建安装状态监听 Intent intent = new Intent(context, InstallResultReceiver.class); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_MUTABLE); session.commit(pendingIntent.getIntentSender()); session.close(); } ``` 3. 接收安装结果广播(需注册广播接收器): ```java public class InstallResultReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); if (status == PackageInstaller.STATUS_SUCCESS) { // 安装成功 } else { String message = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE); // 处理失败 } } } ``` > **注意**:此方法仅适用于系统应用,普通应用调用`createSession`会抛出`SecurityException`[^1]。 ### 方案二:Root设备后使用su命令 如果设备已root,可以通过执行`pm install`命令实现静默安装。 **步骤:** 1. 申请root权限并执行命令: ```java public void rootSilentInstall(File apkFile) { String command = "pm install -r " + apkFile.getAbsolutePath(); Process process = Runtime.getRuntime().exec("su"); DataOutputStream outputStream = new DataOutputStream(process.getOutputStream()); outputStream.writeBytes(command + "\n"); outputStream.writeBytes("exit\n"); outputStream.flush(); process.waitFor(); } ``` > **风险**:此方法依赖设备root,且需要用户授权root权限,安全风险较高。 ### 方案三:使用无障碍服务(非真正静默,但可模拟点击) 对于非系统应用且未root的设备,无法实现真正的静默安装。但可以通过无障碍服务模拟用户点击安装按钮(需用户提前开启无障碍服务)。 **步骤:** 1. 创建无障碍服务并监听安装界面: ```java public class InstallAccessibilityService extends AccessibilityService { @Override public void onAccessibilityEvent(AccessibilityEvent event) { if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { String packageName = event.getPackageName() != null ? event.getPackageName().toString() : ""; if (packageName.equals("com.android.packageinstaller")) { // 查找安装按钮 AccessibilityNodeInfo installButton = findButtonByText("安装", "确定", "继续"); if (installButton != null) { installButton.performAction(AccessibilityNodeInfo.ACTION_CLICK); } } } } private AccessibilityNodeInfo findButtonByText(String... texts) { AccessibilityNodeInfo rootNode = getRootInActiveWindow(); if (rootNode == null) return null; for (String text : texts) { List<AccessibilityNodeInfo> nodes = rootNode.findAccessibilityNodeInfosByText(text); for (AccessibilityNodeInfo node : nodes) { if (node.isClickable()) return node; } } return null; } } ``` 2. 在AndroidManifest.xml中声明服务: ```xml <service android:name=".InstallAccessibilityService" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_service_config" /> </service> ``` 3. 配置accessibility_service_config.xml: ```xml <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/accessibility_service_desc" android:packageNames="com.android.packageinstaller" android:accessibilityEventTypes="typeWindowStateChanged" android:accessibilityFeedbackType="feedbackSpoken" android:canRetrieveWindowContent="true" android:notificationTimeout="100" /> ``` ### 共享APK文件 无论采用哪种安装方案,共享APK文件都需通过FileProvider: ```java public void shareApk(Context context, File apkFile) { Uri contentUri = FileProvider.getUriForFile( context, context.getPackageName() + ".fileprovider", apkFile ); Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.setType("application/vnd.android.package-archive"); shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri); shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); context.startActivity(Intent.createChooser(shareIntent, "分享APK")); } ``` ### 总结 | 方案 | 条件 | 用户感知 | 可靠性 | |---------------------|---------------|----------|--------| | 系统API静默安装 | 系统应用 | 无 | ★★★★ | | Root命令安装 | 已root设备 | 需授权 | ★★★☆ | | 无障碍服务模拟点击 | 开启无障碍 | 可见界面 | ★★☆☆ | > **重要提示**: > - 静默安装可能违反Google Play政策,仅适用于企业级MDM解决方案或定制系统 > - Android 11+对无障碍服务的使用有更严格限制[^3] §§相关问题§§ 1. 如何判断应用是否具有系统权限? 2. Android无障碍服务的使用有哪些限制? 3. 如何通过设备策略管理器(Device Policy Manager)实现静默安装? 4. Android 13中如何正确配置FileProvider共享APK文件? [^1]: 系统级安装需签名和权限匹配 [^2]: 普通应用无法直接调用系统安装API [^3]: 无障碍服务需用户手动开启且界面可见</think>在 Android 13 中实现共享并静默安装 APK 文件需要特殊权限和系统级操作,普通应用无法直接实现。以下是完整的解决方案: ### 一、核心限制与前提条件 1. **静默安装的系统限制**: - Android 禁止普通应用直接静默安装 APK(安全策略) - 需要满足以下任一条件: - 系统应用(预装或系统签名) - 设备已 root - 通过设备管理权限(MDM 解决方案)[^1] 2. **Android 13 新增限制**: - 加强了分区储规则(Scoped Storage) - 禁止普通应用访问 `/data` 目录 - 安装服务需声明 `REQUEST_INSTALL_PACKAGES` 权限 ### 二、系统级解决方案(需系统权限) ```java // 步骤 1:声明特殊权限 (AndroidManifest.xml) <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <!-- Android 11+ 需要 --> // 步骤 2:使用 PackageInstaller API public void silentInstallApk(Context context, File apkFile) { PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL ); PackageInstaller installer = context.getPackageManager().getPackageInstaller(); int sessionId = installer.createSession(params); PackageInstaller.Session session = installer.openSession(sessionId); try (OutputStream out = session.openWrite("package", 0, -1); InputStream in = new FileInputStream(apkFile)) { byte[] buffer = new byte[65536]; int length; while ((length = in.read(buffer)) != -1) { out.write(buffer, 0, length); } session.fsync(out); } catch (IOException e) { throw new RuntimeException("文件复制失败", e); } // 提交安装(静默无界面) Intent intent = new Intent(context, InstallResultReceiver.class); PendingIntent pendingIntent = PendingIntent.getBroadcast( context, sessionId, intent, PendingIntent.FLAG_MUTABLE ); session.commit(pendingIntent.getIntentSender()); } // 步骤 3:接收安装结果广播 public class InstallResultReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1); if (status == PackageInstaller.STATUS_SUCCESS) { // 安装成功 } else { String errorMsg = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE); // 处理错误 } } } ``` ### 三、普通应用的变通方案 #### 方案 1:通过无障碍服务模拟点击 ```java // 在无障碍服务中自动点击安装按钮 public class AutoInstallService extends AccessibilityService { @Override public void onAccessibilityEvent(AccessibilityEvent event) { if (!"com.android.packageinstaller".equals(event.getPackageName())) return; AccessibilityNodeInfo root = getRootInActiveWindow(); if (root == null) return; // 查找安装按钮 List<AccessibilityNodeInfo> buttons = root.findAccessibilityNodeInfosByText("安装"); for (AccessibilityNodeInfo button : buttons) { if (button.isClickable()) { button.performAction(AccessibilityNodeInfo.ACTION_CLICK); break; } } } } ``` **配置** (`res/xml/accessibility_service.xml`): ```xml <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/accessibility_desc" android:packageNames="com.android.packageinstaller" android:accessibilityEventTypes="typeWindowStateChanged" android:accessibilityFlags="flagDefault" android:canRetrieveWindowContent="true"/> ``` #### 方案 2:通过设备策略管理器 (MDM) ```java // 在设备管理员应用中 public void managedInstallApk(ComponentName admin, Uri apkUri) { DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE); dpm.installPackage(admin, apkUri); } ``` **要求**: 1. 应用激活设备管理员权限 2. 企业级 MDM 解决方案 ### 四、APK 共享实现(Android 13 适配) ```java // 使用 FileProvider 共享 APK public void shareApkFile(Context context, File apkFile) { Uri contentUri = FileProvider.getUriForFile( context, context.getPackageName() + ".provider", apkFile ); Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.setType("application/vnd.android.package-archive"); shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri); shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // Android 13 需额外添加此标志 shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(Intent.createChooser(shareIntent, "分享APK")); } ``` **FileProvider 配置** (`res/xml/file_paths.xml`): ```xml <paths> <external-path name="downloads" path="Download/"/> <files-path name="internal_apks" path="apks/"/> </paths> ``` ### 五、完整流程(系统级实现) 1. 将 APK 保存到私有目录: ```java File internalDir = new File(context.getFilesDir(), "apks"); File apkFile = new File(internalDir, "update.apk"); // 写入 APK 数据... ``` 2. 通过 FileProvider 生成 URI: ```java Uri apkUri = FileProvider.getUriForFile(context, AUTHORITY, apkFile); ``` 3. 执行静默安装: ```java silentInstallApk(context, new File(apkUri.getPath())); ``` ### 注意事项 1. **签名要求**:系统级安装需要应用使用与系统相同的签名证书[^1] 2. **Android 13 适配**: - 必须处理 `MANAGE_EXTERNAL_STORAGE` 权限 - 使用 `Intent.FLAG_ACTIVITY_NEW_TASK` 启动安装 - 禁止直接访问 `/data/local/tmp` 等系统目录 3. **兼容性处理**: ```java if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // Android 11+ 需要特殊处理 if (!Environment.isExternalStorageManager()) { Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION); startActivity(intent); } } ``` > **重要警告**: > 普通应用在 Google Play 上架时,使用静默安装技术会违反政策,此方案仅适用于企业级 MDM 或定制 ROM 开发[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值