美文网首页
android 9.0通过广播实现静默安装

android 9.0通过广播实现静默安装

作者: l900 | 来源:发表于2019-12-06 17:22 被阅读0次

一、实现思路
第三方APP通过发送特定广播,把apk文件路径发送给系统,由android系统来安装apk
二、实现步奏

1、在PackageInstaller添加广播接收器SilenceRecevier.java
 @Override
 public void onReceive(Context context, Intent intent) {
     Log.d("1900", "SilenceRecevier");
     String filePath = intent.getStringExtra("path");
     if (intent.getAction().equals("xxxxxx.ACTION_INSTALL_APK")) {                 
             MySingleInstance instance =    MySingleInstance.getInstance(context.getApplicationContext());
             instance.addElem(filePath);             
             instance.startService();
         }                  
 }       
}
2、多调用安装控制器

此次应用场景是多个应用会同时安装,而且安装完成或者失败都需要给客户反馈安装路径,所以上面的广播接收器会短时间内多次接受到广播,而且每一次安装都应该是独立完整的,于是引用了单例模式控制来控制广播接受后的应用安装。单例实现类如下

public class  MySingleInstance {

    private static MySingleInstance instance = null;

    private MySingleInstance() {
    }

    private static Context mContext;
    public static MySingleInstance getInstance( Context context) {
        mContext = context;
        if (instance == null) {
            Log.d("1900","getInstance ");
            instance = new MySingleInstance();
        }
        return instance;
    }
 
    private static LinkedList<String> list = new LinkedList<>();

    public void addElem(String path){
        list.add(path);
        Log.d("1900", "addElem size:" + list.size());
    }

    private static  boolean mComplete = true;
    public  void setIsComplete(boolean isComplete){
        mComplete = isComplete;
    }

    public void startService(){
        if(mComplete) {
            Intent intent = new Intent(mContext, InstallService.class); 
            if(list.size()!=0) {                            
                Log.d("1900", " size:" + list.size());  
                String path = list.poll();
                intent.putExtra("filePath",path);
                intent.setData(Uri.fromFile(new File(path)));
                Log.d("1900", "poll size:" + list.size());
                Log.d("1900", "------path-------  ---------"+path);             
                mContext.startForegroundService(intent);                                      
            }else{
                mContext.stopService(intent);
            }
        }
    }   
            
    public void doNext(){
        startService();
    }   
}

3、静默安装服务

其中客户发送过来的apk路径通过Settings.db来存储
Settings.System.putString(InstallService.this.getApplicationContext().getContentResolver()
,Settings.System.SILENCE_INSTALL_PATH ,path);

settings.db存储数据百度上可以找到很多的资料参考
package com.android.packageinstaller;

import java.io.File;
import static android.content.pm.PackageInstaller.SessionParams.UID_UNKNOWN;
import android.app.IntentService;
import android.app.Service;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.BroadcastReceiver;
import android.app.PendingIntent;
import android.content.Context;
import android.content.ActivityNotFoundException;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Bundle;
import android.os.Message;
import android.widget.Toast;
import android.util.DisplayMetrics;
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.ResolveInfo;
import android.content.pm.VerificationParams;
import android.content.pm.PackageInstaller;
import android.util.Log;
import com.android.packageinstaller.permission.utils.IoUtils;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import android.content.pm.ApplicationInfo;
import android.os.IBinder;
import android.content.pm.PackageParser.PackageLite;
import android.provider.Settings;
 
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.Build;
 
import java.io.FileOutputStream;
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;

import java.util.Timer;
import java.util.TimerTask;
public class InstallService extends Service {

    private PackageInfo mPkgInfo;

    private Uri mPackageURI;
    private Intent mLaunchIntent;
    private ApplicationInfo mAppInfo;
    private static final String BROADCAST_SENDER_PERMISSION ="android.permission.INSTALL_PACKAGES";
    PackageLite pkg;
    boolean open;

    private HandlerThread mInstallThread;
    private Handler mInstallHandler;
    public InstallService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
 
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("install service", "onCreate");
        
