| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License |
| */ |
| |
| package com.android.server.am; |
| |
| import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; |
| |
| import android.app.AppOpsManager; |
| import android.app.Notification; |
| import android.app.NotificationManager; |
| import android.app.PendingIntent; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.IIntentReceiver; |
| import android.content.Intent; |
| import android.content.pm.ResolveInfo; |
| import android.os.Binder; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.os.Process; |
| import android.os.UserHandle; |
| import android.util.Slog; |
| |
| import com.android.internal.R; |
| import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; |
| import com.android.internal.notification.SystemNotificationChannels; |
| import com.android.internal.util.ProgressReporter; |
| import com.android.server.UiThread; |
| |
| import java.util.List; |
| |
| /** |
| * Simple broadcaster that sends {@link Intent#ACTION_PRE_BOOT_COMPLETED} to all |
| * system apps that register for it. Override {@link #onFinished()} to handle |
| * when all broadcasts are finished. |
| */ |
| public abstract class PreBootBroadcaster extends IIntentReceiver.Stub { |
| private static final String TAG = "PreBootBroadcaster"; |
| |
| private final ActivityManagerService mService; |
| private final int mUserId; |
| private final ProgressReporter mProgress; |
| private final boolean mQuiet; |
| |
| private final Intent mIntent; |
| private final List<ResolveInfo> mTargets; |
| |
| private int mIndex = 0; |
| |
| public PreBootBroadcaster(ActivityManagerService service, int userId, |
| ProgressReporter progress, boolean quiet) { |
| mService = service; |
| mUserId = userId; |
| mProgress = progress; |
| mQuiet = quiet; |
| |
| mIntent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED); |
| mIntent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE | Intent.FLAG_DEBUG_TRIAGED_MISSING); |
| |
| mTargets = mService.mContext.getPackageManager().queryBroadcastReceiversAsUser(mIntent, |
| MATCH_SYSTEM_ONLY, UserHandle.of(userId)); |
| } |
| |
| public void sendNext() { |
| if (mIndex >= mTargets.size()) { |
| mHandler.obtainMessage(MSG_HIDE).sendToTarget(); |
| onFinished(); |
| return; |
| } |
| |
| if (!mService.isUserRunning(mUserId, 0)) { |
| Slog.i(TAG, "User " + mUserId + " is no longer running; skipping remaining receivers"); |
| mHandler.obtainMessage(MSG_HIDE).sendToTarget(); |
| onFinished(); |
| return; |
| } |
| |
| if (!mQuiet) { |
| mHandler.obtainMessage(MSG_SHOW, mTargets.size(), mIndex).sendToTarget(); |
| } |
| |
| final ResolveInfo ri = mTargets.get(mIndex++); |
| final ComponentName componentName = ri.activityInfo.getComponentName(); |
| |
| if (mProgress != null) { |
| final CharSequence label = ri.activityInfo |
| .loadLabel(mService.mContext.getPackageManager()); |
| mProgress.setProgress(mIndex, mTargets.size(), |
| mService.mContext.getString(R.string.android_preparing_apk, label)); |
| } |
| |
| Slog.i(TAG, "Pre-boot of " + componentName.toShortString() + " for user " + mUserId); |
| EventLogTags.writeAmPreBoot(mUserId, componentName.getPackageName()); |
| |
| mIntent.setComponent(componentName); |
| mService.broadcastIntentLocked(null, null, mIntent, null, this, 0, null, null, null, |
| AppOpsManager.OP_NONE, null, true, false, ActivityManagerService.MY_PID, |
| Process.SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), mUserId); |
| } |
| |
| @Override |
| public void performReceive(Intent intent, int resultCode, String data, Bundle extras, |
| boolean ordered, boolean sticky, int sendingUser) { |
| sendNext(); |
| } |
| |
| private static final int MSG_SHOW = 1; |
| private static final int MSG_HIDE = 2; |
| |
| private Handler mHandler = new Handler(UiThread.get().getLooper(), null, true) { |
| @Override |
| public void handleMessage(Message msg) { |
| final Context context = mService.mContext; |
| final NotificationManager notifManager = context |
| .getSystemService(NotificationManager.class); |
| final int max = msg.arg1; |
| final int index = msg.arg2; |
| |
| switch (msg.what) { |
| case MSG_SHOW: |
| final CharSequence title = context |
| .getText(R.string.android_upgrading_notification_title); |
| |
| final Intent intent = new Intent(); |
| intent.setClassName("com.android.settings", |
| "com.android.settings.HelpTrampoline"); |
| intent.putExtra(Intent.EXTRA_TEXT, "help_url_upgrading"); |
| |
| final PendingIntent contentIntent; |
| if (context.getPackageManager().resolveActivity(intent, 0) != null) { |
| contentIntent = PendingIntent.getActivity(context, 0, intent, 0); |
| } else { |
| contentIntent = null; |
| } |
| |
| final Notification notif = |
| new Notification.Builder(mService.mContext, |
| SystemNotificationChannels.UPDATES) |
| .setSmallIcon(R.drawable.stat_sys_adb) |
| .setWhen(0) |
| .setOngoing(true) |
| .setTicker(title) |
| .setColor(context.getColor( |
| com.android.internal.R.color.system_notification_accent_color)) |
| .setContentTitle(title) |
| .setContentIntent(contentIntent) |
| .setVisibility(Notification.VISIBILITY_PUBLIC) |
| .setProgress(max, index, false) |
| .build(); |
| notifManager.notifyAsUser(TAG, SystemMessage.NOTE_SYSTEM_UPGRADING, notif, |
| UserHandle.of(mUserId)); |
| break; |
| |
| case MSG_HIDE: |
| notifManager.cancelAsUser(TAG, SystemMessage.NOTE_SYSTEM_UPGRADING, |
| UserHandle.of(mUserId)); |
| break; |
| } |
| } |
| }; |
| |
| public abstract void onFinished(); |
| } |