diff options
author | Jeff Sharkey <jsharkey@android.com> | 2016-03-22 15:32:31 -0600 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2016-03-27 10:56:48 -0600 |
commit | bd91e2f3f6aca512a02be645b2515b5e3331e177 (patch) | |
tree | 490fcef8a5acef7571dc72e3707abbf9c43abd35 | |
parent | 40e1135ea2780fefecf532fb7a1cc43e26bcecc2 (diff) |
Update PRE_BOOT_COMPLETED for FBE.
Now that CE data isn't available until after a user is unlocked, we
need to delay the PRE_BOOT_COMPLETED broadcasts. This is done by
adding a new RUNNING_UNLOCKING user state to the UserController
lifecycle.
We now track the last fingerprint a user was logged in under, and we
dispatch PRE_BOOT receivers when that fingerprint changes. To work
around battery pull issues, we only persist the updated fingerprint
once all PRE_BOOT receivers have finished. This is less granular
than the original solution, but it's still correct. We only consider
a user as "logged in" once it transitions into the RUNNING_UNLOCKED
state.
When starting a process, track if the user was "unlocked" when
started, so that we only spin up unaware providers in processes
started before user unlock.
Add generic IProgressListener to communicate PRE_BOOT progress and
strings up to lock screen. For now, LockSettingsService just blocks
until finished, but it could display these strings in the future.
Bug: 27220885
Change-Id: I349439776b885acd32f6a578d8951ffd95640be2
16 files changed, 581 insertions, 264 deletions
diff --git a/Android.mk b/Android.mk index 59b2a46b76af..c12a8e714271 100644 --- a/Android.mk +++ b/Android.mk @@ -226,6 +226,7 @@ LOCAL_SRC_FILES += \ core/java/android/os/INetworkManagementService.aidl \ core/java/android/os/IPermissionController.aidl \ core/java/android/os/IProcessInfoService.aidl \ + core/java/android/os/IProgressListener.aidl \ core/java/android/os/IPowerManager.aidl \ core/java/android/os/IRecoverySystem.aidl \ core/java/android/os/IRecoverySystemProgressListener.aidl \ diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 86734b1c222c..221b2d36fcec 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -1141,7 +1141,7 @@ public class Am extends BaseCommand { int userId = Integer.parseInt(nextArgRequired()); byte[] token = argToBytes(nextArgRequired()); byte[] secret = argToBytes(nextArgRequired()); - boolean success = mAm.unlockUser(userId, token, secret); + boolean success = mAm.unlockUser(userId, token, secret, null); if (success) { System.out.println("Success: user unlocked"); } else { diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index f5d7e7e068cf..4bf48a338956 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -41,6 +41,7 @@ import android.os.Binder; import android.os.Bundle; import android.os.Debug; import android.os.IBinder; +import android.os.IProgressListener; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; @@ -2122,7 +2123,9 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM int userId = data.readInt(); byte[] token = data.createByteArray(); byte[] secret = data.createByteArray(); - boolean result = unlockUser(userId, token, secret); + IProgressListener listener = IProgressListener.Stub + .asInterface(data.readStrongBinder()); + boolean result = unlockUser(userId, token, secret, listener); reply.writeNoException(); reply.writeInt(result ? 1 : 0); return true; @@ -5707,13 +5710,15 @@ class ActivityManagerProxy implements IActivityManager return result; } - public boolean unlockUser(int userId, byte[] token, byte[] secret) throws RemoteException { + public boolean unlockUser(int userId, byte[] token, byte[] secret, IProgressListener listener) + throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeInt(userId); data.writeByteArray(token); data.writeByteArray(secret); + data.writeStrongInterface(listener); mRemote.transact(IActivityManager.UNLOCK_USER_TRANSACTION, data, reply, 0); reply.readException(); boolean result = reply.readInt() != 0; diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 639c207ec6d9..417c0679e233 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -47,6 +47,7 @@ import android.os.Bundle; import android.os.Debug; import android.os.IBinder; import android.os.IInterface; +import android.os.IProgressListener; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; @@ -461,7 +462,8 @@ public interface IActivityManager extends IInterface { // Multi-user APIs public boolean switchUser(int userid) throws RemoteException; public boolean startUserInBackground(int userid) throws RemoteException; - public boolean unlockUser(int userid, byte[] token, byte[] secret) throws RemoteException; + public boolean unlockUser(int userid, byte[] token, byte[] secret, IProgressListener listener) + throws RemoteException; public int stopUser(int userid, boolean force, IStopUserCallback callback) throws RemoteException; public UserInfo getCurrentUser() throws RemoteException; public boolean isUserRunning(int userid, int flags) throws RemoteException; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 57ab7a2f937c..66e0ada27b52 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2856,12 +2856,15 @@ public class Intent implements Parcelable, Cloneable { "com.google.android.c2dm.intent.RECEIVE"; /** - * Broadcast Action: hook for permforming cleanup after a system update. + * Broadcast Action: This is broadcast once when the user is booting after a + * system update. It can be used to perform cleanup or upgrades after a + * system update. + * <p> + * This broadcast is sent after the {@link #ACTION_LOCKED_BOOT_COMPLETED} + * broadcast but before the {@link #ACTION_BOOT_COMPLETED} broadcast. It's + * only sent when the {@link Build#FINGERPRINT} has changed, and it's only + * sent to receivers in the system image. * - * The broadcast is sent when the system is booting, before the - * BOOT_COMPLETED broadcast. It is only sent to receivers in the system - * image. A receiver for this should do its work and then disable itself - * so that it does not get run again at the next boot. * @hide */ public static final String ACTION_PRE_BOOT_COMPLETED = diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java index e8a3438dd974..dd3a36c7996c 100644 --- a/core/java/android/content/pm/UserInfo.java +++ b/core/java/android/content/pm/UserInfo.java @@ -96,6 +96,7 @@ public class UserInfo implements Parcelable { public int flags; public long creationTime; public long lastLoggedInTime; + public String lastLoggedInFingerprint; public int profileGroupId; public int restrictedProfileParentId; @@ -214,6 +215,7 @@ public class UserInfo implements Parcelable { serialNumber = orig.serialNumber; creationTime = orig.creationTime; lastLoggedInTime = orig.lastLoggedInTime; + lastLoggedInFingerprint = orig.lastLoggedInFingerprint; partial = orig.partial; profileGroupId = orig.profileGroupId; restrictedProfileParentId = orig.restrictedProfileParentId; @@ -241,6 +243,7 @@ public class UserInfo implements Parcelable { dest.writeInt(serialNumber); dest.writeLong(creationTime); dest.writeLong(lastLoggedInTime); + dest.writeString(lastLoggedInFingerprint); dest.writeInt(partial ? 1 : 0); dest.writeInt(profileGroupId); dest.writeInt(guestToRemove ? 1 : 0); @@ -265,6 +268,7 @@ public class UserInfo implements Parcelable { serialNumber = source.readInt(); creationTime = source.readLong(); lastLoggedInTime = source.readLong(); + lastLoggedInFingerprint = source.readString(); partial = source.readInt() != 0; profileGroupId = source.readInt(); guestToRemove = source.readInt() != 0; diff --git a/core/java/android/os/IProgressListener.aidl b/core/java/android/os/IProgressListener.aidl new file mode 100644 index 000000000000..ad58a7cafe19 --- /dev/null +++ b/core/java/android/os/IProgressListener.aidl @@ -0,0 +1,26 @@ +/* + * 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 android.os; + +import android.os.Bundle; + +/** @hide */ +oneway interface IProgressListener { + void onStarted(int id, in Bundle extras); + void onProgress(int id, int progress, in Bundle extras); + void onFinished(int id, in Bundle extras); +} diff --git a/core/java/com/android/internal/util/ProgressReporter.java b/core/java/com/android/internal/util/ProgressReporter.java new file mode 100644 index 000000000000..796f8acccdcb --- /dev/null +++ b/core/java/com/android/internal/util/ProgressReporter.java @@ -0,0 +1,164 @@ +/* + * 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.internal.util; + +import android.annotation.Nullable; +import android.content.Intent; +import android.os.Bundle; +import android.os.IProgressListener; +import android.os.RemoteException; +import android.util.MathUtils; + +/** + * Tracks and reports progress of a single task to a {@link IProgressListener}. + * The reported progress of a task ranges from 0-100, but the task can be + * segmented into smaller pieces using {@link #startSegment(int)} and + * {@link #endSegment(int[])}, and segments can be nested. + * <p> + * Here's an example in action; when finished the overall task progress will be + * at 60. + * + * <pre> + * prog.setProgress(20); + * { + * final int restore = prog.startSegment(40); + * for (int i = 0; i < N; i++) { + * prog.setProgress(i, N); + * ... + * } + * prog.endSegment(restore); + * } + * </pre> + * + * This class is not thread safe. + * + * @hide + */ +public class ProgressReporter { + public static final ProgressReporter NO_OP = new ProgressReporter(0, null); + + private final int mId; + private final IProgressListener mListener; + + private Bundle mExtras = new Bundle(); + + private int mProgress = 0; + + /** + * Current segment range: first element is starting progress of this + * segment, second element is length of segment. + */ + private int[] mSegmentRange = new int[] { 0, 100 }; + + /** + * Create a new task with the given identifier whose progress will be + * reported to the given listener. + */ + public ProgressReporter(int id, @Nullable IProgressListener listener) { + mId = id; + mListener = listener; + } + + /** + * Set the progress of the currently active segment. + * + * @param progress Segment progress between 0-100. + */ + public void setProgress(int progress) { + setProgress(progress, 100, null); + } + + /** + * Set the progress of the currently active segment. + * + * @param progress Segment progress between 0-100. + */ + public void setProgress(int progress, @Nullable CharSequence title) { + setProgress(progress, 100, title); + } + + /** + * Set the fractional progress of the currently active segment. + */ + public void setProgress(int n, int m) { + setProgress(n, m, null); + } + + /** + * Set the fractional progress of the currently active segment. + */ + public void setProgress(int n, int m, @Nullable CharSequence title) { + mProgress = mSegmentRange[0] + + MathUtils.constrain((n * mSegmentRange[1]) / m, 0, mSegmentRange[1]); + if (title != null) { + mExtras.putCharSequence(Intent.EXTRA_TITLE, title); + } + notifyProgress(mId, mProgress, mExtras); + } + + /** + * Start a new inner segment that will contribute the given range towards + * the currently active segment. You must pass the returned value to + * {@link #endSegment(int[])} when finished. + */ + public int[] startSegment(int size) { + final int[] lastRange = mSegmentRange; + mSegmentRange = new int[] { mProgress, (size * mSegmentRange[1] / 100) }; + return lastRange; + } + + /** + * End the current segment. + */ + public void endSegment(int[] lastRange) { + mProgress = mSegmentRange[0] + mSegmentRange[1]; + mSegmentRange = lastRange; + } + + int getProgress() { + return mProgress; + } + + int[] getSegmentRange() { + return mSegmentRange; + } + + /** + * Report this entire task as being finished. + */ + public void finish() { + notifyFinished(mId, null); + } + + private void notifyProgress(int id, int progress, Bundle extras) { + if (mListener != null) { + try { + mListener.onProgress(id, progress, extras); + } catch (RemoteException ignored) { + } + } + } + + public void notifyFinished(int id, Bundle extras) { + if (mListener != null) { + try { + mListener.onFinished(id, extras); + } catch (RemoteException ignored) { + } + } + } +} diff --git a/core/tests/coretests/src/com/android/internal/util/ProgressReporterTest.java b/core/tests/coretests/src/com/android/internal/util/ProgressReporterTest.java new file mode 100644 index 000000000000..fbf552322451 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/util/ProgressReporterTest.java @@ -0,0 +1,119 @@ +/* + * 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.internal.util; + +import junit.framework.TestCase; + +public class ProgressReporterTest extends TestCase { + private ProgressReporter r; + + @Override + protected void setUp() throws Exception { + super.setUp(); + r = new ProgressReporter(0, null); + } + + private void assertProgress(int expected) { + assertEquals(expected, r.getProgress()); + } + + private void assertRange(int start, int len) { + final int[] range = r.getSegmentRange(); + assertEquals("start", start, range[0]); + assertEquals("len", len, range[1]); + } + + public void testBasic() throws Exception { + assertProgress(0); + + r.setProgress(20); + assertProgress(20); + + r.setProgress(-20); + assertProgress(0); + + r.setProgress(1024); + assertProgress(100); + } + + public void testSegment() throws Exception { + r.setProgress(20); + assertProgress(20); + + final int[] lastRange = r.startSegment(40); + { + assertProgress(20); + + r.setProgress(50); + assertProgress(40); + } + r.endSegment(lastRange); + assertProgress(60); + + r.setProgress(80); + assertProgress(80); + } + + public void testSegmentOvershoot() throws Exception { + r.setProgress(20); + assertProgress(20); + + final int[] lastRange = r.startSegment(40); + { + r.setProgress(-100, 2); + assertProgress(20); + + r.setProgress(1, 2); + assertProgress(40); + + r.setProgress(100, 2); + assertProgress(60); + } + r.endSegment(lastRange); + assertProgress(60); + } + + public void testSegmentNested() throws Exception { + r.setProgress(20); + assertProgress(20); + assertRange(0, 100); + + final int[] lastRange = r.startSegment(40); + assertRange(20, 40); + { + r.setProgress(50); + assertProgress(40); + + final int[] lastRange2 = r.startSegment(25); + assertRange(40, 10); + { + r.setProgress(0); + assertProgress(40); + + r.setProgress(50); + assertProgress(45); + + r.setProgress(100); + assertProgress(50); + } + r.endSegment(lastRange2); + assertProgress(50); + } + r.endSegment(lastRange); + assertProgress(60); + } +} diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java index 6fb0671e6f71..ed16af51c7eb 100644 --- a/services/core/java/com/android/server/LockSettingsService.java +++ b/services/core/java/com/android/server/LockSettingsService.java @@ -42,7 +42,10 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR import android.database.sqlite.SQLiteDatabase; import android.os.Binder; +import android.os.Bundle; import android.os.IBinder; +import android.os.IProgressListener; +import android.os.Parcel; import android.os.RemoteException; import android.os.storage.IMountService; import android.os.ServiceManager; @@ -56,6 +59,7 @@ import android.security.KeyStore; import android.service.gatekeeper.GateKeeperResponse; import android.service.gatekeeper.IGateKeeperService; import android.text.TextUtils; +import android.util.Log; import android.util.Slog; import com.android.internal.util.ArrayUtils; @@ -70,6 +74,8 @@ import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * Keeps the lock pattern/password data and related settings for each user. @@ -590,11 +596,37 @@ public class LockSettingsService extends ILockSettings.Stub { } private void unlockUser(int userId, byte[] token, byte[] secret) { + // TODO: make this method fully async so we can update UI with progress strings + final CountDownLatch latch = new CountDownLatch(1); + final IProgressListener listener = new IProgressListener.Stub() { + @Override + public void onStarted(int id, Bundle extras) throws RemoteException { + // Ignored + } + + @Override + public void onProgress(int id, int progress, Bundle extras) throws RemoteException { + // Ignored + } + + @Override + public void onFinished(int id, Bundle extras) throws RemoteException { + Log.d(TAG, "unlockUser finished!"); + latch.countDown(); + } + }; + try { - ActivityManagerNative.getDefault().unlockUser(userId, token, secret); + ActivityManagerNative.getDefault().unlockUser(userId, token, secret, listener); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } + + try { + latch.await(15, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } } private byte[] getCurrentHandle(int userId) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 3d13715820ae..8b67d0ee4941 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -40,6 +40,7 @@ import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.MemInfoReader; import com.android.internal.util.Preconditions; +import com.android.internal.util.ProgressReporter; import com.android.server.AppOpsService; import com.android.server.AttributeCache; import com.android.server.DeviceIdleController; @@ -165,6 +166,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.IPermissionController; import android.os.IProcessInfoService; +import android.os.IProgressListener; import android.os.Looper; import android.os.Message; import android.os.Parcel; @@ -214,10 +216,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -363,9 +361,6 @@ import static org.xmlpull.v1.XmlPullParser.START_TAG; public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { - // File that stores last updated system version and called preboot receivers - static final String CALLED_PRE_BOOTS_FILENAME = "called_pre_boots.dat"; - private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityManagerService" : TAG_AM; private static final String TAG_BACKUP = TAG + POSTFIX_BACKUP; private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST; @@ -500,8 +495,6 @@ public final class ActivityManagerService extends ActivityManagerNative static final int ALLOW_NON_FULL_IN_PROFILE = 1; static final int ALLOW_FULL_ONLY = 2; - static final int LAST_PREBOOT_DELIVERED_FILE_VERSION = 10000; - // Delay in notifying task stack change listeners (in millis) static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100; @@ -1112,8 +1105,6 @@ public final class ActivityManagerService extends ActivityManagerNative boolean mBooting = false; boolean mCallFinishBooting = false; boolean mBootAnimationComplete = false; - boolean mWaitingUpdate = false; - boolean mDidUpdate = false; boolean mOnBattery = false; boolean mLaunchWarningShown = false; @@ -6228,6 +6219,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.debugging = false; app.cached = false; app.killedByAm = false; + app.unlocked = mContext.getSystemService(UserManager.class).isUserUnlocked(app.userId); mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); @@ -10351,7 +10343,7 @@ public final class ActivityManagerService extends ActivityManagerNative } checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission"); - if (!mProcessesReady && !mDidUpdate && !mWaitingUpdate + if (!mProcessesReady && !cpi.processName.equals("system")) { // If this content provider does not run in the system // process, and the system is not yet ready to run other @@ -10942,7 +10934,7 @@ public final class ActivityManagerService extends ActivityManagerNative final int NA = apps.size(); for (int ia = 0; ia < NA; ia++) { final ProcessRecord app = apps.valueAt(ia); - if (app.userId != userId || app.thread == null) continue; + if (app.userId != userId || app.thread == null || app.unlocked) continue; final int NG = app.pkgList.size(); for (int ig = 0; ig < NG; ig++) { @@ -12574,187 +12566,6 @@ public final class ActivityManagerService extends ActivityManagerNative return mSystemReady; } - private static File getCalledPreBootReceiversFile() { - File dataDir = Environment.getDataDirectory(); - File systemDir = new File(dataDir, "system"); - File fname = new File(systemDir, CALLED_PRE_BOOTS_FILENAME); - return fname; - } - - private static ArrayList<ComponentName> readLastDonePreBootReceivers() { - ArrayList<ComponentName> lastDoneReceivers = new ArrayList<ComponentName>(); - File file = getCalledPreBootReceiversFile(); - FileInputStream fis = null; - try { - fis = new FileInputStream(file); - DataInputStream dis = new DataInputStream(new BufferedInputStream(fis, 2048)); - int fvers = dis.readInt(); - if (fvers == LAST_PREBOOT_DELIVERED_FILE_VERSION) { - String vers = dis.readUTF(); - String codename = dis.readUTF(); - String build = dis.readUTF(); - if (android.os.Build.VERSION.RELEASE.equals(vers) - && android.os.Build.VERSION.CODENAME.equals(codename) - && android.os.Build.VERSION.INCREMENTAL.equals(build)) { - int num = dis.readInt(); - while (num > 0) { - num--; - String pkg = dis.readUTF(); - String cls = dis.readUTF(); - lastDoneReceivers.add(new ComponentName(pkg, cls)); - } - } - } - } catch (FileNotFoundException e) { - } catch (IOException e) { - Slog.w(TAG, "Failure reading last done pre-boot receivers", e); - } finally { - if (fis != null) { - try { - fis.close(); - } catch (IOException e) { - } - } - } - return lastDoneReceivers; - } - - private static void writeLastDonePreBootReceivers(ArrayList<ComponentName> list) { - File file = getCalledPreBootReceiversFile(); - FileOutputStream fos = null; - DataOutputStream dos = null; - try { - fos = new FileOutputStream(file); - dos = new DataOutputStream(new BufferedOutputStream(fos, 2048)); - dos.writeInt(LAST_PREBOOT_DELIVERED_FILE_VERSION); - dos.writeUTF(android.os.Build.VERSION.RELEASE); - dos.writeUTF(android.os.Build.VERSION.CODENAME); - dos.writeUTF(android.os.Build.VERSION.INCREMENTAL); - dos.writeInt(list.size()); - for (int i=0; i<list.size(); i++) { - dos.writeUTF(list.get(i).getPackageName()); - dos.writeUTF(list.get(i).getClassName()); - } - } catch (IOException e) { - Slog.w(TAG, "Failure writing last done pre-boot receivers", e); - file.delete(); - } finally { - FileUtils.sync(fos); - if (dos != null) { - try { - dos.close(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - } - - final class PreBootContinuation extends IIntentReceiver.Stub { - final Intent intent; - final Runnable onFinishCallback; - final ArrayList<ComponentName> doneReceivers; - final List<ResolveInfo> ris; - final int[] users; - int lastRi = -1; - int curRi = 0; - int curUser = 0; - - PreBootContinuation(Intent _intent, Runnable _onFinishCallback, - ArrayList<ComponentName> _doneReceivers, List<ResolveInfo> _ris, int[] _users) { - intent = _intent; - onFinishCallback = _onFinishCallback; - doneReceivers = _doneReceivers; - ris = _ris; - users = _users; - } - - void go() { - if (lastRi != curRi) { - ActivityInfo ai = ris.get(curRi).activityInfo; - ComponentName comp = new ComponentName(ai.packageName, ai.name); - intent.setComponent(comp); - doneReceivers.add(comp); - lastRi = curRi; - CharSequence label = ai.loadLabel(mContext.getPackageManager()); - showBootMessage(mContext.getString(R.string.android_preparing_apk, label), false); - } - Slog.i(TAG, "Pre-boot of " + intent.getComponent().toShortString() - + " for user " + users[curUser]); - EventLogTags.writeAmPreBoot(users[curUser], intent.getComponent().getPackageName()); - broadcastIntentLocked(null, null, intent, null, this, - 0, null, null, null, AppOpsManager.OP_NONE, - null, true, false, MY_PID, Process.SYSTEM_UID, users[curUser]); - } - - public void performReceive(Intent intent, int resultCode, - String data, Bundle extras, boolean ordered, - boolean sticky, int sendingUser) { - curUser++; - if (curUser >= users.length) { - curUser = 0; - curRi++; - if (curRi >= ris.size()) { - // All done sending broadcasts! - if (onFinishCallback != null) { - // The raw IIntentReceiver interface is called - // with the AM lock held, so redispatch to - // execute our code without the lock. - mHandler.post(onFinishCallback); - } - return; - } - } - go(); - } - } - - private boolean deliverPreBootCompleted(final Runnable onFinishCallback, - ArrayList<ComponentName> doneReceivers) { - Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED); - List<ResolveInfo> ris = null; - try { - ris = AppGlobals.getPackageManager().queryIntentReceivers( - intent, null, MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM).getList(); - } catch (RemoteException e) { - } - if (ris == null) { - return false; - } - intent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE | Intent.FLAG_DEBUG_TRIAGED_MISSING); - - ArrayList<ComponentName> lastDoneReceivers = readLastDonePreBootReceivers(); - for (int i=0; i<ris.size(); i++) { - ActivityInfo ai = ris.get(i).activityInfo; - ComponentName comp = new ComponentName(ai.packageName, ai.name); - if (lastDoneReceivers.contains(comp)) { - // We already did the pre boot receiver for this app with the current - // platform version, so don't do it again... - ris.remove(i); - i--; - // ...however, do keep it as one that has been done, so we don't - // forget about it when rewriting the file of last done receivers. - doneReceivers.add(comp); - } - } - - if (ris.size() <= 0) { - return false; - } - - // TODO: can we still do this with per user encryption? - final int[] users = mUserController.getUsers(); - if (users.length <= 0) { - return false; - } - - PreBootContinuation cont = new PreBootContinuation(intent, onFinishCallback, doneReceivers, - ris, users); - cont.go(); - return true; - } - public void systemReady(final Runnable goingCallback) { synchronized(this) { if (mSystemReady) { @@ -12771,33 +12582,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Make sure we have the current profile info, since it is needed for security checks. mUserController.onSystemReady(); - mRecentTasks.onSystemReadyLocked(); - // Check to see if there are any update receivers to run. - if (!mDidUpdate) { - if (mWaitingUpdate) { - return; - } - final ArrayList<ComponentName> doneReceivers = new ArrayList<ComponentName>(); - mWaitingUpdate = deliverPreBootCompleted(new Runnable() { - public void run() { - synchronized (ActivityManagerService.this) { - mDidUpdate = true; - } - showBootMessage(mContext.getText( - R.string.android_upgrading_complete), - false); - writeLastDonePreBootReceivers(doneReceivers); - systemReady(goingCallback); - } - }, doneReceivers); - - if (mWaitingUpdate) { - return; - } - mDidUpdate = true; - } - mAppOpsService.systemReady(); mSystemReady = true; } @@ -20725,8 +20510,8 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override - public boolean unlockUser(int userId, byte[] token, byte[] secret) { - return mUserController.unlockUser(userId, token, secret); + public boolean unlockUser(int userId, byte[] token, byte[] secret, IProgressListener listener) { + return mUserController.unlockUser(userId, token, secret, new ProgressReporter(0, listener)); } @Override diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java new file mode 100644 index 000000000000..1825c88628cc --- /dev/null +++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java @@ -0,0 +1,95 @@ +/* + * 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.content.ComponentName; +import android.content.IIntentReceiver; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.os.Bundle; +import android.os.Process; +import android.os.UserHandle; +import android.util.Slog; + +import com.android.internal.R; +import com.android.internal.util.ProgressReporter; + +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 Intent mIntent; + private final List<ResolveInfo> mTargets; + + private int mIndex = 0; + + public PreBootBroadcaster(ActivityManagerService service, int userId, + ProgressReporter progress) { + mService = service; + mUserId = userId; + mProgress = progress; + + 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()) { + onFinished(); + return; + } + + final ResolveInfo ri = mTargets.get(mIndex++); + final ComponentName componentName = ri.activityInfo.getComponentName(); + + 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, mUserId); + } + + @Override + public void performReceive(Intent intent, int resultCode, String data, Bundle extras, + boolean ordered, boolean sticky, int sendingUser) { + sendNext(); + } + + public abstract void onFinished(); +} diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 5407d2846b07..bacbd3e5e25a 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -116,6 +116,7 @@ final class ProcessRecord { boolean killed; // True once we know the process has been killed boolean procStateChanged; // Keep track of whether we changed 'setAdj'. boolean reportedInteraction;// Whether we have told usage stats about it being an interaction + boolean unlocked; // True when proc was started in user unlocked state long interactionEventTime; // The time we sent the last interaction event long fgInteractionTime; // When we became foreground for interaction purposes String waitingToKill; // Process is waiting to be killed when in the bg, and reason diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 5baba524aab4..c59591e9920e 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -24,6 +24,7 @@ import static android.app.ActivityManager.USER_OP_IS_CURRENT; import static android.app.ActivityManager.USER_OP_SUCCESS; import static android.content.Context.KEYGUARD_SERVICE; import static android.os.Process.SYSTEM_UID; + import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -37,6 +38,10 @@ import static com.android.server.am.ActivityManagerService.SYSTEM_USER_CURRENT_M import static com.android.server.am.ActivityManagerService.SYSTEM_USER_START_MSG; import static com.android.server.am.ActivityManagerService.SYSTEM_USER_UNLOCK_MSG; import static com.android.server.am.ActivityManagerService.USER_SWITCH_TIMEOUT_MSG; +import static com.android.server.am.UserState.STATE_BOOTING; +import static com.android.server.am.UserState.STATE_RUNNING_LOCKED; +import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKED; +import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKING; import android.annotation.NonNull; import android.annotation.UserIdInt; @@ -53,6 +58,7 @@ import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.os.BatteryStats; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.Debug; import android.os.Handler; @@ -77,6 +83,7 @@ import android.util.SparseIntArray; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.ProgressReporter; import com.android.internal.widget.LockPatternUtils; import com.android.server.LocalServices; import com.android.server.pm.UserManagerService; @@ -86,6 +93,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; /** @@ -219,9 +227,7 @@ final class UserController { // consistent developer events. We step into RUNNING_LOCKED here, // but we might immediately step into RUNNING below if the user // storage is already unlocked. - if (uss.state == UserState.STATE_BOOTING) { - uss.setState(UserState.STATE_RUNNING_LOCKED); - + if (uss.setState(STATE_BOOTING, STATE_RUNNING_LOCKED)) { Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null); intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT @@ -236,11 +242,10 @@ final class UserController { } /** - * Consider stepping from {@link UserState#STATE_RUNNING_LOCKED} into - * {@link UserState#STATE_RUNNING}, which only occurs if the user storage is - * actually unlocked. + * Step from {@link UserState#STATE_RUNNING_LOCKED} to + * {@link UserState#STATE_RUNNING_UNLOCKING}. */ - void finishUserUnlock(UserState uss) { + void finishUserUnlocking(final UserState uss, final ProgressReporter progress) { final int userId = uss.mHandle.getIdentifier(); synchronized (mService) { // Bail if we ended up with a stale user @@ -249,14 +254,58 @@ final class UserController { // Only keep marching forward if user is actually unlocked if (!isUserKeyUnlocked(userId)) return; - if (uss.state == UserState.STATE_RUNNING_LOCKED) { - uss.setState(UserState.STATE_RUNNING); - - // Give user manager a chance to prepare app storage + if (uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) { + // Prepare app storage before we go any further + progress.setProgress(5, mService.mContext.getString(R.string.android_start_title)); mUserManager.onBeforeUnlockUser(userId); + progress.setProgress(20); + + // Send PRE_BOOT broadcasts if fingerprint changed + final UserInfo info = getUserInfo(userId); + if (!Objects.equals(info.lastLoggedInFingerprint, Build.FINGERPRINT)) { + progress.startSegment(80); + new PreBootBroadcaster(mService, userId, progress) { + @Override + public void onFinished() { + finishUserUnlocked(uss, progress); + } + }.sendNext(); + } else { + finishUserUnlocked(uss, progress); + } + } + } + } + /** + * Step from {@link UserState#STATE_RUNNING_UNLOCKING} to + * {@link UserState#STATE_RUNNING_UNLOCKED}. + */ + void finishUserUnlocked(UserState uss, ProgressReporter progress) { + try { + finishUserUnlockedInternal(uss); + } finally { + progress.finish(); + } + } + + void finishUserUnlockedInternal(UserState uss) { + final int userId = uss.mHandle.getIdentifier(); + synchronized (mService) { + // Bail if we ended up with a stale user + if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return; + + // Only keep marching forward if user is actually unlocked + if (!isUserKeyUnlocked(userId)) return; + + if (uss.setState(STATE_RUNNING_UNLOCKING, STATE_RUNNING_UNLOCKED)) { + // Remember that we logged in + mUserManager.onUserLoggedIn(userId); + + // Dispatch unlocked to system services mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0)); + // Dispatch unlocked to external apps final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED); unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); unlockedIntent.addFlags( @@ -769,7 +818,7 @@ final class UserController { return result; } - boolean unlockUser(final int userId, byte[] token, byte[] secret) { + boolean unlockUser(final int userId, byte[] token, byte[] secret, ProgressReporter progress) { if (mService.checkCallingPermission(INTERACT_ACROSS_USERS_FULL) != PackageManager.PERMISSION_GRANTED) { String msg = "Permission Denial: unlockUser() from pid=" @@ -782,7 +831,7 @@ final class UserController { final long binderToken = Binder.clearCallingIdentity(); try { - return unlockUserCleared(userId, token, secret); + return unlockUserCleared(userId, token, secret, progress); } finally { Binder.restoreCallingIdentity(binderToken); } @@ -796,14 +845,20 @@ final class UserController { */ boolean maybeUnlockUser(final int userId) { // Try unlocking storage using empty token - return unlockUserCleared(userId, null, null); + return unlockUserCleared(userId, null, null, ProgressReporter.NO_OP); } - boolean unlockUserCleared(final int userId, byte[] token, byte[] secret) { + boolean unlockUserCleared(final int userId, byte[] token, byte[] secret, + ProgressReporter progress) { synchronized (mService) { // Bail if already running unlocked final UserState uss = mStartedUsers.get(userId); - if (uss.state == UserState.STATE_RUNNING) return true; + switch (uss.state) { + case STATE_RUNNING_UNLOCKING: + case STATE_RUNNING_UNLOCKED: + progress.finish(); + return true; + } } if (!isUserKeyUnlocked(userId)) { @@ -813,13 +868,14 @@ final class UserController { mountService.unlockUserKey(userId, userInfo.serialNumber, token, secret); } catch (RemoteException | RuntimeException e) { Slog.w(TAG, "Failed to unlock: " + e.getMessage()); + progress.finish(); return false; } } synchronized (mService) { final UserState uss = mStartedUsers.get(userId); - finishUserUnlock(uss); + finishUserUnlocking(uss, progress); } return true; @@ -971,7 +1027,6 @@ final class UserController { mService.mStackSupervisor.resumeFocusedStackTopActivityLocked(); } EventLogTags.writeAmSwitchUser(newUserId); - getUserManager().onUserForeground(newUserId); sendUserSwitchBroadcastsLocked(oldUserId, newUserId); } @@ -1219,7 +1274,8 @@ final class UserController { unlocked = false; break; - case UserState.STATE_RUNNING: + case UserState.STATE_RUNNING_UNLOCKING: + case UserState.STATE_RUNNING_UNLOCKED: unlocked = true; break; } diff --git a/services/core/java/com/android/server/am/UserState.java b/services/core/java/com/android/server/am/UserState.java index 7b18a1783aae..6e2342b2556b 100644 --- a/services/core/java/com/android/server/am/UserState.java +++ b/services/core/java/com/android/server/am/UserState.java @@ -33,14 +33,16 @@ public final class UserState { // User is first coming up. public final static int STATE_BOOTING = 0; - // User is in the locked running state. + // User is in the locked state. public final static int STATE_RUNNING_LOCKED = 1; - // User is in the normal running state. - public final static int STATE_RUNNING = 2; + // User is in the unlocking state. + public final static int STATE_RUNNING_UNLOCKING = 2; + // User is in the running state. + public final static int STATE_RUNNING_UNLOCKED = 3; // User is in the initial process of being stopped. - public final static int STATE_STOPPING = 3; + public final static int STATE_STOPPING = 4; // User is in the final phase of stopping, sending Intent.ACTION_SHUTDOWN. - public final static int STATE_SHUTDOWN = 4; + public final static int STATE_SHUTDOWN = 5; public final UserHandle mHandle; public final ArrayList<IStopUserCallback> mStopCallbacks @@ -61,6 +63,17 @@ public final class UserState { mHandle = handle; } + public boolean setState(int oldState, int newState) { + if (state == oldState) { + setState(newState); + return true; + } else { + Slog.w(TAG, "Expected user " + mHandle.getIdentifier() + " in state " + + stateToString(oldState) + " but was in state " + stateToString(state)); + return false; + } + } + public void setState(int newState) { if (DEBUG_MU) { Slog.i(TAG, "User " + mHandle.getIdentifier() + " state changed from " @@ -74,7 +87,8 @@ public final class UserState { switch (state) { case STATE_BOOTING: return "BOOTING"; case STATE_RUNNING_LOCKED: return "RUNNING_LOCKED"; - case STATE_RUNNING: return "RUNNING"; + case STATE_RUNNING_UNLOCKING: return "RUNNING_UNLOCKING"; + case STATE_RUNNING_UNLOCKED: return "RUNNING_UNLOCKED"; case STATE_STOPPING: return "STOPPING"; case STATE_SHUTDOWN: return "SHUTDOWN"; default: return Integer.toString(state); @@ -84,7 +98,6 @@ public final class UserState { void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("state="); pw.print(stateToString(state)); - pw.print(" lastState="); pw.print(stateToString(lastState)); if (switching) pw.print(" SWITCHING"); if (initializing) pw.print(" INITIALIZING"); pw.println(); diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index ac19e24ba010..5263c3707929 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -20,6 +20,7 @@ package com.android.server.pm; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerInternal; @@ -37,6 +38,7 @@ import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Bitmap; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.Debug; import android.os.Environment; @@ -122,6 +124,7 @@ public class UserManagerService extends IUserManager.Stub { private static final String ATTR_ID = "id"; private static final String ATTR_CREATION_TIME = "created"; private static final String ATTR_LAST_LOGGED_IN_TIME = "lastLoggedIn"; + private static final String ATTR_LAST_LOGGED_IN_FINGERPRINT = "lastLoggedInFingerprint"; private static final String ATTR_SERIAL_NO = "serialNumber"; private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber"; private static final String ATTR_PARTIAL = "partial"; @@ -380,8 +383,6 @@ public class UserManagerService extends IUserManager.Stub { removeUserState(ui.id); } - onUserForeground(UserHandle.USER_SYSTEM); - mAppOpsService = IAppOpsService.Stub.asInterface( ServiceManager.getService(Context.APP_OPS_SERVICE)); @@ -1541,6 +1542,8 @@ public class UserManagerService extends IUserManager.Stub { serializer.attribute(null, ATTR_CREATION_TIME, Long.toString(userInfo.creationTime)); serializer.attribute(null, ATTR_LAST_LOGGED_IN_TIME, Long.toString(userInfo.lastLoggedInTime)); + serializer.attribute(null, ATTR_LAST_LOGGED_IN_FINGERPRINT, + userInfo.lastLoggedInFingerprint); if (userInfo.iconPath != null) { serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath); } @@ -1671,6 +1674,7 @@ public class UserManagerService extends IUserManager.Stub { String iconPath = null; long creationTime = 0L; long lastLoggedInTime = 0L; + String lastLoggedInFingerprint = null; int profileGroupId = UserInfo.NO_PROFILE_GROUP_ID; int restrictedProfileParentId = UserInfo.NO_PROFILE_GROUP_ID; boolean partial = false; @@ -1711,6 +1715,8 @@ public class UserManagerService extends IUserManager.Stub { iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH); creationTime = readLongAttribute(parser, ATTR_CREATION_TIME, 0); lastLoggedInTime = readLongAttribute(parser, ATTR_LAST_LOGGED_IN_TIME, 0); + lastLoggedInFingerprint = parser.getAttributeValue(null, + ATTR_LAST_LOGGED_IN_FINGERPRINT); profileGroupId = readIntAttribute(parser, ATTR_PROFILE_GROUP_ID, UserInfo.NO_PROFILE_GROUP_ID); restrictedProfileParentId = readIntAttribute(parser, @@ -1763,6 +1769,7 @@ public class UserManagerService extends IUserManager.Stub { userInfo.serialNumber = serialNumber; userInfo.creationTime = creationTime; userInfo.lastLoggedInTime = lastLoggedInTime; + userInfo.lastLoggedInFingerprint = lastLoggedInFingerprint; userInfo.partial = partial; userInfo.guestToRemove = guestToRemove; userInfo.profileGroupId = profileGroupId; @@ -2553,7 +2560,7 @@ public class UserManagerService extends IUserManager.Stub { * Called right before a user is unlocked. This gives us a chance to prepare * app storage. */ - public void onBeforeUnlockUser(int userId) { + public void onBeforeUnlockUser(@UserIdInt int userId) { final int userSerial = getUserSerialNumber(userId); prepareUserStorage(userId, userSerial, StorageManager.FLAG_STORAGE_CE); mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE); @@ -2563,17 +2570,19 @@ public class UserManagerService extends IUserManager.Stub { * Make a note of the last started time of a user and do some cleanup. * @param userId the user that was just foregrounded */ - public void onUserForeground(int userId) { + public void onUserLoggedIn(@UserIdInt int userId) { UserData userData = getUserDataNoChecks(userId); if (userData == null || userData.info.partial) { Slog.w(LOG_TAG, "userForeground: unknown user #" + userId); return; } - long now = System.currentTimeMillis(); + + final long now = System.currentTimeMillis(); if (now > EPOCH_PLUS_30_YEARS) { userData.info.lastLoggedInTime = now; - scheduleWriteUser(userData); } + userData.info.lastLoggedInFingerprint = Build.FINGERPRINT; + scheduleWriteUser(userData); } /** @@ -2838,6 +2847,8 @@ public class UserManagerService extends IUserManager.Stub { sb.append(" ago"); pw.println(sb); } + pw.print(" Last logged in fingerprint: "); + pw.println(userInfo.lastLoggedInFingerprint); pw.print(" Has profile owner: "); pw.println(mIsUserManaged.get(userId)); pw.println(" Restrictions:"); |