用java下载apk解析包出错_Android 9.0自动更新 安装包解析错误 java.lang.SecurityException: Permission Denial...

这是一个关于如何处理在Android 9.0系统中使用Java下载并解析APK包时遇到的安全异常问题的博客。文章详细介绍了在Android 9.0上安装APK时可能遇到的`java.lang.SecurityException: Permission Denial`错误,并提供了相应的解决策略。内容包括下载服务`UpdateService`的实现,包括通知构建、广播管理和文件下载逻辑。
摘要由CSDN通过智能技术生成

/**

* Created by Sun on 2020/12/19.

*/

public class UpdateService extends Service {

public static final String TAG = "UpdateService";

public static final String ACTION = "me.shenfan.UPDATE_APP";

public static final String STATUS = "status";

public static final String PROGRESS = "progress";

public static boolean DEBUG = false;

//下载大小通知频率

public static final int UPDATE_NUMBER_SIZE = 1;

public static final int DEFAULT_RES_ID = -1;

public static final int UPDATE_PROGRESS_STATUS = 0;

public static final int UPDATE_ERROR_STATUS = -1;

public static final int UPDATE_SUCCESS_STATUS = 1;

//params

private static final String URL = "downloadUrl";

private static final String ICO_RES_ID = "icoResId";

private static final String ICO_SMALL_RES_ID = "icoSmallResId";

private static final String UPDATE_PROGRESS = "updateProgress";

private static final String STORE_DIR = "storeDir";

private static final String DOWNLOAD_NOTIFICATION_FLAG = "downloadNotificationFlag";

private static final String DOWNLOAD_SUCCESS_NOTIFICATION_FLAG = "downloadSuccessNotificationFlag";

private static final String DOWNLOAD_ERROR_NOTIFICATION_FLAG = "downloadErrorNotificationFlag";

private static final String IS_SEND_BROADCAST = "isSendBroadcast";

private String downloadUrl;

private int icoResId; //default app ico

private int icoSmallResId;

private int updateProgress; //update notification progress when it add number

private String storeDir; //default sdcard/Android/package/update

private int downloadNotificationFlag;

private int downloadSuccessNotificationFlag;

private int downloadErrorNotificationFlag;

private boolean isSendBroadcast;

private UpdateProgressListener updateProgressListener;

private LocalBinder localBinder = new LocalBinder();

/**

* Class used for the client Binder.

*/

public class LocalBinder extends Binder {

/**

* set update progress call back

*

* @param listener

*/

public void setUpdateProgressListener(UpdateProgressListener listener) {

UpdateService.this.setUpdateProgressListener(listener);

}

}

private boolean startDownload;//开始下载

private int lastProgressNumber;

private NotificationCompat.Builder builder;

private NotificationManager manager;

private int notifyId;

private String appName;

private LocalBroadcastManager localBroadcastManager;

private Intent localIntent;

private DownloadApk downloadApkTask;

/**

* whether debug

*/

public static void debug() {

DEBUG = true;

}

private Intent installIntent(Context context, String path) {

Intent intent = new Intent(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, new File(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(new File(path)), "application/vnd.android.package-archive");

}

return intent;

}

/**

* 获取FileProvider的auth

*/

private static 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;

}

private static Intent webLauncher(String downloadUrl) {

Uri download = Uri.parse(downloadUrl);

Intent intent = new Intent(Intent.ACTION_VIEW, download);

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

return intent;

}

private static String getSaveFileName(String downloadUrl) {

if (downloadUrl == null || TextUtils.isEmpty(downloadUrl)) {

return System.currentTimeMillis() + ".apk";

}

return downloadUrl.substring(downloadUrl.lastIndexOf("/"));

}

private static File getDownloadDir(UpdateService service) {

File downloadDir = null;

if (Environment.getExternalStorageState().equals(

Environment.MEDIA_MOUNTED)) {

if (service.storeDir != null) {

downloadDir = new File(Environment.getExternalStorageDirectory(), service.storeDir);

} else {

downloadDir = new File(service.getExternalCacheDir(), "update");

}

} else {

downloadDir = new File(service.getCacheDir(), "update");

}

if (!downloadDir.exists()) {

downloadDir.mkdirs();

}

return downloadDir;

}

@Override

public void onCreate() {

super.onCreate();

appName = getApplicationName();

}

@Override

public int onStartCommand(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 = new DownloadApk(this);

downloadApkTask.execute(downloadUrl);

}

return super.onStartCommand(intent, flags, startId);

}

