/*
|
* 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);
|
synchronized (mService) {
|
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();
|
}
|