admin 管理员组文章数量: 1184232
在android 9.0自动下载更新时 遇到 安装包解析错误
12-1819:21:32.66548045604 W InstallStaging: java.lang.SecurityException: Permission Denial: reading android.support.v4.content.FileProvider uri content://com.***.***.update_app.file_provider/download/update/***.apk from pid=4804, uid=1000 requires the provider be exported, or grantUriPermission()
12-18 19:21:32.665 4804 5604 W InstallStaging: at android.os.Parcel.createException(Parcel.java:1950)
12-18 19:21:32.665 4804 5604 W InstallStaging: at android.os.Parcel.readException(Parcel.java:1918)
12-18 19:21:32.665 4804 5604 W InstallStaging: at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
12-18 19:21:32.665 4804 5604 W InstallStaging: at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:146)
12-18 19:21:32.665 4804 5604 W InstallStaging: at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:698)
12-18 19:21:32.665 4804 5604 W InstallStaging: at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1459)
12-18 19:21:32.665 4804 5604 W InstallStaging: at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1296)
12-18 19:21:32.665 4804 5604 W InstallStaging: at android.content.ContentResolver.openInputStream(ContentResolver.java:1016)
12-18 19:21:32.665 4804 5604 W InstallStaging: at com.android.packageinstaller.InstallStaging$StagingAsyncTask.doInBackground(InstallStaging.java:167)
12-18 19:21:32.665 4804 5604 W InstallStaging: at com.android.packageinstaller.InstallStaging$StagingAsyncTask.doInBackground(InstallStaging.java:160)
12-18 19:21:32.665 4804 5604 W InstallStaging: at android.os.AsyncTask$2.call(AsyncTask.java:333)
12-18 19:21:32.665 4804 5604 W InstallStaging: at java.util.concurrent.FutureTask.run(FutureTask.java:266)
12-18 19:21:32.665 4804 5604 W InstallStaging: at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
12-18 19:21:32.665 4804 5604 W InstallStaging: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
12-18 19:21:32.665 4804 5604 W InstallStaging: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
12-18 19:21:32.665 4804 5604 W InstallStaging: at java.lang.Thread.run(Thread.java:764)
发现Android9.0是需要权限读取fileProvider和uri的
于是我看了一下三方框架的源码,确实没有加权限
private Intent installIntent(Context context, String path){
Intent intent =newIntent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addCategory(Intent.CATEGORY_DEFAULT);
String fileProviderAuthority =getFileProviderAuthority(context);if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.N&&null!= fileProviderAuthority){
Uri fileUri = FileProvider.getUriForFile(context, fileProviderAuthority,newFile(path));
intent.setDataAndType(fileUri,"application/vnd.android.package-archive");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);}else{
intent.setDataAndType(Uri.fromFile(newFile(path)),"application/vnd.android.package-archive");}return intent;}于是我就扒了一下源码对其进行修改
private Intent installIntent(Context context, String path){
Intent intent =newIntent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addCategory(Intent.CATEGORY_DEFAULT);
String fileProviderAuthority =getFileProviderAuthority(context);if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.N&&null!= fileProviderAuthority){
Uri fileUri = FileProvider.getUriForFile(context, fileProviderAuthority,newFile(path));
intent.setDataAndType(fileUri,"application/vnd.android.package-archive");grantUriPermission(getPackageName(), fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);}else{
intent.setDataAndType(Uri.fromFile(newFile(path)),"application/vnd.android.package-archive");}return intent;}测试通过,完美安装,没有出现解析失败问题
代码:
<!--AndroidManifest.xml--><uses-permission android:name="android.permission.FOREGROUND_SERVICE"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.INTERNET"/><provider
android:name="android.support.v4.content.FileProvider"
android:authorities="包名.file_provider"
android:exported="false"
android:grantUriPermissions="true"><!-- 元数据 --><meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"
tools:replace="android:resource"/></provider><service android:name=".UpdateService"/>//从源码下载的升级下载安装apk的服务<!-- file_paths.xml --><?xml version="1.0" encoding="utf-8"?><paths><external-path path="." name="download"/><external-cache-path
name="storage/emulated/0"
path="."/></paths>/**
* Created by Sun on 2020/12/19.
*/publicclassUpdateServiceextendsService{publicstaticfinal String TAG ="UpdateService";publicstaticfinal String ACTION ="me.shenfan.UPDATE_APP";publicstaticfinal String STATUS ="status";publicstaticfinal String PROGRESS ="progress";publicstaticboolean DEBUG =false;//下载大小通知频率publicstaticfinalint UPDATE_NUMBER_SIZE =1;publicstaticfinalint DEFAULT_RES_ID =-1;publicstaticfinalint UPDATE_PROGRESS_STATUS =0;publicstaticfinalint UPDATE_ERROR_STATUS =-1;publicstaticfinalint UPDATE_SUCCESS_STATUS =1;//paramsprivatestaticfinal String URL ="downloadUrl";privatestaticfinal String ICO_RES_ID ="icoResId";privatestaticfinal String ICO_SMALL_RES_ID ="icoSmallResId";privatestaticfinal String UPDATE_PROGRESS ="updateProgress";privatestaticfinal String STORE_DIR ="storeDir";privatestaticfinal String DOWNLOAD_NOTIFICATION_FLAG ="downloadNotificationFlag";privatestaticfinal String DOWNLOAD_SUCCESS_NOTIFICATION_FLAG ="downloadSuccessNotificationFlag";privatestaticfinal String DOWNLOAD_ERROR_NOTIFICATION_FLAG ="downloadErrorNotificationFlag";privatestaticfinal String IS_SEND_BROADCAST ="isSendBroadcast";private String downloadUrl;privateint icoResId;//default app icoprivateint icoSmallResId;privateint updateProgress;//update notification progress when it add numberprivate String storeDir;//default sdcard/Android/package/updateprivateint downloadNotificationFlag;privateint downloadSuccessNotificationFlag;privateint downloadErrorNotificationFlag;privateboolean isSendBroadcast;private UpdateProgressListener updateProgressListener;private LocalBinder localBinder =newLocalBinder();/**
* Class used for the client Binder.
*/publicclassLocalBinderextendsBinder{/**
* set update progress call back
*
* @param listener
*/publicvoidsetUpdateProgressListener(UpdateProgressListener listener){
UpdateService.this.setUpdateProgressListener(listener);}}privateboolean startDownload;//开始下载privateint lastProgressNumber;private NotificationCompat.Builder builder;private NotificationManager manager;privateint notifyId;private String appName;private LocalBroadcastManager localBroadcastManager;private Intent localIntent;private DownloadApk downloadApkTask;/**
* whether debug
*/publicstaticvoiddebug(){
DEBUG =true;}private Intent installIntent(Context context, String path){
Intent intent =newIntent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addCategory(Intent.CATEGORY_DEFAULT);
String fileProviderAuthority =getFileProviderAuthority(context);if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && null!= fileProviderAuthority){
Uri fileUri = FileProvider.getUriForFile(context, fileProviderAuthority,newFile(path));
intent.setDataAndType(fileUri,"application/vnd.android.package-archive");grantUriPermission(getPackageName(), fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);}else{
intent.setDataAndType(Uri.fromFile(newFile(path)),"application/vnd.android.package-archive");}return intent;}/**
* 获取FileProvider的auth
*/privatestatic String getFileProviderAuthority(Context context){try{for(ProviderInfo provider : context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_PROVIDERS).providers){if(FileProvider.class.getName().equals(provider.name)&& provider.authority.endsWith(".update_app.file_provider")){return provider.authority;}}}catch(PackageManager.NameNotFoundException ignore){}return null;}privatestatic Intent webLauncher(String downloadUrl){
Uri download = Uri.parse(downloadUrl);
Intent intent =newIntent(Intent.ACTION_VIEW, download);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);return intent;}privatestatic String getSaveFileName(String downloadUrl){if(downloadUrl == null || TextUtils.isEmpty(downloadUrl)){return System.currentTimeMillis()+".apk";}return downloadUrl.substring(downloadUrl.lastIndexOf("/"));}privatestatic File getDownloadDir(UpdateService service){
File downloadDir = null;if(Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)){if(service.storeDir != null){
downloadDir =newFile(Environment.getExternalStorageDirectory(), service.storeDir);}else{
downloadDir =newFile(service.getExternalCacheDir(),"update");}}else{
downloadDir =newFile(service.getCacheDir(),"update");}if(!downloadDir.exists()){
downloadDir.mkdirs();}return downloadDir;}@OverridepublicvoidonCreate(){super.onCreate();
appName =getApplicationName();}@OverridepublicintonStartCommand(Intent intent,int flags,int startId){if(!startDownload && intent != null){
startDownload =true;
downloadUrl = intent.getStringExtra(URL);
icoResId = intent.getIntExtra(ICO_RES_ID, DEFAULT_RES_ID);
icoSmallResId = intent.getIntExtra(ICO_SMALL_RES_ID, DEFAULT_RES_ID);
storeDir = intent.getStringExtra(STORE_DIR);
updateProgress = intent.getIntExtra(UPDATE_PROGRESS, UPDATE_NUMBER_SIZE);
downloadNotificationFlag = intent.getIntExtra(DOWNLOAD_NOTIFICATION_FLAG,0);
downloadErrorNotificationFlag = intent.getIntExtra(DOWNLOAD_ERROR_NOTIFICATION_FLAG,0);
downloadSuccessNotificationFlag = intent.getIntExtra(DOWNLOAD_SUCCESS_NOTIFICATION_FLAG,0);
isSendBroadcast = intent.getBooleanExtra(IS_SEND_BROADCAST,false);if(DEBUG){
Log.d(TAG,"downloadUrl: "+ downloadUrl);
Log.d(TAG,"icoResId: "+ icoResId);
Log.d(TAG,"icoSmallResId: "+ icoSmallResId);
Log.d(TAG,"storeDir: "+ storeDir);
Log.d(TAG,"updateProgress: "+ updateProgress);
Log.d(TAG,"downloadNotificationFlag: "+ downloadNotificationFlag);
Log.d(TAG,"downloadErrorNotificationFlag: "+ downloadErrorNotificationFlag);
Log.d(TAG,"downloadSuccessNotificationFlag: "+ downloadSuccessNotificationFlag);
Log.d(TAG,"isSendBroadcast: "+ isSendBroadcast);}
notifyId = startId;buildNotification();buildBroadcast();
downloadApkTask =newDownloadApk(this);
downloadApkTask.execute(downloadUrl);}returnsuper.onStartCommand(intent, flags, startId);}@Nullable@Overridepublic IBinder onBind(Intent intent){return localBinder;}@OverridepublicbooleanonUnbind(Intent intent){returntrue;}publicvoidsetUpdateProgressListener(UpdateProgressListener updateProgressListener){this.updateProgressListener = updateProgressListener;}@OverridepublicvoidonDestroy(){if(downloadApkTask != null){
downloadApkTask.cancel(true);}if(updateProgressListener != null){
updateProgressListener = null;}
localIntent = null;
builder = null;super.onDestroy();}public String getApplicationName(){
PackageManager packageManager = null;
ApplicationInfo applicationInfo = null;try{
packageManager =getApplicationContext().getPackageManager();
applicationInfo = packageManager.getApplicationInfo(getPackageName(),0);}catch(PackageManager.NameNotFoundException e){
applicationInfo = null;}
String applicationName =(String) packageManager.getApplicationLabel(applicationInfo);return applicationName;}privatevoidbuildBroadcast(){if(!isSendBroadcast){return;}
localBroadcastManager = LocalBroadcastManager.getInstance(this);
localIntent =newIntent(ACTION);}privatevoidsendLocalBroadcast(int status,int progress){if(!isSendBroadcast || localIntent == null){return;}
localIntent.putExtra(STATUS, status);
localIntent.putExtra(PROGRESS, progress);
localBroadcastManager.sendBroadcast(localIntent);}privatevoidbuildNotification(){
manager =(NotificationManager)getSystemService(NOTIFICATION_SERVICE);
builder =newNotificationCompat.Builder(this);
builder.setContentTitle(getString(R.string.update_app_model_prepare, appName)).setWhen(System.currentTimeMillis()).setProgress(100,1,false).setSmallIcon(icoSmallResId).setLargeIcon(BitmapFactory.decodeResource(getResources(), icoResId)).setDefaults(downloadNotificationFlag);
manager.notify(notifyId, builder.build());}privatevoidstart(){
builder.setContentTitle(appName);
builder.setContentText(getString(R.string.update_app_model_progress,1,"%"));//setChannelId 必须添加否则不能在通知栏显示(Android 8.0)
builder.setChannelId(getPackageName());
manager.notify(notifyId, builder.build());sendLocalBroadcast(UPDATE_PROGRESS_STATUS,1);if(updateProgressListener != null){
updateProgressListener.start();}}/**
* @param progress download percent , max 100
*/privatevoidupdate(int progress){if(progress - lastProgressNumber > updateProgress){
lastProgressNumber = progress;
builder.setProgress(100, progress,false);
builder.setContentText(getString(R.string.update_app_model_progress, progress,"%"));
manager.notify(notifyId, builder.build());sendLocalBroadcast(UPDATE_PROGRESS_STATUS, progress);if(updateProgressListener != null){
updateProgressListener.update(progress);}}}privatevoidsuccess(String path){
builder.setProgress(0,0,false);
builder.setContentText(getString(R.string.update_app_model_success));
Intent i =installIntent(this, path);
PendingIntent intent = PendingIntent.getActivity(this,0, i, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(intent);
builder.setDefaults(downloadSuccessNotificationFlag);
Notification n = builder.build();
n.contentIntent = intent;
manager.notify(notifyId, n);sendLocalBroadcast(UPDATE_SUCCESS_STATUS,100);if(updateProgressListener != null){
updateProgressListener.success();}startActivity(i);stopSelf();}privatevoiderror(){
Intent i =webLauncher(downloadUrl);
PendingIntent intent = PendingIntent.getActivity(this,0, i,
PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentText(getString(R.string.update_app_model_error));
builder.setContentIntent(intent);
builder.setProgress(0,0,false);
builder.setDefaults(downloadErrorNotificationFlag);
Notification n = builder.build();
n.contentIntent = intent;
manager.notify(notifyId, n);sendLocalBroadcast(UPDATE_ERROR_STATUS,-1);if(updateProgressListener != null){
updateProgressListener.error();}stopSelf();}privatestaticclassDownloadApkextendsAsyncTask<String, Integer, String>{private WeakReference<UpdateService> updateServiceWeakReference;publicDownloadApk(UpdateService service){
updateServiceWeakReference =newWeakReference<>(service);}@OverrideprotectedvoidonPreExecute(){super.onPreExecute();
UpdateService service = updateServiceWeakReference.get();if(service != null){
service.start();}}@Overrideprotected String doInBackground(String... params){final String downloadUrl = params[0];final File file =newFile(UpdateService.getDownloadDir(updateServiceWeakReference.get()),
UpdateService.getSaveFileName(downloadUrl));if(DEBUG){
Log.d(TAG,"download url is "+ downloadUrl);
Log.d(TAG,"download apk cache at "+ file.getAbsolutePath());}
File dir = file.getParentFile();if(!dir.exists()){
dir.mkdirs();}
HttpURLConnection httpConnection = null;
InputStream is = null;
FileOutputStream fos = null;int updateTotalSize =0;
URL url;try{
url =newURL(downloadUrl);
httpConnection =(HttpURLConnection) url.openConnection();
httpConnection.setConnectTimeout(20000);
httpConnection.setReadTimeout(20000);if(DEBUG){
Log.d(TAG,"download status code: "+ httpConnection.getResponseCode());}if(httpConnection.getResponseCode()!=200){return null;}
updateTotalSize = httpConnection.getContentLength();if(file.exists()){if(updateTotalSize == file.length()){// 下载完成return file.getAbsolutePath();}else{
file.delete();}}
file.createNewFile();
is = httpConnection.getInputStream();
fos =newFileOutputStream(file,false);byte buffer[]=newbyte[4096];int readSize =0;int currentSize =0;while((readSize = is.read(buffer))>0){
fos.write(buffer,0, readSize);
currentSize += readSize;publishProgress((currentSize *100/ updateTotalSize));}// download success}catch(Exception e){
e.printStackTrace();return null;}finally{if(httpConnection != null){
httpConnection.disconnect();}if(is != null){try{
is.close();}catch(IOException e){
e.printStackTrace();}}if(fos != null){try{
fos.close();}catch(IOException e){
e.printStackTrace();}}}return file.getAbsolutePath();}@OverrideprotectedvoidonProgressUpdate(Integer... values){super.onProgressUpdate(values);if(DEBUG){
Log.d(TAG,"current progress is "+ values[0]);}
UpdateService service = updateServiceWeakReference.get();if(service != null){
service.update(values[0]);}}@OverrideprotectedvoidonPostExecute(String s){super.onPostExecute(s);
UpdateService service = updateServiceWeakReference.get();if(service != null){if(s != null){
service.success(s);}else{
service.error();}}}}/**
* a builder class helper use UpdateService
*/publicstaticclassBuilder{private String downloadUrl;privateint icoResId = DEFAULT_RES_ID;//default app icoprivateint icoSmallResId = DEFAULT_RES_ID;privateint updateProgress = UPDATE_NUMBER_SIZE;//update notification progress when it add numberprivate String storeDir;//default sdcard/Android/package/updateprivateint downloadNotificationFlag;privateint downloadSuccessNotificationFlag;privateint downloadErrorNotificationFlag;privateboolean isSendBroadcast;protectedBuilder(String downloadUrl){this.downloadUrl = downloadUrl;}publicstatic Builder create(String downloadUrl){if(downloadUrl == null){thrownewNullPointerException("downloadUrl == null");}returnnewBuilder(downloadUrl);}public String getDownloadUrl(){return downloadUrl;}publicintgetIcoResId(){return icoResId;}public Builder setIcoResId(int icoResId){this.icoResId = icoResId;returnthis;}publicintgetIcoSmallResId(){return icoSmallResId;}public Builder setIcoSmallResId(int icoSmallResId){this.icoSmallResId = icoSmallResId;returnthis;}publicintgetUpdateProgress(){return updateProgress;}public Builder setUpdateProgress(int updateProgress){if(updateProgress <1){thrownewIllegalArgumentException("updateProgress < 1");}this.updateProgress = updateProgress;returnthis;}public String getStoreDir(){return storeDir;}public Builder setStoreDir(String storeDir){this.storeDir = storeDir;returnthis;}publicintgetDownloadNotificationFlag(){return downloadNotificationFlag;}public Builder setDownloadNotificationFlag(int downloadNotificationFlag){this.downloadNotificationFlag = downloadNotificationFlag;returnthis;}publicintgetDownloadSuccessNotificationFlag(){return downloadSuccessNotificationFlag;}public Builder setDownloadSuccessNotificationFlag(int downloadSuccessNotificationFlag){this.downloadSuccessNotificationFlag = downloadSuccessNotificationFlag;returnthis;}publicintgetDownloadErrorNotificationFlag(){return downloadErrorNotificationFlag;}public Builder setDownloadErrorNotificationFlag(int downloadErrorNotificationFlag){this.downloadErrorNotificationFlag = downloadErrorNotificationFlag;returnthis;}publicbooleanisSendBroadcast(){return isSendBroadcast;}public Builder setIsSendBroadcast(boolean isSendBroadcast){this.isSendBroadcast = isSendBroadcast;returnthis;}public Builder build(Context context){if(context == null){thrownewNullPointerException("context == null");}
Intent intent =newIntent();
intent.setClass(context, UpdateService.class);
intent.putExtra(URL, downloadUrl);if(icoResId == DEFAULT_RES_ID){
icoResId =getIcon(context);}if(icoSmallResId == DEFAULT_RES_ID){
icoSmallResId = icoResId;}
intent.putExtra(ICO_RES_ID, icoResId);
intent.putExtra(STORE_DIR, storeDir);
intent.putExtra(ICO_SMALL_RES_ID, icoSmallResId);
intent.putExtra(UPDATE_PROGRESS, updateProgress);
intent.putExtra(DOWNLOAD_NOTIFICATION_FLAG, downloadNotificationFlag);
intent.putExtra(DOWNLOAD_SUCCESS_NOTIFICATION_FLAG, downloadSuccessNotificationFlag);
intent.putExtra(DOWNLOAD_ERROR_NOTIFICATION_FLAG, downloadErrorNotificationFlag);
intent.putExtra(IS_SEND_BROADCAST, isSendBroadcast);
context.startService(intent);returnthis;}privateintgetIcon(Context context){final PackageManager packageManager = context.getPackageManager();
ApplicationInfo appInfo = null;try{
appInfo = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);}catch(PackageManager.NameNotFoundException e){
e.printStackTrace();}if(appInfo != null){return appInfo.icon;}return0;}}}/**
* 下载回调
*/publicinterfaceUpdateProgressListener{/**
* download start
*/voidstart();/**
* update download progress
* @param progress
*/voidupdate(int progress);/**
* download success
*/voidsuccess();/**
* download error
*/voiderror();} UpdateService.Builder.create("服务器给的下载路径").setStoreDir("下载路径")//default sdcard/Android/package/update.setIsSendBroadcast(true).setIcoResId(android.R.drawable.ic_notification_clear_all).setIcoSmallResId(android.R.drawable.ic_notification_overlay).build(context);版权声明:本文标题:遇到在Android 9.0上自动更新时,由Java语言引发的权限问题 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.roclinux.cn/p/1773531854a3563010.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论