项目背景 : 插上U盘后,自动识别扫描指定目录下的apk文件,并安装
1.监听U盘挂载
public final class USBReceiver extends BroadcastReceiver {
private static final String TAG = "USBReceiver";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
AutoInstallService.checkAutoInstallFolder(context, "需要检测的U盘路径Path");
Log.d(TAG, "U盘挂载");
}
}
}
2.安装
public class PackageManagerCompatP {
private final static String TAG = PackageManagerCompatP.class.getSimpleName();
private Context mContext;
public PackageManagerCompatP(Context context) {
this.mContext = context;
}
private PackageManager getPm() {
return mContext.getPackageManager();
}
private PackageInstaller getPi() {
return getPm().getPackageInstaller();
}
private void writeApkToInstallSession(PackageInstaller.Session session, String inPath,
String splitName) throws RemoteException {
long sizeBytes = 0;
final File file = new File(inPath);
if (file.isFile()) {
sizeBytes = file.length();
} else {
return;
}
try (InputStream in = Files.newInputStream(Paths.get(inPath));
OutputStream out = session.openWrite(splitName, 0, sizeBytes);) {
int total = 0;
byte[] buffer = new byte[65536];
int c;
while ((c = in.read(buffer)) != -1) {
total += c;
out.write(buffer, 0, c);
}
session.fsync(out);
} catch (IOException e) {
e.printStackTrace();
} /*finally {
session.close();
}*/
}
public int invokeInstallPackage(String inPath, String pkg) {
Log.d(TAG, "invokeInstallPackage inPath:" + inPath);
PackageInstaller.Session session = null;
try {
final PackageInstaller.SessionParams sessionParams =
new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
sessionParams.installFlags = 0 | PackageManager.INSTALL_REPLACE_EXISTING;
final int sessionId = getPi().createSession(sessionParams);
session = getPi().openSession(sessionId);
writeApkToInstallSession(session, inPath, pkg);
final LocalIntentReceiver localReceiver = new LocalIntentReceiver();
session.commit(localReceiver.getIntentSender());
final Intent result = localReceiver.getResult();
final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
PackageInstaller.STATUS_FAILURE);
Log.d(TAG, "get install state:" + status);
session.close();
session = null;
if (status != PackageInstaller.STATUS_SUCCESS) {
Log.e(TAG, "Installation should have succeeded, but got code "
+ status);
} else {
Log.e(TAG, "Installation have succeeded");
}
return status;
} catch (IllegalArgumentException | IOException | RemoteException e) {
Log.e(TAG, "Failed to install package; path=" + inPath, e);
} finally {
if (session != null) {
session.close();
}
}
return PackageInstaller.STATUS_FAILURE;
}
private static class LocalIntentReceiver {
private final SynchronousQueue<Intent> mResult = new SynchronousQueue<>();
private final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
@Override
public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
try {
mResult.offer(intent, 5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
};
public IntentSender getIntentSender() {
return new IntentSender((IIntentSender) mLocalSender);
}
public Intent getResult() {
try {
return mResult.take();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class AutoInstallService extends Service {
private static final String TAG = AutoInstallService.class.getSimpleName();
public final static String AUTO_INSTALL_FOLDER = "AutoInstall";
public final static String EXTRA_USB_PATH = "extra_usb_path";
public AutoInstallService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return null;
}
public static void checkAutoInstallFolder(Context context, final String path) {
Log.d(TAG, "installFolderExists path:" + path);
if (!TextUtils.isEmpty(path)) {
String installPath = path.endsWith(File.separator) ?
path + AUTO_INSTALL_FOLDER : path + File.separator + AUTO_INSTALL_FOLDER;
if (new File(installPath).exists()) {
Intent intent = new Intent(context, AutoInstallService.class);
intent.putExtra(EXTRA_USB_PATH, path);
context.startServiceAsUser(intent, UserHandle.CURRENT);
}
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String path = intent.getStringExtra(EXTRA_USB_PATH);
Log.d(TAG, "onStartCommand path:" + path + " intent:" + intent);
if (!TextUtils.isEmpty(path)) {
String installPath = path.endsWith(File.separator) ?
path + AUTO_INSTALL_FOLDER : path + File.separator + AUTO_INSTALL_FOLDER;
File usb = new File(installPath);
Log.d(TAG, "installPath:" + installPath + " exists:" + usb.exists());
if (usb.exists()) {
File[] list = usb.listFiles((file, name) -> name.endsWith(".apk") || name.endsWith(".APK"));
// new Handler().post(new InstallTask(getApplicationContext(), list));
new Thread(new InstallTask(getApplicationContext(), list, new MsgHandler(this))).start();
}
}
return Service.START_NOT_STICKY;
}
private static final class InstallTask implements Runnable {
private final File[] mApkList;
private final PackageManagerCompatP mPMCP;
private final Context mContext;
private final MsgHandler mHandler;
public InstallTask(Context context, File[] list, MsgHandler handler) {
mApkList = list;
mPMCP = new PackageManagerCompatP(context);
mContext = context;
mHandler = handler;
}
@Override
public void run() {
Log.d(TAG, "InstallTask list:" + mApkList + " size:" + (mApkList == null ? 0 : mApkList.length));
if (mApkList != null) {
mHandler.sendMessage(
mHandler.obtainMessage(MsgHandler.WHAT_DIALOG_SHOW,
"现在开始安装,总共有" + mApkList.length + "个APK待安装!"));
for (File file : mApkList) {
Log.d(Thread.currentThread().getName(), "start to install file:" + file.getAbsolutePath());
PackageInfo info = mContext.getPackageManager().getPackageArchiveInfo(file.getAbsolutePath(),
PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES);
/*mHandler.sendMessage(
mHandler.obtainMessage(MsgHandler.WHAT_UPDATE_DIALOG_MESSAGE,
String.format("应用(%s)正在安装中...", info.packageName)));*/
int status = mPMCP.invokeInstallPackage(file.getAbsolutePath(), info.packageName);
String msg = String.format("安装应用(%s)%s!",
info.packageName, status == PackageInstaller.STATUS_SUCCESS ? "成功" : "失败");
if (status != PackageInstaller.STATUS_SUCCESS) {
mHandler.sendMessage(mHandler.obtainMessage(MsgHandler.WHAT_UPDATE_DIALOG_MESSAGE, msg));
}
}
mHandler.sendMessage(
mHandler.obtainMessage(MsgHandler.WHAT_UPDATE_DIALOG_MESSAGE,
"所有应用安装已经完成,请到\"我的应用\"中查看!"));
mHandler.sendEmptyMessageDelayed(MsgHandler.WHAT_DIALOG_DISMISS, 10 * 1000L);
}
mContext.stopServiceAsUser(new Intent(mContext, AutoInstallService.class), UserHandle.CURRENT);
}
}
private static final class MsgHandler extends Handler {
private static final int WHAT_UPDATE_DIALOG_MESSAGE = 10086;
private static final int WHAT_DIALOG_SHOW = 10010;
private static final int WHAT_DIALOG_DISMISS = 10011;
private final WeakReference<AutoInstallService> mBase;
public MsgHandler(AutoInstallService service) {
mBase = new WeakReference<>(service);
}
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case WHAT_UPDATE_DIALOG_MESSAGE:
mBase.get().updateDialogMessage((String) msg.obj);
break;
case WHAT_DIALOG_SHOW:
mBase.get().showDialog((String) msg.obj);
break;
case WHAT_DIALOG_DISMISS:
if (mBase.get() != null) {
mBase.get().dismissDialog();
}
break;
}
}
}
private AlertDialog mDialog;
private void showDialog(String msg) {
/*if (mDialog == null) {
mDialog = new AlertDialog.Builder(this)
.setTitle("注意")
.setMessage(msg)
.setNegativeButton("确定", (dialogInterface, i) -> dialogInterface.dismiss())
.create();
mDialog.getWindow().setType(TYPE_SYSTEM_ALERT);
} else {
mDialog.setMessage(msg);
}
mDialog.show();*/
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
}
private void dismissDialog() {
if (mDialog != null) {
mDialog.dismiss();
}
}
private void updateDialogMessage(String msg) {
/*mDialog.setMessage(msg);
if (!mDialog.isShowing()) {
mDialog.show();
}*/
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
}
}