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之后前臺service需要申請FOREGROUND_SERVICE權限if(r.appInfo.targetSdkVersion >=Build.VERSION_CODES.P){mAm.enforcePermission(android.Manifest.permission.FOREGROUND_SERVICE,r.app.pid, r.appInfo.uid,"startForeground");}// 一般傳入的service type默認為FOREGROUND_SERVICE_TYPE_MANIFEST,而manifestType默認為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");}// 后臺啟動的前臺Service不得擁有“使用中權限”,下面有講到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;//移除前臺service 10s的超時消息。重置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不在電池優化白名單內&不是前臺app啟動,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相關變量賦值notification.flags |=Notification.FLAG_FOREGROUND_SERVICE;r.foregroundNoti = notification;r.foregroundServiceType = foregroundServiceType;// 前臺service第一次設置會為falseif(!r.isForeground){finalServiceMap smap =getServiceMapLocked(r.userId);if(smap !=null){// 統計每個app前臺service的數量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++;}// 設置isForeground為true,第二次執行該方法后不必執行這些代碼,且表示當前service在前臺r.isForeground =true;........}// 每次執行該方法都會重新post通知,異步執行r.postNotification();if(r.app !=null){// 更新進程優先級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 ,是取消前臺service的流程,下面講}
發送通知
通知入隊等操作異步進行,避免死鎖
通知沒有對應的icon,打印警告信息,并給設置一個簡陋的通知模板,告知用戶當前正在運行該進程 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){// 通知沒有對應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));// 重新構建通知相關信息Notification.Builder notiBuilder =newNotification.Builder(ctx,localForegroundNoti.getChannelId());// 設置icon為app應用信息的iconnotiBuilder.setSmallIcon(appInfo.icon);// mark as foregroundnotiBuilder.setFlag(Notification.FLAG_FOREGROUND_SERVICE,true);// 構建pendingIntentrunningIntent.setData(Uri.fromParts("package", appInfo.packageName,null));PendingIntent pi =PendingIntent.getActivityAsUser(ams.mContext,0,runningIntent,PendingIntent.FLAG_UPDATE_CURRENT,null,UserHandle.of(userId));// 設置系統的通知顏色notiBuilder.setColor(ams.mContext.getColor(com.android.internal.R.color.system_notification_accent_color));// 設置通知標題為“xxx 正在運行”notiBuilder.setContentTitle(ams.mContext.getString(com.android.internal.R.string.app_running_notification_title,appName));// 設置通知的內容"點按即可了解詳情或停止應用",miui定制國內版本為"可能導致系統卡頓,降低待機時間,點按關閉"// 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);}// 通知入隊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);// 出現問題會stop server,阿里巴巴app就經常因為這個原因被殺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(){// 異步執行,避免死鎖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);}}});}
后臺啟動的前臺Service不得擁有使用中權限
android會限制從后臺啟動的前臺service擁有“使用中”權限,包括位置、攝像頭、麥克風等,否則會打印出如下的警告信息。 W ActivityManager: Foreground service started from background can not have location/camera/microphone access: service com.mi.health/.scenerecognition.SceneForegroundService