1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.server.am;
18
19import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
20
21import android.app.AppOpsManager;
22import android.app.Notification;
23import android.app.NotificationManager;
24import android.app.PendingIntent;
25import android.content.ComponentName;
26import android.content.Context;
27import android.content.IIntentReceiver;
28import android.content.Intent;
29import android.content.pm.ResolveInfo;
30import android.os.Bundle;
31import android.os.Handler;
32import android.os.Message;
33import android.os.Process;
34import android.os.UserHandle;
35import android.util.Slog;
36
37import com.android.internal.R;
38import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
39import com.android.internal.notification.SystemNotificationChannels;
40import com.android.internal.util.ProgressReporter;
41import com.android.server.UiThread;
42
43import java.util.List;
44
45/**
46 * Simple broadcaster that sends {@link Intent#ACTION_PRE_BOOT_COMPLETED} to all
47 * system apps that register for it. Override {@link #onFinished()} to handle
48 * when all broadcasts are finished.
49 */
50public abstract class PreBootBroadcaster extends IIntentReceiver.Stub {
51    private static final String TAG = "PreBootBroadcaster";
52
53    private final ActivityManagerService mService;
54    private final int mUserId;
55    private final ProgressReporter mProgress;
56    private final boolean mQuiet;
57
58    private final Intent mIntent;
59    private final List<ResolveInfo> mTargets;
60
61    private int mIndex = 0;
62
63    public PreBootBroadcaster(ActivityManagerService service, int userId,
64            ProgressReporter progress, boolean quiet) {
65        mService = service;
66        mUserId = userId;
67        mProgress = progress;
68        mQuiet = quiet;
69
70        mIntent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
71        mIntent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE | Intent.FLAG_DEBUG_TRIAGED_MISSING);
72
73        mTargets = mService.mContext.getPackageManager().queryBroadcastReceiversAsUser(mIntent,
74                MATCH_SYSTEM_ONLY, UserHandle.of(userId));
75    }
76
77    public void sendNext() {
78        if (mIndex >= mTargets.size()) {
79            mHandler.obtainMessage(MSG_HIDE).sendToTarget();
80            onFinished();
81            return;
82        }
83
84        if (!mService.isUserRunning(mUserId, 0)) {
85            Slog.i(TAG, "User " + mUserId + " is no longer running; skipping remaining receivers");
86            mHandler.obtainMessage(MSG_HIDE).sendToTarget();
87            onFinished();
88            return;
89        }
90
91        if (!mQuiet) {
92            mHandler.obtainMessage(MSG_SHOW, mTargets.size(), mIndex).sendToTarget();
93        }
94
95        final ResolveInfo ri = mTargets.get(mIndex++);
96        final ComponentName componentName = ri.activityInfo.getComponentName();
97
98        if (mProgress != null) {
99            final CharSequence label = ri.activityInfo
100                    .loadLabel(mService.mContext.getPackageManager());
101            mProgress.setProgress(mIndex, mTargets.size(),
102                    mService.mContext.getString(R.string.android_preparing_apk, label));
103        }
104
105        Slog.i(TAG, "Pre-boot of " + componentName.toShortString() + " for user " + mUserId);
106        EventLogTags.writeAmPreBoot(mUserId, componentName.getPackageName());
107
108        mIntent.setComponent(componentName);
109        mService.broadcastIntentLocked(null, null, mIntent, null, this, 0, null, null, null,
110                AppOpsManager.OP_NONE, null, true, false, ActivityManagerService.MY_PID,
111                Process.SYSTEM_UID, mUserId);
112    }
113
114    @Override
115    public void performReceive(Intent intent, int resultCode, String data, Bundle extras,
116            boolean ordered, boolean sticky, int sendingUser) {
117        sendNext();
118    }
119
120    private static final int MSG_SHOW = 1;
121    private static final int MSG_HIDE = 2;
122
123    private Handler mHandler = new Handler(UiThread.get().getLooper(), null, true) {
124        @Override
125        public void handleMessage(Message msg) {
126            final Context context = mService.mContext;
127            final NotificationManager notifManager = context
128                    .getSystemService(NotificationManager.class);
129            final int max = msg.arg1;
130            final int index = msg.arg2;
131
132            switch (msg.what) {
133                case MSG_SHOW:
134                    final CharSequence title = context
135                            .getText(R.string.android_upgrading_notification_title);
136
137                    final Intent intent = new Intent();
138                    intent.setClassName("com.android.settings",
139                            "com.android.settings.HelpTrampoline");
140                    intent.putExtra(Intent.EXTRA_TEXT, "help_url_upgrading");
141
142                    final PendingIntent contentIntent;
143                    if (context.getPackageManager().resolveActivity(intent, 0) != null) {
144                        contentIntent = PendingIntent.getActivity(context, 0, intent, 0);
145                    } else {
146                        contentIntent = null;
147                    }
148
149                    final Notification notif =
150                            new Notification.Builder(mService.mContext,
151                                    SystemNotificationChannels.UPDATES)
152                            .setSmallIcon(R.drawable.stat_sys_adb)
153                            .setWhen(0)
154                            .setOngoing(true)
155                            .setTicker(title)
156                            .setColor(context.getColor(
157                                    com.android.internal.R.color.system_notification_accent_color))
158                            .setContentTitle(title)
159                            .setContentIntent(contentIntent)
160                            .setVisibility(Notification.VISIBILITY_PUBLIC)
161                            .setProgress(max, index, false)
162                            .build();
163                    notifManager.notifyAsUser(TAG, SystemMessage.NOTE_SYSTEM_UPGRADING, notif,
164                            UserHandle.of(mUserId));
165                    break;
166
167                case MSG_HIDE:
168                    notifManager.cancelAsUser(TAG, SystemMessage.NOTE_SYSTEM_UPGRADING,
169                            UserHandle.of(mUserId));
170                    break;
171            }
172        }
173    };
174
175    public abstract void onFinished();
176}
177