        mInstallThread = new HandlerThread("InstallThread");
        mInstallThread.start();
        mInstallHandler = new Handler(mInstallThread.getLooper());
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("com.install");
        registerReceiver(mBroadcastReceiver, intentFilter,BROADCAST_SENDER_PERMISSION, null /*scheduler*/);
    }

    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final int statusCode = intent.getIntExtra(
                    PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
            onPackageInstalled(statusCode);
        }
    };
    private NotificationManager notificationManager;
    private String notificationId = "channelId";
    private String notificationName = "channelName";
    private Notification getNotification() {
        Notification.Builder builder = new Notification.Builder(this)
                .setSmallIcon(R.drawable.ic_success)
                .setContentTitle("install ")
                .setContentText("install service running");
         
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            builder.setChannelId(notificationId);
        }
        Notification notification = builder.build();
        return notification;
    }
    

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
         Log.d("1900","installService onStartCommand");
        MySingleInstance.getInstance(InstallService.this.getApplicationContext()).setIsComplete(false);     
        notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            NotificationChannel channel = new NotificationChannel(notificationId, notificationName, NotificationManager.IMPORTANCE_HIGH);
            notificationManager.createNotificationChannel(channel);
        }
        startForeground(1,getNotification());
        
        
        if(intent==null){
            return super.onStartCommand(intent, flags, startId);
        }
        if(intent.getData()==null){
            return super.onStartCommand(intent, flags, startId);
        }
        mInstallHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                dowork(intent);
            }
        },3000);
        
        return super.onStartCommand(intent, flags, startId);
    }
    
    private void dowork(Intent intent){
        PackageManager pm = getPackageManager();
        mPackageURI = intent.getData();
        open = intent.getBooleanExtra("OPEN", false);
                           
        Settings.System.putString(InstallService.this.getApplicationContext().getContentResolver()
                ,Settings.System.SILENCE_INSTALL_PATH   ,mPackageURI.getPath());
        final File sourceFile = new File(mPackageURI.getPath());
        Log.d("1900","mPackageURI = "+mPackageURI+"    sourceFile= "+sourceFile);
        final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        params.originatingUid = UID_UNKNOWN ;
        try {
            params.setInstallLocation(PackageParser.parsePackageLite(sourceFile, 0).installLocation);
            pkg = PackageParser.parsePackageLite(sourceFile, 0);
        } catch (PackageParser.PackageParserException e) {          
            Log.d("1900", "Cannot parse package " + sourceFile + ". Assuming defaults.");
        }
        mInstallHandler.post(new Runnable() {
            @Override
            public void run() {
                doPackageStage(pm, params);
            }
        });
    }


    private void doPackageStage(PackageManager pm, PackageInstaller.SessionParams params) {
        Log.d("1900", "doPackageStage");
        final PackageInstaller packageInstaller = pm.getPackageInstaller();
        PackageInstaller.Session session = null;
        try {
            final String packageLocation = mPackageURI.getPath();
            final File file = new File(packageLocation);
            final int sessionId = packageInstaller.createSession(params);
            final byte[] buffer = new byte[65536];
            Log.d("1900", "doPackageStage path:" +packageLocation);
            session = packageInstaller.openSession(sessionId);

            final InputStream in = new FileInputStream(file);
            final long sizeBytes = file.length();
            final OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes);
            try {
                int c;
                while ((c = in.read(buffer)) != -1) {
                    out.write(buffer, 0, c);
                    if (sizeBytes > 0) {
                        final float fraction = ((float) c / (float) sizeBytes);
                        session.addProgress(fraction);
                    }
                }
                session.fsync(out);
            } finally {
                IoUtils.closeQuietly(in);
                IoUtils.closeQuietly(out);
            }

            // Create a PendingIntent and use it to generate the IntentSender
            Intent broadcastIntent = new Intent("com.install");
            PendingIntent pendingIntent = PendingIntent.getBroadcast(
                    InstallService.this /*context*/,
                    sessionId,
                    broadcastIntent,
                    PendingIntent.FLAG_UPDATE_CURRENT);
            session.commit(pendingIntent.getIntentSender());
        } catch (IOException e) {
            Log.d("1900", "doPackageStage IOException");
            onPackageInstalled(PackageInstaller.STATUS_FAILURE);
        } finally {
            IoUtils.closeQuietly(session);
        }
    }
    private final int INSTALL_COMPLETE = 1;

    private Handler mHandler = new Handler() {

        public void handleMessage(Message msg) {
         
    
                switch (msg.what) {

                case INSTALL_COMPLETE:
                    if (msg.arg1 == PackageInstaller.STATUS_SUCCESS) {
                         Log.d("1900","Install STATUS_SUCCESS ");
                    } 
                    if(msg.arg1 == PackageInstaller.STATUS_FAILURE){
                         Log.d("1900","Install STATUS_FAILURE ");
                    }                   
                    break;
                default:
                    break;
            }

        }

    };

    void onPackageInstalled(int statusCode) {       
        MySingleInstance.getInstance(InstallService.this.getApplicationContext()).setIsComplete(true);
        MySingleInstance.getInstance(InstallService.this.getApplicationContext()).doNext();             
        Message msg = mHandler.obtainMessage(INSTALL_COMPLETE);
        msg.arg1 = statusCode;
        mHandler.sendMessage(msg);
    }

    @Override
    public void onDestroy() {        
        unregisterReceiver(mBroadcastReceiver);
        mInstallThread.getLooper().quitSafely();
        stopForeground(true);
        super.onDestroy();
    }

}

4、安装广播安装服务注册
<receiver
        android:name=".SilenceRecevier"
        android:exported="true">
        <intent-filter>
        <action android:name="xxxxx.ACTION_INSTALL_APK" />
        </intent-filter>
    </receiver>
    <service
        android:name=".InstallService"
        android:enabled="true"
        android:exported="true"></service>

///#####5、广播调用安装
把apk的地址通过bunlde传递给机器内置安装服务的广播接收器

2019年12月6日17:46:46

相关文章

网友评论

      本文标题:android 9.0通过广播实现静默安装

      本文链接:https://www.haomeiwen.com/subject/cdmegctx.html