[Description]
如何在手机第一次开机的时候,把用户放在外置SD卡上的APK自动安装到手机的存储卡。
[Solution]设计思路:
1. 将APK放到SD卡上某个目录,如apks_preInstall目录.
(对那些有在AndroidManifest.xml中声明了存储路径是internalOnly的apk,不建议使用这种安装方法)
2. 在第一次开机的时候,等sd卡mount上来,去读取这个目录安装APK
实现代码:
1. 修改\packages\apps\PackageInstaller\AndroidManifest.xml
<!--增加一个receiver -->
<receiver android:name=".FirstInstallReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_MOUNTED_FOR_FIRTIME" />
</intent-filter>
</receiver>
<!--end -->
2. \packages\apps\PackageInstaller\src\com\android\packageinstaller下增加FirstInstallReceiver.java文件,用于接收第一次SD卡mount上来的广播,以便安装APK,内容如下:
package com.android.packageinstaller;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.BroadcastReceiver;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.util.Log;
import android.content.pm.IPackageInstallObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import java.io.File;
import android.net.Uri;
public class FirstInstallReceiver extends BroadcastReceiver{
private final String TAG="FirstInstallReceiver";
private String action = "android.intent.action.MEDIA_MOUNTED_FOR_FIRTIME";
/*下面变量apksPath是外置sd卡上的某个路径,要修改默认预置的apk存放路径,请修改这个路径。
GB版本、GB2、ICS单卡项目:sd卡路径是/mnt/sdcard
GB2双卡项目: 默认外置sd卡路径是/mnt/sdcard/sdcard2,如打开SDSwap Feature,外置SD卡路径则是:/mnt/sdcard
ICS双卡项目:默认外置SD卡路径是/mnt/sdcard2,如打开SDSwap Feature,外置SD卡路径则是:/mnt/sdcard
因此修改要针对不同情况,首先需要确认外置SD卡的路径。
*/
private String apksPath = "/mnt/sdcard/apks_preInstall"; //这个例子假设外置SD卡路径为/mnt/sdcard////////////////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG,"onReceive:"+intent);
new Thread()
{
public void run() {
try
{
File apksFile = new File(apksPath);
PackageManager pm = context.getPackageManager();
int installFlags = 0;
installFlags |= PackageManager.INSTALL_EXTERNAL;
if(apksFile.exists()) {
File[] files = apksFile.listFiles();
for(File f1:files)
{
String filename = f1.getName();
String type = filename.substring(filename.lastIndexOf(".")+1);
if(type!=null&&type.equals("apk"))
{
Uri mPackageURI = Uri.fromFile(f1);
pm.installPackage(mPackageURI, null, installFlags, null);
}
}
}
}
catch(Exception e)
{
Log.d(TAG,"onReceive:install failed:e:"+e);
}
}
}.start();
}
}
3. 修改mountservice.java, 用于发送第一次开机sd卡mount上来的广播
private void notifyVolumeStateChange(String label, String path, int oldState, int newState) { .....
else if (newState == VolumeState.Mounted) {
//mtkadded
String isFirstBoot = SystemProperties.get("persist.sys.mount_is_first");
if(isFirstBoot == null || isFirstBoot.equals(""))
{
Slog.i(TAG, "first boot");
SystemProperties.set("persist.sys.mount_is_first", "DONE");
Intent firIntent = new Intent("android.intent.action.MEDIA_MOUNTED_FOR_FIRTIME");
mContext.sendStickyBroadcast(firIntent);
}
//mtkadded
if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
.....