@Nullable

@Override

public IBinder onBind(Intent intent) {

return localBinder;

}

@Override

public boolean onUnbind(Intent intent) {

return true;

}

public void setUpdateProgressListener(UpdateProgressListener updateProgressListener) {

this.updateProgressListener = updateProgressListener;

}

@Override

public void onDestroy() {

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;

}

private void buildBroadcast() {

if (!isSendBroadcast) {

return;

}

localBroadcastManager = LocalBroadcastManager.getInstance(this);

localIntent = new Intent(ACTION);

}

private void sendLocalBroadcast(int status, int progress) {

if (!isSendBroadcast || localIntent == null) {

return;

}

localIntent.putExtra(STATUS, status);

localIntent.putExtra(PROGRESS, progress);

localBroadcastManager.sendBroadcast(localIntent);

}

private void buildNotification() {

manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

builder = new NotificationCompat.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());

}

private void start() {

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

*/

private void update(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);

}

}

}

private void success(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();

}

private void error() {

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();

}

private static class DownloadApk extends AsyncTask {

private WeakReference updateServiceWeakReference;

public DownloadApk(UpdateService service) {

updateServiceWeakReference = new WeakReference<>(service);

}

@Override

protected void onPreExecute() {

super.onPreExecute();

UpdateService service = updateServiceWeakReference.get();

if (service != null) {

service.start();

}

}

@Override

protected String doInBackground(String... params) {

final String downloadUrl = params[0];

final File file = new File(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 = new URL(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 = new FileOutputStream(file, false);

byte buffer[] = new byte[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();

}

@Override

protected void onProgressUpdate(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]);

}

}

@Override

protected void onPostExecute(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

*/

public static class Builder {

private String downloadUrl;

private int icoResId = DEFAULT_RES_ID; //default app ico

private int icoSmallResId = DEFAULT_RES_ID;

private int updateProgress = UPDATE_NUMBER_SIZE; //update notification progress when it add number

private String storeDir; //default sdcard/Android/package/update

private int downloadNotificationFlag;

private int downloadSuccessNotificationFlag;

private int downloadErrorNotificationFlag;

private boolean isSendBroadcast;

protected Builder(String downloadUrl) {

this.downloadUrl = downloadUrl;

}

public static Builder create(String downloadUrl) {

if (downloadUrl == null) {

throw new NullPointerException("downloadUrl == null");

}

return new Builder(downloadUrl);

}

public String getDownloadUrl() {

return downloadUrl;

}

public int getIcoResId() {

return icoResId;

}

public Builder setIcoResId(int icoResId) {

this.icoResId = icoResId;

return this;

}

public int getIcoSmallResId() {

return icoSmallResId;

}

public Builder setIcoSmallResId(int icoSmallResId) {

this.icoSmallResId = icoSmallResId;

return this;

}

public int getUpdateProgress() {

return updateProgress;

}

public Builder setUpdateProgress(int updateProgress) {

if (updateProgress < 1) {

throw new IllegalArgumentException("updateProgress < 1");

}

this.updateProgress = updateProgress;

return this;

}

public String getStoreDir() {

return storeDir;

}

public Builder setStoreDir(String storeDir) {

this.storeDir = storeDir;

return this;

}

public int getDownloadNotificationFlag() {

return downloadNotificationFlag;

}

public Builder setDownloadNotificationFlag(int downloadNotificationFlag) {

this.downloadNotificationFlag = downloadNotificationFlag;

return this;

}

public int getDownloadSuccessNotificationFlag() {

return downloadSuccessNotificationFlag;

}

public Builder setDownloadSuccessNotificationFlag(int downloadSuccessNotificationFlag) {

this.downloadSuccessNotificationFlag = downloadSuccessNotificationFlag;

return this;

}

public int getDownloadErrorNotificationFlag() {

return downloadErrorNotificationFlag;

}

public Builder setDownloadErrorNotificationFlag(int downloadErrorNotificationFlag) {

this.downloadErrorNotificationFlag = downloadErrorNotificationFlag;

return this;

}

public boolean isSendBroadcast() {

return isSendBroadcast;

}

public Builder setIsSendBroadcast(boolean isSendBroadcast) {

this.isSendBroadcast = isSendBroadcast;

return this;

}

public Builder build(Context context) {

if (context == null) {

throw new NullPointerException("context == null");

}

Intent intent = new Intent();

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);

return this;

}

private int getIcon(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;

}

return 0;

}

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值