ActivityManager: Background start not allowed: service Intent { act=geofence_trigered cmp=com.xiaomi.smarthome/.scene.activity.GeoActionService (has extras) } to com.xiaomi.smarthome/.scene.activity.GeoActionService from pid=9233 uid=10270 pkg=com.xiaomi.smarthome startFg?=true
I am_anr : [0,16206,com.android.mms,952745573,Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{3201640 u0 com.android.mms/com.xiaomi.mms.transaction.MxActivateService}]
/*** @param id Notification ID. Zero === exit foreground state for the given service.*/privatevoidsetServiceForegroundInnerLocked(finalServiceRecord r,int id,Notification notification,int flags,int foregroundServiceType){if(id !=0){if(notification ==null){thrownewIllegalArgumentException("null notification");}// Instant apps need permission to create foreground services.if(r.appInfo.isInstantApp()){.......}else{// P或P之后前臺(tái)service需要申請(qǐng)F(tuán)OREGROUND_SERVICE權(quán)限if(r.appInfo.targetSdkVersion >=Build.VERSION_CODES.P){mAm.enforcePermission(android.Manifest.permission.FOREGROUND_SERVICE,r.app.pid, r.appInfo.uid,"startForeground");}// 一般傳入的service type默認(rèn)為FOREGROUND_SERVICE_TYPE_MANIFEST,而manifestType默認(rèn)為0(none)int manifestType = r.serviceInfo.getForegroundServiceType();if(foregroundServiceType == FOREGROUND_SERVICE_TYPE_MANIFEST){foregroundServiceType = manifestType;}// 這種異常一般較少if((foregroundServiceType & manifestType)!= foregroundServiceType){thrownewIllegalArgumentException("foregroundServiceType "+String.format("0x%08X", foregroundServiceType)+" is not a subset of foregroundServiceType attribute "+String.format("0x%08X", manifestType)+" in service element of manifest file");}// 后臺(tái)啟動(dòng)的前臺(tái)Service不得擁有“使用中權(quán)限”,下面有講到if(!r.mAllowWhileInUsePermissionInFgs){Slog.w(TAG,"Foreground service started from background can not have "+"location/camera/microphone access: service "+ r.shortInstanceName);}}boolean alreadyStartedOp =false;boolean stopProcStatsOp =false;//移除前臺(tái)service 10s的超時(shí)消息。重置fgRequired 和 fgWaitingif(r.fgRequired){ r.fgRequired =false;r.fgWaiting =false;alreadyStartedOp = stopProcStatsOp =true;mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);}try{boolean ignoreForeground =false;......// service的app不在電池優(yōu)化白名單內(nèi)&不是前臺(tái)app啟動(dòng),ignoreForeground賦值為trueif(!ignoreForeground&&!appIsTopLocked(r.appInfo.uid)&&appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)){Slog.w(TAG,"Service.startForeground() not allowed due to bg restriction: service "+ r.shortInstanceName);updateServiceForegroundLocked(r.app,false);ignoreForeground =true;}if(!ignoreForeground){// 取消可能存在的notificationif(r.foregroundId != id){cancelForegroundNotificationLocked(r);r.foregroundId = id;}// 給service的notification相關(guān)變量賦值notification.flags |=Notification.FLAG_FOREGROUND_SERVICE;r.foregroundNoti = notification;r.foregroundServiceType = foregroundServiceType;// 前臺(tái)service第一次設(shè)置會(huì)為falseif(!r.isForeground){finalServiceMap smap =getServiceMapLocked(r.userId);if(smap !=null){// 統(tǒng)計(jì)每個(gè)app前臺(tái)service的數(shù)量ActiveForegroundApp active = smap.mActiveForegroundApps.get(r.packageName);if(active ==null){active =newActiveForegroundApp();active.mPackageName = r.packageName;active.mUid = r.appInfo.uid;active.mShownWhileScreenOn = mScreenOn;if(r.app !=null&& r.app.uidRecord !=null){active.mAppOnTop = active.mShownWhileTop =r.app.uidRecord.getCurProcState()<=ActivityManager.PROCESS_STATE_TOP;}active.mStartTime = active.mStartVisibleTime=SystemClock.elapsedRealtime();smap.mActiveForegroundApps.put(r.packageName, active);requestUpdateActiveForegroundAppsLocked(smap,0);}active.mNumActive++;}// 設(shè)置isForeground為true,第二次執(zhí)行該方法后不必執(zhí)行這些代碼,且表示當(dāng)前service在前臺(tái)r.isForeground =true;........}// 每次執(zhí)行該方法都會(huì)重新post通知,異步執(zhí)行r.postNotification();if(r.app !=null){// 更新進(jìn)程優(yōu)先級(jí)updateServiceForegroundLocked(r.app,true);}getServiceMapLocked(r.userId).ensureNotStartingBackgroundLocked(r);mAm.notifyPackageUse(r.serviceInfo.packageName,PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE);}else{}}finally{.......}}else{// notification id為0 ,是取消前臺(tái)service的流程,下面講}
發(fā)送通知
通知入隊(duì)等操作異步進(jìn)行,避免死鎖
通知沒(méi)有對(duì)應(yīng)的icon,打印警告信息,并給設(shè)置一個(gè)簡(jiǎn)陋的通知模板,告知用戶當(dāng)前正在運(yùn)行該進(jìn)程 V/ActivityManager: Attempted to start a foreground service (com.mfashiongallery.emag/.lks.minusonescreen.overlay.OverlayService) with a broken notification (no icon: Notification(channel=gallery_overlay_windows shortcut=null contentView=null vibrate=null sound=null defaults=0x0 flags=0x40 color=0x00000000 vis=PRIVATE))
// ServiceRecord.javapublicvoidpostNotification(){finalint appUid = appInfo.uid;finalint appPid = app.pid;if(foregroundId !=0&& foregroundNoti !=null){// Do asynchronous communication with notification manager to// avoid deadlocks.finalString localPackageName = packageName;finalint localForegroundId = foregroundId;finalNotification _foregroundNoti = foregroundNoti;finalServiceRecordrecord=this;ams.mHandler.post(newRunnable(){publicvoidrun(){NotificationManagerInternal nm =LocalServices.getService(NotificationManagerInternal.class);if(nm ==null){return;}Notification localForegroundNoti = _foregroundNoti;try{if(localForegroundNoti.getSmallIcon()==null){// 通知沒(méi)有對(duì)應(yīng)icon,打印警告信息Slog.v(TAG,"Attempted to start a foreground service ("+ shortInstanceName +") with a broken notification (no icon: "+ localForegroundNoti +")");CharSequence appName = appInfo.loadLabel(ams.mContext.getPackageManager());if(appName ==null){appName = appInfo.packageName;}Context ctx =null;try{ctx = ams.mContext.createPackageContextAsUser(appInfo.packageName,0,newUserHandle(userId));// 重新構(gòu)建通知相關(guān)信息Notification.Builder notiBuilder =newNotification.Builder(ctx,localForegroundNoti.getChannelId());// 設(shè)置icon為app應(yīng)用信息的iconnotiBuilder.setSmallIcon(appInfo.icon);// mark as foregroundnotiBuilder.setFlag(Notification.FLAG_FOREGROUND_SERVICE,true);// 構(gòu)建pendingIntentrunningIntent.setData(Uri.fromParts("package", appInfo.packageName,null));PendingIntent pi =PendingIntent.getActivityAsUser(ams.mContext,0,runningIntent,PendingIntent.FLAG_UPDATE_CURRENT,null,UserHandle.of(userId));// 設(shè)置系統(tǒng)的通知顏色notiBuilder.setColor(ams.mContext.getColor(com.android.internal.R.color.system_notification_accent_color));// 設(shè)置通知標(biāo)題為“xxx 正在運(yùn)行”notiBuilder.setContentTitle(ams.mContext.getString(com.android.internal.R.string.app_running_notification_title,appName));// 設(shè)置通知的內(nèi)容"點(diǎn)按即可了解詳情或停止應(yīng)用",miui定制國(guó)內(nèi)版本為"可能導(dǎo)致系統(tǒng)卡頓,降低待機(jī)時(shí)間,點(diǎn)按關(guān)閉"// MIUI MOD START:// notiBuilder.setContentText(// ams.mContext.getString(// com.android.internal.R.string// .app_running_notification_text,// appName));if(miui.os.Build.IS_INTERNATIONAL_BUILD){notiBuilder.setContentText(ams.mContext.getString(com.android.internal.R.string.app_running_notification_text,appName));}else{notiBuilder.setContentText(ams.mContext.getString(com.android.internal.R.string.app_running_notification_tip_text,appName));}// ENDnotiBuilder.setContentIntent(pi);localForegroundNoti = notiBuilder.build();}catch(PackageManager.NameNotFoundException e){}}if(nm.getNotificationChannel(localPackageName, appUid,localForegroundNoti.getChannelId())==null){int targetSdkVersion =Build.VERSION_CODES.O_MR1;try{finalApplicationInfo applicationInfo =ams.mContext.getPackageManager().getApplicationInfoAsUser(appInfo.packageName,0, userId);targetSdkVersion = applicationInfo.targetSdkVersion;}catch(PackageManager.NameNotFoundException e){}if(targetSdkVersion >=Build.VERSION_CODES.O_MR1){thrownewRuntimeException("invalid channel for service notification: "+ foregroundNoti);}}if(localForegroundNoti.getSmallIcon()==null){// Notifications whose icon is 0 are defined to not show// a notification, silently ignoring it. We don't want to// just ignore it, we want to prevent the service from// being foreground.thrownewRuntimeException("invalid service notification: "+ foregroundNoti);}// 通知入隊(duì)nm.enqueueNotification(localPackageName, localPackageName,appUid, appPid,null, localForegroundId, localForegroundNoti,userId);foregroundNoti = localForegroundNoti;// save it for amending next time}catch(RuntimeException e){Slog.w(TAG,"Error showing notification for service", e);// 出現(xiàn)問(wèn)題會(huì)stop server,阿里巴巴app就經(jīng)常因?yàn)檫@個(gè)原因被殺ams.mServices.killMisbehavingService(record,appUid, appPid, localPackageName);}}});}}
privatevoidcancelForegroundNotificationLocked(ServiceRecord r){// notification id不為0if(r.foregroundId !=0){// First check to see if this app has any other active foreground services// with the same notification ID. If so, we shouldn't actually cancel it,// because that would wipe away the notification that still needs to be shown// due the other service.ServiceMap sm =getServiceMapLocked(r.userId);if(sm !=null){for(int i = sm.mServicesByInstanceName.size()-1; i >=0; i--){ServiceRecord other = sm.mServicesByInstanceName.valueAt(i);if(other != r && other.foregroundId == r.foregroundId&& other.packageName.equals(r.packageName)){// Found one! Abort the cancel.return;}}}r.cancelNotification();}}publicvoidcancelNotification(){// 異步執(zhí)行,避免死鎖finalString localPackageName = packageName;finalint localForegroundId = foregroundId;finalint appUid = appInfo.uid;finalint appPid = app !=null? app.pid :0;ams.mHandler.post(newRunnable(){publicvoidrun(){NotificationManagerInternal nm =LocalServices.getService(NotificationManagerInternal.class);if(nm ==null){return;}try{nm.cancelNotification(localPackageName, localPackageName, appUid, appPid,null, localForegroundId, userId);}catch(RuntimeException e){Slog.w(TAG,"Error canceling notification for service", e);}}});}
后臺(tái)啟動(dòng)的前臺(tái)Service不得擁有使用中權(quán)限
android會(huì)限制從后臺(tái)啟動(dòng)的前臺(tái)service擁有“使用中”權(quán)限,包括位置、攝像頭、麥克風(fēng)等,否則會(huì)打印出如下的警告信息。 W ActivityManager: Foreground service started from background can not have location/camera/microphone access: service com.mi.health/.scenerecognition.SceneForegroundService