diff options
author | Justin Klaassen <justinklaassen@google.com> | 2017-10-10 15:20:13 -0400 |
---|---|---|
committer | Justin Klaassen <justinklaassen@google.com> | 2017-10-10 15:20:13 -0400 |
commit | 93b7ee4fce01df52a6607f0b1965cbafdfeaf1a6 (patch) | |
tree | 49f76f879a89c256a4f65b674086be50760bdffb /com/android | |
parent | bc81c7ada5aab3806dd0b17498f5c9672c9b33c4 (diff) | |
download | android-28-93b7ee4fce01df52a6607f0b1965cbafdfeaf1a6.tar.gz |
Import Android SDK Platform P [4386628]
/google/data/ro/projects/android/fetch_artifact \
--bid 4386628 \
--target sdk_phone_armv7-win_sdk \
sdk-repo-linux-sources-4386628.zip
AndroidVersion.ApiLevel has been modified to appear as 28
Change-Id: I9b8400ac92116cae4f033d173f7a5682b26ccba9
Diffstat (limited to 'com/android')
355 files changed, 18176 insertions, 14024 deletions
diff --git a/com/android/commands/pm/Pm.java b/com/android/commands/pm/Pm.java index ad989dee..c5c38f53 100644 --- a/com/android/commands/pm/Pm.java +++ b/com/android/commands/pm/Pm.java @@ -416,7 +416,7 @@ public final class Pm { PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null, null, null); params.sessionParams.setSize( - PackageHelper.calculateInstalledSize(pkgLite, false, + PackageHelper.calculateInstalledSize(pkgLite, params.sessionParams.abiOverride)); } catch (PackageParserException | IOException e) { System.err.println("Error: Failed to parse APK file: " + e); @@ -636,7 +636,7 @@ public final class Pm { out = session.openWrite(splitName, 0, sizeBytes); int total = 0; - byte[] buffer = new byte[65536]; + byte[] buffer = new byte[1024 * 1024]; int c; while ((c = in.read(buffer)) != -1) { total += c; diff --git a/com/android/defcontainer/DefaultContainerService.java b/com/android/defcontainer/DefaultContainerService.java index 3800e6f7..4a771ebd 100644 --- a/com/android/defcontainer/DefaultContainerService.java +++ b/com/android/defcontainer/DefaultContainerService.java @@ -16,8 +16,6 @@ package com.android.defcontainer; -import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME; - import android.app.IntentService; import android.content.Context; import android.content.Intent; @@ -31,21 +29,15 @@ import android.content.pm.PackageParser.PackageParserException; import android.content.res.ObbInfo; import android.content.res.ObbScanner; import android.os.Binder; -import android.os.Environment; import android.os.Environment.UserEnvironment; -import android.os.FileUtils; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; -import android.system.ErrnoException; -import android.system.Os; -import android.system.StructStatVfs; import android.util.Slog; import com.android.internal.app.IMediaContainerService; -import com.android.internal.content.NativeLibraryHelper; import com.android.internal.content.PackageHelper; import com.android.internal.os.IParcelFileDescriptorFactory; import com.android.internal.util.ArrayUtils; @@ -72,51 +64,6 @@ public class DefaultContainerService extends IntentService { private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() { /** - * Creates a new container and copies package there. - * - * @param packagePath absolute path to the package to be copied. Can be - * a single monolithic APK file or a cluster directory - * containing one or more APKs. - * @param containerId the id of the secure container that should be used - * for creating a secure container into which the resource - * will be copied. - * @param key Refers to key used for encrypting the secure container - * @return Returns the new cache path where the resource has been copied - * into - */ - @Override - public String copyPackageToContainer(String packagePath, String containerId, String key, - boolean isExternal, boolean isForwardLocked, String abiOverride) { - if (packagePath == null || containerId == null) { - return null; - } - - if (isExternal) { - // Make sure the sdcard is mounted. - String status = Environment.getExternalStorageState(); - if (!status.equals(Environment.MEDIA_MOUNTED)) { - Slog.w(TAG, "Make sure sdcard is mounted."); - return null; - } - } - - PackageLite pkg = null; - NativeLibraryHelper.Handle handle = null; - try { - final File packageFile = new File(packagePath); - pkg = PackageParser.parsePackageLite(packageFile, 0); - handle = NativeLibraryHelper.Handle.create(pkg); - return copyPackageToContainerInner(pkg, handle, containerId, key, isExternal, - isForwardLocked, abiOverride); - } catch (PackageParserException | IOException e) { - Slog.w(TAG, "Failed to copy package at " + packagePath, e); - return null; - } finally { - IoUtils.closeQuietly(handle); - } - } - - /** * Copy package to the target location. * * @param packagePath absolute path to the package to be copied. Can be @@ -153,7 +100,6 @@ public class DefaultContainerService extends IntentService { public PackageInfoLite getMinimalPackageInfo(String packagePath, int flags, String abiOverride) { final Context context = DefaultContainerService.this; - final boolean isForwardLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0; PackageInfoLite ret = new PackageInfoLite(); if (packagePath == null) { @@ -167,7 +113,7 @@ public class DefaultContainerService extends IntentService { final long sizeBytes; try { pkg = PackageParser.parsePackageLite(packageFile, 0); - sizeBytes = PackageHelper.calculateInstalledSize(pkg, isForwardLocked, abiOverride); + sizeBytes = PackageHelper.calculateInstalledSize(pkg, abiOverride); } catch (PackageParserException | IOException e) { Slog.w(TAG, "Failed to parse package at " + packagePath + ": " + e); @@ -230,13 +176,13 @@ public class DefaultContainerService extends IntentService { * containing one or more APKs. */ @Override - public long calculateInstalledSize(String packagePath, boolean isForwardLocked, - String abiOverride) throws RemoteException { + public long calculateInstalledSize(String packagePath, String abiOverride) + throws RemoteException { final File packageFile = new File(packagePath); final PackageParser.PackageLite pkg; try { pkg = PackageParser.parsePackageLite(packageFile, 0); - return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, abiOverride); + return PackageHelper.calculateInstalledSize(pkg, abiOverride); } catch (PackageParserException | IOException e) { Slog.w(TAG, "Failed to calculate installed size: " + e); return Long.MAX_VALUE; @@ -292,60 +238,6 @@ public class DefaultContainerService extends IntentService { return mBinder; } - private String copyPackageToContainerInner(PackageLite pkg, NativeLibraryHelper.Handle handle, - String newCid, String key, boolean isExternal, boolean isForwardLocked, - String abiOverride) throws IOException { - - // Calculate container size, rounding up to nearest MB and adding an - // extra MB for filesystem overhead - final long sizeBytes = PackageHelper.calculateInstalledSize(pkg, handle, - isForwardLocked, abiOverride); - - // Create new container - final String newMountPath = PackageHelper.createSdDir(sizeBytes, newCid, key, - Process.myUid(), isExternal); - if (newMountPath == null) { - throw new IOException("Failed to create container " + newCid); - } - final File targetDir = new File(newMountPath); - - try { - // Copy all APKs - copyFile(pkg.baseCodePath, targetDir, "base.apk", isForwardLocked); - if (!ArrayUtils.isEmpty(pkg.splitNames)) { - for (int i = 0; i < pkg.splitNames.length; i++) { - copyFile(pkg.splitCodePaths[i], targetDir, - "split_" + pkg.splitNames[i] + ".apk", isForwardLocked); - } - } - - // Extract native code - final File libraryRoot = new File(targetDir, LIB_DIR_NAME); - final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot, - abiOverride); - if (res != PackageManager.INSTALL_SUCCEEDED) { - throw new IOException("Failed to extract native code, res=" + res); - } - - if (!PackageHelper.finalizeSdDir(newCid)) { - throw new IOException("Failed to finalize " + newCid); - } - - if (PackageHelper.isContainerMounted(newCid)) { - PackageHelper.unMountSdDir(newCid); - } - - } catch (ErrnoException e) { - PackageHelper.destroySdDir(newCid); - throw e.rethrowAsIOException(); - } catch (IOException e) { - PackageHelper.destroySdDir(newCid); - throw e; - } - - return newMountPath; - } - private int copyPackageInner(PackageLite pkg, IParcelFileDescriptorFactory target) throws IOException, RemoteException { copyFile(pkg.baseCodePath, target, "base.apk"); @@ -373,28 +265,4 @@ public class DefaultContainerService extends IntentService { IoUtils.closeQuietly(in); } } - - private void copyFile(String sourcePath, File targetDir, String targetName, - boolean isForwardLocked) throws IOException, ErrnoException { - final File sourceFile = new File(sourcePath); - final File targetFile = new File(targetDir, targetName); - - Slog.d(TAG, "Copying " + sourceFile + " to " + targetFile); - if (!FileUtils.copyFile(sourceFile, targetFile)) { - throw new IOException("Failed to copy " + sourceFile + " to " + targetFile); - } - - if (isForwardLocked) { - final String publicTargetName = PackageHelper.replaceEnd(targetName, - ".apk", ".zip"); - final File publicTargetFile = new File(targetDir, publicTargetName); - - PackageHelper.extractPublicFiles(sourceFile, publicTargetFile); - - Os.chmod(targetFile.getAbsolutePath(), 0640); - Os.chmod(publicTargetFile.getAbsolutePath(), 0644); - } else { - Os.chmod(targetFile.getAbsolutePath(), 0644); - } - } } diff --git a/com/android/ims/ImsCallProfile.java b/com/android/ims/ImsCallProfile.java index 36abfc9d..489c208a 100644 --- a/com/android/ims/ImsCallProfile.java +++ b/com/android/ims/ImsCallProfile.java @@ -19,7 +19,9 @@ package com.android.ims; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.os.PersistableBundle; import android.telecom.VideoProfile; +import android.util.Log; import com.android.internal.telephony.PhoneConstants; @@ -216,6 +218,29 @@ public class ImsCallProfile implements Parcelable { public int mServiceType; public int mCallType; public int mRestrictCause = CALL_RESTRICT_CAUSE_NONE; + + /** + * Extras associated with this {@link ImsCallProfile}. + * <p> + * Valid data types include: + * <ul> + * <li>{@link Integer} (and int)</li> + * <li>{@link Long} (and long)</li> + * <li>{@link Double} (and double)</li> + * <li>{@link String}</li> + * <li>{@code int[]}</li> + * <li>{@code long[]}</li> + * <li>{@code double[]}</li> + * <li>{@code String[]}</li> + * <li>{@link PersistableBundle}</li> + * <li>{@link Boolean} (and boolean)</li> + * <li>{@code boolean[]}</li> + * <li>Other {@link Parcelable} classes in the {@code android.*} namespace.</li> + * </ul> + * <p> + * Invalid types will be removed when the {@link ImsCallProfile} is parceled for transmit across + * a {@link android.os.Binder}. + */ public Bundle mCallExtras; public ImsStreamMediaProfile mMediaProfile; @@ -315,16 +340,17 @@ public class ImsCallProfile implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { + Bundle filteredExtras = maybeCleanseExtras(mCallExtras); out.writeInt(mServiceType); out.writeInt(mCallType); - out.writeParcelable(mCallExtras, 0); + out.writeBundle(filteredExtras); out.writeParcelable(mMediaProfile, 0); } private void readFromParcel(Parcel in) { mServiceType = in.readInt(); mCallType = in.readInt(); - mCallExtras = in.readParcelable(null); + mCallExtras = in.readBundle(); mMediaProfile = in.readParcelable(null); } @@ -465,6 +491,31 @@ public class ImsCallProfile implements Parcelable { } /** + * Cleanses a {@link Bundle} to ensure that it contains only data of type: + * 1. Primitive data types (e.g. int, bool, and other values determined by + * {@link android.os.PersistableBundle#isValidType(Object)}). + * 2. Other Bundles. + * 3. {@link Parcelable} objects in the {@code android.*} namespace. + * @param extras the source {@link Bundle} + * @return where all elements are valid types the source {@link Bundle} is returned unmodified, + * otherwise a copy of the {@link Bundle} with the invalid elements is returned. + */ + private Bundle maybeCleanseExtras(Bundle extras) { + if (extras == null) { + return null; + } + + int startSize = extras.size(); + Bundle filtered = extras.filterValues(); + int endSize = filtered.size(); + if (startSize != endSize) { + Log.i(TAG, "maybeCleanseExtras: " + (startSize - endSize) + " extra values were " + + "removed - only primitive types and system parcelables are permitted."); + } + return filtered; + } + + /** * Determines if a video state is set in a video state bit-mask. * * @param videoState The video state bit mask. diff --git a/com/android/internal/alsa/AlsaDevicesParser.java b/com/android/internal/alsa/AlsaDevicesParser.java index 7cdd8970..6e3d5966 100644 --- a/com/android/internal/alsa/AlsaDevicesParser.java +++ b/com/android/internal/alsa/AlsaDevicesParser.java @@ -258,7 +258,7 @@ public class AlsaDevicesParser { return line.charAt(kIndex_CardDeviceField) == '['; } - public void scan() { + public boolean scan() { mDeviceRecords.clear(); File devicesFile = new File(kDevicesFilePath); @@ -274,11 +274,13 @@ public class AlsaDevicesParser { } } reader.close(); + return true; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } + return false; } // diff --git a/com/android/internal/app/ChooserActivity.java b/com/android/internal/app/ChooserActivity.java index 2cab009f..6e0ba341 100644 --- a/com/android/internal/app/ChooserActivity.java +++ b/com/android/internal/app/ChooserActivity.java @@ -100,7 +100,7 @@ public class ChooserActivity extends ResolverActivity { private static final boolean DEBUG = false; private static final int QUERY_TARGET_SERVICE_LIMIT = 5; - private static final int WATCHDOG_TIMEOUT_MILLIS = 5000; + private static final int WATCHDOG_TIMEOUT_MILLIS = 2000; private Bundle mReplacementExtras; private IntentSender mChosenComponentSender; @@ -1450,11 +1450,16 @@ public class ChooserActivity extends ResolverActivity { getFirstRowPosition(rowPosition + 1)); int serviceSpacing = holder.row.getContext().getResources() .getDimensionPixelSize(R.dimen.chooser_service_spacing); - int top = rowPosition == 0 ? serviceSpacing : 0; - if (nextStartType != ChooserListAdapter.TARGET_SERVICE) { - setVertPadding(holder, top, serviceSpacing); + if (rowPosition == 0 && nextStartType != ChooserListAdapter.TARGET_SERVICE) { + // if the row is the only row for target service + setVertPadding(holder, 0, 0); } else { - setVertPadding(holder, top, 0); + int top = rowPosition == 0 ? serviceSpacing : 0; + if (nextStartType != ChooserListAdapter.TARGET_SERVICE) { + setVertPadding(holder, top, serviceSpacing); + } else { + setVertPadding(holder, top, 0); + } } } else { holder.row.setBackgroundColor(Color.TRANSPARENT); @@ -1580,8 +1585,8 @@ public class ChooserActivity extends ResolverActivity { } catch (RemoteException e) { Log.e(TAG, "Querying ChooserTargetService " + name + " failed.", e); mChooserActivity.unbindService(this); - destroy(); mChooserActivity.mServiceConnections.remove(this); + destroy(); } } } @@ -1597,7 +1602,6 @@ public class ChooserActivity extends ResolverActivity { } mChooserActivity.unbindService(this); - destroy(); mChooserActivity.mServiceConnections.remove(this); if (mChooserActivity.mServiceConnections.isEmpty()) { mChooserActivity.mChooserHandler.removeMessages( @@ -1605,6 +1609,7 @@ public class ChooserActivity extends ResolverActivity { mChooserActivity.sendVoiceChoicesIfNeeded(); } mConnectedComponent = null; + destroy(); } } diff --git a/com/android/internal/app/NightDisplayController.java b/com/android/internal/app/NightDisplayController.java index 860c5c4c..7a1383c7 100644 --- a/com/android/internal/app/NightDisplayController.java +++ b/com/android/internal/app/NightDisplayController.java @@ -32,8 +32,12 @@ import com.android.internal.R; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Calendar; -import java.util.Locale; +import java.time.DateTimeException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.format.DateTimeParseException; /** * Controller for managing Night display settings. @@ -116,8 +120,9 @@ public final class NightDisplayController { */ public boolean setActivated(boolean activated) { if (isActivated() != activated) { - Secure.putLongForUser(mContext.getContentResolver(), - Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, System.currentTimeMillis(), + Secure.putStringForUser(mContext.getContentResolver(), + Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, + LocalDateTime.now().toString(), mUserId); } return Secure.putIntForUser(mContext.getContentResolver(), @@ -128,17 +133,22 @@ public final class NightDisplayController { * Returns the time when Night display's activation state last changed, or {@code null} if it * has never been changed. */ - public Calendar getLastActivatedTime() { + public LocalDateTime getLastActivatedTime() { final ContentResolver cr = mContext.getContentResolver(); - final long lastActivatedTimeMillis = Secure.getLongForUser( - cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, -1, mUserId); - if (lastActivatedTimeMillis < 0) { - return null; + final String lastActivatedTime = Secure.getStringForUser( + cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, mUserId); + if (lastActivatedTime != null) { + try { + return LocalDateTime.parse(lastActivatedTime); + } catch (DateTimeParseException ignored) {} + // Uses the old epoch time. + try { + return LocalDateTime.ofInstant( + Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)), + ZoneId.systemDefault()); + } catch (DateTimeException|NumberFormatException ignored) {} } - - final Calendar lastActivatedTime = Calendar.getInstance(); - lastActivatedTime.setTimeInMillis(lastActivatedTimeMillis); - return lastActivatedTime; + return null; } /** @@ -183,8 +193,10 @@ public final class NightDisplayController { } if (getAutoMode() != autoMode) { - Secure.putLongForUser(mContext.getContentResolver(), - Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, -1L, mUserId); + Secure.putStringForUser(mContext.getContentResolver(), + Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, + null, + mUserId); } return Secure.putIntForUser(mContext.getContentResolver(), Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mUserId); @@ -206,7 +218,7 @@ public final class NightDisplayController { R.integer.config_defaultNightDisplayCustomStartTime); } - return LocalTime.valueOf(startTimeValue); + return LocalTime.ofSecondOfDay(startTimeValue / 1000); } /** @@ -221,7 +233,7 @@ public final class NightDisplayController { throw new IllegalArgumentException("startTime cannot be null"); } return Secure.putIntForUser(mContext.getContentResolver(), - Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, startTime.toMillis(), mUserId); + Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, startTime.toSecondOfDay() * 1000, mUserId); } /** @@ -240,7 +252,7 @@ public final class NightDisplayController { R.integer.config_defaultNightDisplayCustomEndTime); } - return LocalTime.valueOf(endTimeValue); + return LocalTime.ofSecondOfDay(endTimeValue / 1000); } /** @@ -255,7 +267,7 @@ public final class NightDisplayController { throw new IllegalArgumentException("endTime cannot be null"); } return Secure.putIntForUser(mContext.getContentResolver(), - Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.toMillis(), mUserId); + Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.toSecondOfDay() * 1000, mUserId); } /** @@ -379,106 +391,6 @@ public final class NightDisplayController { } /** - * A time without a time-zone or date. - */ - public static class LocalTime { - - /** - * The hour of the day from 0 - 23. - */ - public final int hourOfDay; - /** - * The minute within the hour from 0 - 59. - */ - public final int minute; - - public LocalTime(int hourOfDay, int minute) { - if (hourOfDay < 0 || hourOfDay > 23) { - throw new IllegalArgumentException("Invalid hourOfDay: " + hourOfDay); - } else if (minute < 0 || minute > 59) { - throw new IllegalArgumentException("Invalid minute: " + minute); - } - - this.hourOfDay = hourOfDay; - this.minute = minute; - } - - /** - * Returns the first date time corresponding to this local time that occurs before the - * provided date time. - * - * @param time the date time to compare against - * @return the prior date time corresponding to this local time - */ - public Calendar getDateTimeBefore(Calendar time) { - final Calendar c = Calendar.getInstance(); - c.set(Calendar.YEAR, time.get(Calendar.YEAR)); - c.set(Calendar.DAY_OF_YEAR, time.get(Calendar.DAY_OF_YEAR)); - - c.set(Calendar.HOUR_OF_DAY, hourOfDay); - c.set(Calendar.MINUTE, minute); - c.set(Calendar.SECOND, 0); - c.set(Calendar.MILLISECOND, 0); - - // Check if the local time has past, if so return the same time tomorrow. - if (c.after(time)) { - c.add(Calendar.DATE, -1); - } - - return c; - } - - /** - * Returns the first date time corresponding to this local time that occurs after the - * provided date time. - * - * @param time the date time to compare against - * @return the next date time corresponding to this local time - */ - public Calendar getDateTimeAfter(Calendar time) { - final Calendar c = Calendar.getInstance(); - c.set(Calendar.YEAR, time.get(Calendar.YEAR)); - c.set(Calendar.DAY_OF_YEAR, time.get(Calendar.DAY_OF_YEAR)); - - c.set(Calendar.HOUR_OF_DAY, hourOfDay); - c.set(Calendar.MINUTE, minute); - c.set(Calendar.SECOND, 0); - c.set(Calendar.MILLISECOND, 0); - - // Check if the local time has past, if so return the same time tomorrow. - if (c.before(time)) { - c.add(Calendar.DATE, 1); - } - - return c; - } - - /** - * Returns a local time corresponding the given number of milliseconds from midnight. - * - * @param millis the number of milliseconds from midnight - * @return the corresponding local time - */ - private static LocalTime valueOf(int millis) { - final int hourOfDay = (millis / 3600000) % 24; - final int minutes = (millis / 60000) % 60; - return new LocalTime(hourOfDay, minutes); - } - - /** - * Returns the local time represented as milliseconds from midnight. - */ - private int toMillis() { - return hourOfDay * 3600000 + minute * 60000; - } - - @Override - public String toString() { - return String.format(Locale.US, "%02d:%02d", hourOfDay, minute); - } - } - - /** * Callback invoked whenever the Night display settings are changed. */ public interface Callback { diff --git a/com/android/internal/app/ShutdownActivity.java b/com/android/internal/app/ShutdownActivity.java index 745d28f3..f81e8383 100644 --- a/com/android/internal/app/ShutdownActivity.java +++ b/com/android/internal/app/ShutdownActivity.java @@ -41,6 +41,9 @@ public class ShutdownActivity extends Activity { mReboot = Intent.ACTION_REBOOT.equals(intent.getAction()); mConfirm = intent.getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false); mUserRequested = intent.getBooleanExtra(Intent.EXTRA_USER_REQUESTED_SHUTDOWN, false); + final String reason = mUserRequested + ? PowerManager.SHUTDOWN_USER_REQUESTED + : intent.getStringExtra(Intent.EXTRA_REASON); Slog.i(TAG, "onCreate(): confirm=" + mConfirm); Thread thr = new Thread("ShutdownActivity") { @@ -52,9 +55,7 @@ public class ShutdownActivity extends Activity { if (mReboot) { pm.reboot(mConfirm, null, false); } else { - pm.shutdown(mConfirm, - mUserRequested ? PowerManager.SHUTDOWN_USER_REQUESTED : null, - false); + pm.shutdown(mConfirm, reason, false); } } catch (RemoteException e) { } diff --git a/com/android/internal/app/procstats/DumpUtils.java b/com/android/internal/app/procstats/DumpUtils.java index ebedc89c..0bc8c483 100644 --- a/com/android/internal/app/procstats/DumpUtils.java +++ b/com/android/internal/app/procstats/DumpUtils.java @@ -29,6 +29,7 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; import static com.android.internal.app.procstats.ProcessStats.*; @@ -66,6 +67,8 @@ public final class DumpUtils { "cch-activity", "cch-aclient", "cch-empty" }; + // State enum is defined in frameworks/base/core/proto/android/service/procstats.proto + // Update states must sync enum definition as well, the ordering must not be changed. static final String[] ADJ_SCREEN_TAGS = new String[] { "0", "1" }; @@ -177,6 +180,13 @@ public final class DumpUtils { printArrayEntry(pw, STATE_TAGS, state, 1); } + public static void printProcStateTagProto(ProtoOutputStream proto, long screenId, long memId, + long stateId, int state) { + state = printProto(proto, screenId, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD * STATE_COUNT); + state = printProto(proto, memId, ADJ_MEM_TAGS, state, STATE_COUNT); + printProto(proto, stateId, STATE_TAGS, state, 1); + } + public static void printAdjTag(PrintWriter pw, int state) { state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD); printArrayEntry(pw, ADJ_MEM_TAGS, state, 1); @@ -352,6 +362,15 @@ public final class DumpUtils { return value - index*mod; } + public static int printProto(ProtoOutputStream proto, long fieldId, String[] array, int value, int mod) { + int index = value/mod; + if (index >= 0 && index < array.length) { + // Valid state enum number starts at 1, 0 stands for unknown. + proto.write(fieldId, index + 1); + } // else enum default is always zero in proto3 + return value - index*mod; + } + public static String collapseString(String pkgName, String itemName) { if (itemName.startsWith(pkgName)) { final int ITEMLEN = itemName.length(); diff --git a/com/android/internal/app/procstats/ProcessState.java b/com/android/internal/app/procstats/ProcessState.java index e0a40536..7519fce4 100644 --- a/com/android/internal/app/procstats/ProcessState.java +++ b/com/android/internal/app/procstats/ProcessState.java @@ -21,6 +21,8 @@ import android.os.Parcelable; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; +import android.service.pm.PackageProto; +import android.service.procstats.ProcessStatsProto; import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.ArraySet; @@ -29,6 +31,8 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; +import android.util.proto.ProtoUtils; import com.android.internal.app.procstats.ProcessStats; import com.android.internal.app.procstats.ProcessStats.PackageState; @@ -69,6 +73,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Objects; public final class ProcessState { @@ -1157,6 +1164,7 @@ public final class ProcessState { } } + @Override public String toString() { StringBuilder sb = new StringBuilder(128); sb.append("ProcessState{").append(Integer.toHexString(System.identityHashCode(this))) @@ -1167,4 +1175,76 @@ public final class ProcessState { sb.append("}"); return sb.toString(); } + + public void toProto(ProtoOutputStream proto, String procName, int uid, long now) { + proto.write(ProcessStatsProto.PROCESS, procName); + proto.write(ProcessStatsProto.UID, uid); + if (mNumExcessiveCpu > 0 || mNumCachedKill > 0 ) { + final long killToken = proto.start(ProcessStatsProto.KILL); + proto.write(ProcessStatsProto.Kill.CPU, mNumExcessiveCpu); + proto.write(ProcessStatsProto.Kill.CACHED, mNumCachedKill); + ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.Kill.CACHED_PSS, + mMinCachedKillPss, mAvgCachedKillPss, mMaxCachedKillPss); + proto.end(killToken); + } + + // Group proc stats by type (screen state + mem state + process state) + Map<Integer, Long> durationByState = new HashMap<>(); + boolean didCurState = false; + for (int i=0; i<mDurations.getKeyCount(); i++) { + final int key = mDurations.getKeyAt(i); + final int type = SparseMappingTable.getIdFromKey(key); + long time = mDurations.getValue(key); + if (mCurState == type) { + didCurState = true; + time += now - mStartTime; + } + durationByState.put(type, time); + } + if (!didCurState && mCurState != STATE_NOTHING) { + durationByState.put(mCurState, now - mStartTime); + } + + for (int i=0; i<mPssTable.getKeyCount(); i++) { + final int key = mPssTable.getKeyAt(i); + final int type = SparseMappingTable.getIdFromKey(key); + if (!durationByState.containsKey(type)) { + // state without duration should not have stats! + continue; + } + final long stateToken = proto.start(ProcessStatsProto.STATES); + DumpUtils.printProcStateTagProto(proto, + ProcessStatsProto.State.SCREEN_STATE, + ProcessStatsProto.State.MEMORY_STATE, + ProcessStatsProto.State.PROCESS_STATE, + type); + + long duration = durationByState.get(type); + durationByState.remove(type); // remove the key since it is already being dumped. + proto.write(ProcessStatsProto.State.DURATION_MS, duration); + + proto.write(ProcessStatsProto.State.SAMPLE_SIZE, mPssTable.getValue(key, PSS_SAMPLE_COUNT)); + ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.PSS, + mPssTable.getValue(key, PSS_MINIMUM), + mPssTable.getValue(key, PSS_AVERAGE), + mPssTable.getValue(key, PSS_MAXIMUM)); + ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.USS, + mPssTable.getValue(key, PSS_USS_MINIMUM), + mPssTable.getValue(key, PSS_USS_AVERAGE), + mPssTable.getValue(key, PSS_USS_MAXIMUM)); + + proto.end(stateToken); + } + + for (Map.Entry<Integer, Long> entry : durationByState.entrySet()) { + final long stateToken = proto.start(ProcessStatsProto.STATES); + DumpUtils.printProcStateTagProto(proto, + ProcessStatsProto.State.SCREEN_STATE, + ProcessStatsProto.State.MEMORY_STATE, + ProcessStatsProto.State.PROCESS_STATE, + entry.getKey()); + proto.write(ProcessStatsProto.State.DURATION_MS, entry.getValue()); + proto.end(stateToken); + } + } } diff --git a/com/android/internal/app/procstats/ProcessStats.java b/com/android/internal/app/procstats/ProcessStats.java index 35b53c22..14f5e5b5 100644 --- a/com/android/internal/app/procstats/ProcessStats.java +++ b/com/android/internal/app/procstats/ProcessStats.java @@ -22,6 +22,7 @@ import android.os.Parcelable; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; +import android.service.procstats.ProcessStatsSectionProto; import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.ArraySet; @@ -30,6 +31,7 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; import com.android.internal.app.ProcessMap; import com.android.internal.app.procstats.DurationsTable; @@ -1706,6 +1708,46 @@ public final class ProcessStats implements Parcelable { } } + public void toProto(ProtoOutputStream proto, long now) { + final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap(); + + proto.write(ProcessStatsSectionProto.START_REALTIME_MS, mTimePeriodStartRealtime); + proto.write(ProcessStatsSectionProto.END_REALTIME_MS, + mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime); + proto.write(ProcessStatsSectionProto.START_UPTIME_MS, mTimePeriodStartUptime); + proto.write(ProcessStatsSectionProto.END_UPTIME_MS, mTimePeriodEndUptime); + proto.write(ProcessStatsSectionProto.RUNTIME, mRuntime); + proto.write(ProcessStatsSectionProto.HAS_SWAPPED_PSS, mHasSwappedOutPss); + boolean partial = true; + if ((mFlags&FLAG_SHUTDOWN) != 0) { + proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SHUTDOWN); + partial = false; + } + if ((mFlags&FLAG_SYSPROPS) != 0) { + proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SYSPROPS); + partial = false; + } + if ((mFlags&FLAG_COMPLETE) != 0) { + proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_COMPLETE); + partial = false; + } + if (partial) { + proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_PARTIAL); + } + + ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); + for (int ip=0; ip<procMap.size(); ip++) { + String procName = procMap.keyAt(ip); + SparseArray<ProcessState> uids = procMap.valueAt(ip); + for (int iu=0; iu<uids.size(); iu++) { + final int uid = uids.keyAt(iu); + final ProcessState procState = uids.valueAt(iu); + final long processStateToken = proto.start(ProcessStatsSectionProto.PROCESS_STATS); + procState.toProto(proto, procName, uid, now); + proto.end(processStateToken); + } + } + } final public static class ProcessStateHolder { public final int appVersion; diff --git a/com/android/internal/content/PackageHelper.java b/com/android/internal/content/PackageHelper.java index e923223d..59a7995a 100644 --- a/com/android/internal/content/PackageHelper.java +++ b/com/android/internal/content/PackageHelper.java @@ -16,7 +16,6 @@ package com.android.internal.content; -import static android.net.TrafficStats.MB_IN_BYTES; import static android.os.storage.VolumeInfo.ID_PRIVATE_INTERNAL; import android.content.Context; @@ -27,13 +26,11 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageParser.PackageLite; import android.os.Environment; -import android.os.FileUtils; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; -import android.os.storage.StorageResultCode; import android.os.storage.StorageVolume; import android.os.storage.VolumeInfo; import android.provider.Settings; @@ -45,15 +42,9 @@ import com.android.internal.annotations.VisibleForTesting; import libcore.io.IoUtils; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.util.Collections; import java.util.Objects; import java.util.UUID; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; -import java.util.zip.ZipOutputStream; /** * Constants used internally between the PackageManager @@ -72,7 +63,6 @@ public class PackageHelper { public static final int RECOMMEND_FAILED_INVALID_URI = -6; public static final int RECOMMEND_FAILED_VERSION_DOWNGRADE = -7; - private static final boolean localLOGV = false; private static final String TAG = "PackageHelper"; // App installation location settings values public static final int APP_INSTALL_AUTO = 0; @@ -91,259 +81,6 @@ public class PackageHelper { } } - public static String createSdDir(long sizeBytes, String cid, String sdEncKey, int uid, - boolean isExternal) { - // Round up to nearest MB, plus another MB for filesystem overhead - final int sizeMb = (int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES) + 1; - try { - IStorageManager storageManager = getStorageManager(); - - if (localLOGV) - Log.i(TAG, "Size of container " + sizeMb + " MB"); - - int rc = storageManager.createSecureContainer(cid, sizeMb, "ext4", sdEncKey, uid, - isExternal); - if (rc != StorageResultCode.OperationSucceeded) { - Log.e(TAG, "Failed to create secure container " + cid); - return null; - } - String cachePath = storageManager.getSecureContainerPath(cid); - if (localLOGV) Log.i(TAG, "Created secure container " + cid + - " at " + cachePath); - return cachePath; - } catch (RemoteException e) { - Log.e(TAG, "StorageManagerService running?"); - } - return null; - } - - public static boolean resizeSdDir(long sizeBytes, String cid, String sdEncKey) { - // Round up to nearest MB, plus another MB for filesystem overhead - final int sizeMb = (int) ((sizeBytes + MB_IN_BYTES) / MB_IN_BYTES) + 1; - try { - IStorageManager storageManager = getStorageManager(); - int rc = storageManager.resizeSecureContainer(cid, sizeMb, sdEncKey); - if (rc == StorageResultCode.OperationSucceeded) { - return true; - } - } catch (RemoteException e) { - Log.e(TAG, "StorageManagerService running?"); - } - Log.e(TAG, "Failed to create secure container " + cid); - return false; - } - - public static String mountSdDir(String cid, String key, int ownerUid) { - return mountSdDir(cid, key, ownerUid, true); - } - - public static String mountSdDir(String cid, String key, int ownerUid, boolean readOnly) { - try { - int rc = getStorageManager().mountSecureContainer(cid, key, ownerUid, readOnly); - if (rc != StorageResultCode.OperationSucceeded) { - Log.i(TAG, "Failed to mount container " + cid + " rc : " + rc); - return null; - } - return getStorageManager().getSecureContainerPath(cid); - } catch (RemoteException e) { - Log.e(TAG, "StorageManagerService running?"); - } - return null; - } - - public static boolean unMountSdDir(String cid) { - try { - int rc = getStorageManager().unmountSecureContainer(cid, true); - if (rc != StorageResultCode.OperationSucceeded) { - Log.e(TAG, "Failed to unmount " + cid + " with rc " + rc); - return false; - } - return true; - } catch (RemoteException e) { - Log.e(TAG, "StorageManagerService running?"); - } - return false; - } - - public static boolean renameSdDir(String oldId, String newId) { - try { - int rc = getStorageManager().renameSecureContainer(oldId, newId); - if (rc != StorageResultCode.OperationSucceeded) { - Log.e(TAG, "Failed to rename " + oldId + " to " + - newId + "with rc " + rc); - return false; - } - return true; - } catch (RemoteException e) { - Log.i(TAG, "Failed ot rename " + oldId + " to " + newId + - " with exception : " + e); - } - return false; - } - - public static String getSdDir(String cid) { - try { - return getStorageManager().getSecureContainerPath(cid); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get container path for " + cid + - " with exception " + e); - } - return null; - } - - public static String getSdFilesystem(String cid) { - try { - return getStorageManager().getSecureContainerFilesystemPath(cid); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get container path for " + cid + - " with exception " + e); - } - return null; - } - - public static boolean finalizeSdDir(String cid) { - try { - int rc = getStorageManager().finalizeSecureContainer(cid); - if (rc != StorageResultCode.OperationSucceeded) { - Log.i(TAG, "Failed to finalize container " + cid); - return false; - } - return true; - } catch (RemoteException e) { - Log.e(TAG, "Failed to finalize container " + cid + - " with exception " + e); - } - return false; - } - - public static boolean destroySdDir(String cid) { - try { - if (localLOGV) Log.i(TAG, "Forcibly destroying container " + cid); - int rc = getStorageManager().destroySecureContainer(cid, true); - if (rc != StorageResultCode.OperationSucceeded) { - Log.i(TAG, "Failed to destroy container " + cid); - return false; - } - return true; - } catch (RemoteException e) { - Log.e(TAG, "Failed to destroy container " + cid + - " with exception " + e); - } - return false; - } - - public static String[] getSecureContainerList() { - try { - return getStorageManager().getSecureContainerList(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get secure container list with exception" + - e); - } - return null; - } - - public static boolean isContainerMounted(String cid) { - try { - return getStorageManager().isSecureContainerMounted(cid); - } catch (RemoteException e) { - Log.e(TAG, "Failed to find out if container " + cid + " mounted"); - } - return false; - } - - /** - * Extract public files for the single given APK. - */ - public static long extractPublicFiles(File apkFile, File publicZipFile) - throws IOException { - final FileOutputStream fstr; - final ZipOutputStream publicZipOutStream; - - if (publicZipFile == null) { - fstr = null; - publicZipOutStream = null; - } else { - fstr = new FileOutputStream(publicZipFile); - publicZipOutStream = new ZipOutputStream(fstr); - Log.d(TAG, "Extracting " + apkFile + " to " + publicZipFile); - } - - long size = 0L; - - try { - final ZipFile privateZip = new ZipFile(apkFile.getAbsolutePath()); - try { - // Copy manifest, resources.arsc and res directory to public zip - for (final ZipEntry zipEntry : Collections.list(privateZip.entries())) { - final String zipEntryName = zipEntry.getName(); - if ("AndroidManifest.xml".equals(zipEntryName) - || "resources.arsc".equals(zipEntryName) - || zipEntryName.startsWith("res/")) { - size += zipEntry.getSize(); - if (publicZipFile != null) { - copyZipEntry(zipEntry, privateZip, publicZipOutStream); - } - } - } - } finally { - try { privateZip.close(); } catch (IOException e) {} - } - - if (publicZipFile != null) { - publicZipOutStream.finish(); - publicZipOutStream.flush(); - FileUtils.sync(fstr); - publicZipOutStream.close(); - FileUtils.setPermissions(publicZipFile.getAbsolutePath(), FileUtils.S_IRUSR - | FileUtils.S_IWUSR | FileUtils.S_IRGRP | FileUtils.S_IROTH, -1, -1); - } - } finally { - IoUtils.closeQuietly(publicZipOutStream); - } - - return size; - } - - private static void copyZipEntry(ZipEntry zipEntry, ZipFile inZipFile, - ZipOutputStream outZipStream) throws IOException { - byte[] buffer = new byte[4096]; - int num; - - ZipEntry newEntry; - if (zipEntry.getMethod() == ZipEntry.STORED) { - // Preserve the STORED method of the input entry. - newEntry = new ZipEntry(zipEntry); - } else { - // Create a new entry so that the compressed len is recomputed. - newEntry = new ZipEntry(zipEntry.getName()); - } - outZipStream.putNextEntry(newEntry); - - final InputStream data = inZipFile.getInputStream(zipEntry); - try { - while ((num = data.read(buffer)) > 0) { - outZipStream.write(buffer, 0, num); - } - outZipStream.flush(); - } finally { - IoUtils.closeQuietly(data); - } - } - - public static boolean fixSdPermissions(String cid, int gid, String filename) { - try { - int rc = getStorageManager().fixPermissionsSecureContainer(cid, gid, filename); - if (rc != StorageResultCode.OperationSucceeded) { - Log.i(TAG, "Failed to fixperms container " + cid); - return false; - } - return true; - } catch (RemoteException e) { - Log.e(TAG, "Failed to fixperms container " + cid + " with exception " + e); - } - return false; - } - /** * A group of external dependencies used in * {@link #resolveInstallVolume(Context, String, int, long)}. It can be backed by real values @@ -638,29 +375,37 @@ public class PackageHelper { return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; } + @Deprecated public static long calculateInstalledSize(PackageLite pkg, boolean isForwardLocked, String abiOverride) throws IOException { + return calculateInstalledSize(pkg, abiOverride); + } + + public static long calculateInstalledSize(PackageLite pkg, String abiOverride) + throws IOException { NativeLibraryHelper.Handle handle = null; try { handle = NativeLibraryHelper.Handle.create(pkg); - return calculateInstalledSize(pkg, handle, isForwardLocked, abiOverride); + return calculateInstalledSize(pkg, handle, abiOverride); } finally { IoUtils.closeQuietly(handle); } } + @Deprecated + public static long calculateInstalledSize(PackageLite pkg, boolean isForwardLocked, + NativeLibraryHelper.Handle handle, String abiOverride) throws IOException { + return calculateInstalledSize(pkg, handle, abiOverride); + } + public static long calculateInstalledSize(PackageLite pkg, NativeLibraryHelper.Handle handle, - boolean isForwardLocked, String abiOverride) throws IOException { + String abiOverride) throws IOException { long sizeBytes = 0; // Include raw APKs, and possibly unpacked resources for (String codePath : pkg.getAllCodePaths()) { final File codeFile = new File(codePath); sizeBytes += codeFile.length(); - - if (isForwardLocked) { - sizeBytes += PackageHelper.extractPublicFiles(codeFile, null); - } } // Include all relevant native code diff --git a/com/android/internal/os/BatteryStatsHelper.java b/com/android/internal/os/BatteryStatsHelper.java index f085e290..15dc6f50 100644 --- a/com/android/internal/os/BatteryStatsHelper.java +++ b/com/android/internal/os/BatteryStatsHelper.java @@ -143,6 +143,9 @@ public class BatteryStatsHelper { public static boolean checkWifiOnly(Context context) { ConnectivityManager cm = (ConnectivityManager) context.getSystemService( Context.CONNECTIVITY_SERVICE); + if (cm == null) { + return false; + } return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); } diff --git a/com/android/internal/os/BatteryStatsImpl.java b/com/android/internal/os/BatteryStatsImpl.java index 0bd29815..36fd991c 100644 --- a/com/android/internal/os/BatteryStatsImpl.java +++ b/com/android/internal/os/BatteryStatsImpl.java @@ -119,7 +119,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 166 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 167 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS; @@ -341,8 +341,8 @@ public class BatteryStatsImpl extends BatteryStats { protected final TimeBase mOnBatteryTimeBase = new TimeBase(); // These are the objects that will want to do something when the device - // is unplugged from power *and* the screen is off. - final TimeBase mOnBatteryScreenOffTimeBase = new TimeBase(); + // is unplugged from power *and* the screen is off or doze. + protected final TimeBase mOnBatteryScreenOffTimeBase = new TimeBase(); // Set to true when we want to distribute CPU across wakelocks for the next // CPU update, even if we aren't currently running wake locks. @@ -436,8 +436,12 @@ public class BatteryStatsImpl extends BatteryStats { public boolean mRecordAllHistory; boolean mNoAutoReset; - int mScreenState = Display.STATE_UNKNOWN; - StopwatchTimer mScreenOnTimer; + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + protected int mScreenState = Display.STATE_UNKNOWN; + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + protected StopwatchTimer mScreenOnTimer; + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + protected StopwatchTimer mScreenDozeTimer; int mScreenBrightnessBin = -1; final StopwatchTimer[] mScreenBrightnessTimer = new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS]; @@ -583,12 +587,16 @@ public class BatteryStatsImpl extends BatteryStats { int mHighDischargeAmountSinceCharge; int mDischargeScreenOnUnplugLevel; int mDischargeScreenOffUnplugLevel; + int mDischargeScreenDozeUnplugLevel; int mDischargeAmountScreenOn; int mDischargeAmountScreenOnSinceCharge; int mDischargeAmountScreenOff; int mDischargeAmountScreenOffSinceCharge; + int mDischargeAmountScreenDoze; + int mDischargeAmountScreenDozeSinceCharge; private LongSamplingCounter mDischargeScreenOffCounter; + private LongSamplingCounter mDischargeScreenDozeCounter; private LongSamplingCounter mDischargeCounter; static final int MAX_LEVEL_STEPS = 200; @@ -673,13 +681,18 @@ public class BatteryStatsImpl extends BatteryStats { } @Override - public LongCounter getDischargeScreenOffCoulombCounter() { - return mDischargeScreenOffCounter; + public long getMahDischarge(int which) { + return mDischargeCounter.getCountLocked(which); + } + + @Override + public long getMahDischargeScreenOff(int which) { + return mDischargeScreenOffCounter.getCountLocked(which); } @Override - public LongCounter getDischargeCoulombCounter() { - return mDischargeCounter; + public long getMahDischargeScreenDoze(int which) { + return mDischargeScreenDozeCounter.getCountLocked(which); } @Override @@ -3573,8 +3586,9 @@ public class BatteryStatsImpl extends BatteryStats { mActiveHistoryStates2 = 0xffffffff; } - public void updateTimeBasesLocked(boolean unplugged, boolean screenOff, long uptime, + public void updateTimeBasesLocked(boolean unplugged, int screenState, long uptime, long realtime) { + final boolean screenOff = isScreenOff(screenState) || isScreenDoze(screenState); final boolean updateOnBatteryTimeBase = unplugged != mOnBatteryTimeBase.isRunning(); final boolean updateOnBatteryScreenOffTimeBase = (unplugged && screenOff) != mOnBatteryScreenOffTimeBase.isRunning(); @@ -3591,20 +3605,22 @@ public class BatteryStatsImpl extends BatteryStats { updateRpmStatsLocked(); // if either OnBattery or OnBatteryScreenOff timebase changes. } if (DEBUG_ENERGY_CPU) { - Slog.d(TAG, "Updating cpu time because screen is now " + (screenOff ? "off" : "on") + Slog.d(TAG, "Updating cpu time because screen is now " + + Display.stateToString(screenState) + " and battery is " + (unplugged ? "on" : "off")); } updateCpuTimeLocked(); mOnBatteryTimeBase.setRunning(unplugged, uptime, realtime); - mOnBatteryScreenOffTimeBase.setRunning(unplugged && screenOff, uptime, realtime); - for (int i = mUidStats.size() - 1; i >= 0; --i) { - final Uid u = mUidStats.valueAt(i); - if (updateOnBatteryTimeBase) { - u.updateOnBatteryBgTimeBase(uptime, realtime); + if (updateOnBatteryTimeBase) { + for (int i = mUidStats.size() - 1; i >= 0; --i) { + mUidStats.valueAt(i).updateOnBatteryBgTimeBase(uptime, realtime); } - if (updateOnBatteryScreenOffTimeBase) { - u.updateOnBatteryScreenOffBgTimeBase(uptime, realtime); + } + if (updateOnBatteryScreenOffTimeBase) { + mOnBatteryScreenOffTimeBase.setRunning(unplugged && screenOff, uptime, realtime); + for (int i = mUidStats.size() - 1; i >= 0; --i) { + mUidStats.valueAt(i).updateOnBatteryScreenOffBgTimeBase(uptime, realtime); } } } @@ -3864,8 +3880,10 @@ public class BatteryStatsImpl extends BatteryStats { } public void setPretendScreenOff(boolean pretendScreenOff) { - mPretendScreenOff = pretendScreenOff; - noteScreenStateLocked(pretendScreenOff ? Display.STATE_OFF : Display.STATE_ON); + if (mPretendScreenOff != pretendScreenOff) { + mPretendScreenOff = pretendScreenOff; + noteScreenStateLocked(pretendScreenOff ? Display.STATE_OFF : Display.STATE_ON); + } } private String mInitialAcquireWakeName; @@ -4195,54 +4213,58 @@ public class BatteryStatsImpl extends BatteryStats { } } - if (state == Display.STATE_ON) { - // Screen turning on. - final long elapsedRealtime = mClocks.elapsedRealtime(); - final long uptime = mClocks.uptimeMillis(); + final long elapsedRealtime = mClocks.elapsedRealtime(); + final long uptime = mClocks.uptimeMillis(); + + boolean updateHistory = false; + if (isScreenDoze(state)) { + mHistoryCur.states |= HistoryItem.STATE_SCREEN_DOZE_FLAG; + mScreenDozeTimer.startRunningLocked(elapsedRealtime); + updateHistory = true; + } else if (isScreenDoze(oldState)) { + mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_DOZE_FLAG; + mScreenDozeTimer.stopRunningLocked(elapsedRealtime); + updateHistory = true; + } + if (isScreenOn(state)) { mHistoryCur.states |= HistoryItem.STATE_SCREEN_ON_FLAG; if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: " + Integer.toHexString(mHistoryCur.states)); - addHistoryRecordLocked(elapsedRealtime, uptime); mScreenOnTimer.startRunningLocked(elapsedRealtime); if (mScreenBrightnessBin >= 0) { mScreenBrightnessTimer[mScreenBrightnessBin].startRunningLocked(elapsedRealtime); } - - updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), false, - mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000); - - // Fake a wake lock, so we consider the device waked as long - // as the screen is on. - noteStartWakeLocked(-1, -1, "screen", null, WAKE_TYPE_PARTIAL, false, - elapsedRealtime, uptime); - - // Update discharge amounts. - if (mOnBatteryInternal) { - updateDischargeScreenLevelsLocked(false, true); - } - } else if (oldState == Display.STATE_ON) { - // Screen turning off or dozing. - final long elapsedRealtime = mClocks.elapsedRealtime(); - final long uptime = mClocks.uptimeMillis(); + updateHistory = true; + } else if (isScreenOn(oldState)) { mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_ON_FLAG; if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: " + Integer.toHexString(mHistoryCur.states)); - addHistoryRecordLocked(elapsedRealtime, uptime); mScreenOnTimer.stopRunningLocked(elapsedRealtime); if (mScreenBrightnessBin >= 0) { mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(elapsedRealtime); } - + updateHistory = true; + } + if (updateHistory) { + if (DEBUG_HISTORY) Slog.v(TAG, "Screen state to: " + + Display.stateToString(state)); + addHistoryRecordLocked(elapsedRealtime, uptime); + } + if (isScreenOn(state)) { + updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state, + mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000); + // Fake a wake lock, so we consider the device waked as long as the screen is on. + noteStartWakeLocked(-1, -1, "screen", null, WAKE_TYPE_PARTIAL, false, + elapsedRealtime, uptime); + } else if (isScreenOn(oldState)) { noteStopWakeLocked(-1, -1, "screen", "screen", WAKE_TYPE_PARTIAL, elapsedRealtime, uptime); - - updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), true, + updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state, mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000); - - // Update discharge amounts. - if (mOnBatteryInternal) { - updateDischargeScreenLevelsLocked(true, false); - } + } + // Update discharge amounts. + if (mOnBatteryInternal) { + updateDischargeScreenLevelsLocked(oldState, state); } } } @@ -5391,6 +5413,14 @@ public class BatteryStatsImpl extends BatteryStats { return mScreenOnTimer.getCountLocked(which); } + @Override public long getScreenDozeTime(long elapsedRealtimeUs, int which) { + return mScreenDozeTimer.getTotalTimeLocked(elapsedRealtimeUs, which); + } + + @Override public int getScreenDozeCount(int which) { + return mScreenDozeTimer.getCountLocked(which); + } + @Override public long getScreenBrightnessTime(int brightnessBin, long elapsedRealtimeUs, int which) { return mScreenBrightnessTimer[brightnessBin].getTotalTimeLocked( @@ -8829,6 +8859,7 @@ public class BatteryStatsImpl extends BatteryStats { mHandler = new MyHandler(handler.getLooper()); mStartCount++; mScreenOnTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase); + mScreenDozeTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase); for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) { mScreenBrightnessTimer[i] = new StopwatchTimer(mClocks, null, -100-i, null, mOnBatteryTimeBase); @@ -8887,6 +8918,7 @@ public class BatteryStatsImpl extends BatteryStats { mCameraOnTimer = new StopwatchTimer(mClocks, null, -13, null, mOnBatteryTimeBase); mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase); mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase); + mDischargeScreenDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase); mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase); mOnBattery = mOnBatteryInternal = false; long uptime = mClocks.uptimeMillis() * 1000; @@ -9430,8 +9462,16 @@ public class BatteryStatsImpl extends BatteryStats { return mCharging; } - public boolean isScreenOn() { - return mScreenState == Display.STATE_ON; + public boolean isScreenOn(int state) { + return state == Display.STATE_ON; + } + + public boolean isScreenOff(int state) { + return state == Display.STATE_OFF; + } + + public boolean isScreenDoze(int state) { + return state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND; } void initTimes(long uptime, long realtime) { @@ -9451,9 +9491,12 @@ public class BatteryStatsImpl extends BatteryStats { mDischargeAmountScreenOnSinceCharge = 0; mDischargeAmountScreenOff = 0; mDischargeAmountScreenOffSinceCharge = 0; + mDischargeAmountScreenDoze = 0; + mDischargeAmountScreenDozeSinceCharge = 0; mDischargeStepTracker.init(); mChargeStepTracker.init(); mDischargeScreenOffCounter.reset(false); + mDischargeScreenDozeCounter.reset(false); mDischargeCounter.reset(false); } @@ -9471,15 +9514,22 @@ public class BatteryStatsImpl extends BatteryStats { mOnBatteryTimeBase.reset(uptime, realtime); mOnBatteryScreenOffTimeBase.reset(uptime, realtime); if ((mHistoryCur.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) == 0) { - if (mScreenState == Display.STATE_ON) { + if (isScreenOn(mScreenState)) { mDischargeScreenOnUnplugLevel = mHistoryCur.batteryLevel; + mDischargeScreenDozeUnplugLevel = 0; + mDischargeScreenOffUnplugLevel = 0; + } else if (isScreenDoze(mScreenState)) { + mDischargeScreenOnUnplugLevel = 0; + mDischargeScreenDozeUnplugLevel = mHistoryCur.batteryLevel; mDischargeScreenOffUnplugLevel = 0; } else { mDischargeScreenOnUnplugLevel = 0; + mDischargeScreenDozeUnplugLevel = 0; mDischargeScreenOffUnplugLevel = mHistoryCur.batteryLevel; } mDischargeAmountScreenOn = 0; mDischargeAmountScreenOff = 0; + mDischargeAmountScreenDoze = 0; } initActiveHistoryEventsLocked(mSecRealtime, mSecUptime); } @@ -9490,6 +9540,7 @@ public class BatteryStatsImpl extends BatteryStats { mStartCount = 0; initTimes(uptimeMillis * 1000, elapsedRealtimeMillis * 1000); mScreenOnTimer.reset(false); + mScreenDozeTimer.reset(false); for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) { mScreenBrightnessTimer[i].reset(false); } @@ -9626,33 +9677,52 @@ public class BatteryStatsImpl extends BatteryStats { } } - void updateDischargeScreenLevelsLocked(boolean oldScreenOn, boolean newScreenOn) { - if (oldScreenOn) { + void updateDischargeScreenLevelsLocked(int oldState, int newState) { + updateOldDischargeScreenLevelLocked(oldState); + updateNewDischargeScreenLevelLocked(newState); + } + + private void updateOldDischargeScreenLevelLocked(int state) { + if (isScreenOn(state)) { int diff = mDischargeScreenOnUnplugLevel - mDischargeCurrentLevel; if (diff > 0) { mDischargeAmountScreenOn += diff; mDischargeAmountScreenOnSinceCharge += diff; } - } else { + } else if (isScreenDoze(state)) { + int diff = mDischargeScreenDozeUnplugLevel - mDischargeCurrentLevel; + if (diff > 0) { + mDischargeAmountScreenDoze += diff; + mDischargeAmountScreenDozeSinceCharge += diff; + } + } else if (isScreenOff(state)){ int diff = mDischargeScreenOffUnplugLevel - mDischargeCurrentLevel; if (diff > 0) { mDischargeAmountScreenOff += diff; mDischargeAmountScreenOffSinceCharge += diff; } } - if (newScreenOn) { + } + + private void updateNewDischargeScreenLevelLocked(int state) { + if (isScreenOn(state)) { mDischargeScreenOnUnplugLevel = mDischargeCurrentLevel; mDischargeScreenOffUnplugLevel = 0; - } else { + mDischargeScreenDozeUnplugLevel = 0; + } else if (isScreenDoze(state)){ + mDischargeScreenOnUnplugLevel = 0; + mDischargeScreenDozeUnplugLevel = mDischargeCurrentLevel; + mDischargeScreenOffUnplugLevel = 0; + } else if (isScreenOff(state)) { mDischargeScreenOnUnplugLevel = 0; + mDischargeScreenDozeUnplugLevel = 0; mDischargeScreenOffUnplugLevel = mDischargeCurrentLevel; } } public void pullPendingStateUpdatesLocked() { if (mOnBatteryInternal) { - final boolean screenOn = mScreenState == Display.STATE_ON; - updateDischargeScreenLevelsLocked(screenOn, screenOn); + updateDischargeScreenLevelsLocked(mScreenState, mScreenState); } } @@ -10785,8 +10855,8 @@ public class BatteryStatsImpl extends BatteryStats { return false; } - void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery, - final int oldStatus, final int level, final int chargeUAh) { + protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, + final boolean onBattery, final int oldStatus, final int level, final int chargeUAh) { boolean doWrite = false; Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE); m.arg1 = onBattery ? 1 : 0; @@ -10794,7 +10864,7 @@ public class BatteryStatsImpl extends BatteryStats { final long uptime = mSecUptime * 1000; final long realtime = mSecRealtime * 1000; - final boolean screenOn = mScreenState == Display.STATE_ON; + final int screenState = mScreenState; if (onBattery) { // We will reset our status if we are unplugging after the // battery was last full, or the level is at 100, or @@ -10870,16 +10940,23 @@ public class BatteryStatsImpl extends BatteryStats { } addHistoryRecordLocked(mSecRealtime, mSecUptime); mDischargeCurrentLevel = mDischargeUnplugLevel = level; - if (screenOn) { + if (isScreenOn(screenState)) { mDischargeScreenOnUnplugLevel = level; + mDischargeScreenDozeUnplugLevel = 0; + mDischargeScreenOffUnplugLevel = 0; + } else if (isScreenDoze(screenState)) { + mDischargeScreenOnUnplugLevel = 0; + mDischargeScreenDozeUnplugLevel = level; mDischargeScreenOffUnplugLevel = 0; } else { mDischargeScreenOnUnplugLevel = 0; + mDischargeScreenDozeUnplugLevel = 0; mDischargeScreenOffUnplugLevel = level; } mDischargeAmountScreenOn = 0; + mDischargeAmountScreenDoze = 0; mDischargeAmountScreenOff = 0; - updateTimeBasesLocked(true, !screenOn, uptime, realtime); + updateTimeBasesLocked(true, screenState, uptime, realtime); } else { mLastChargingStateLevel = level; mOnBattery = mOnBatteryInternal = false; @@ -10894,8 +10971,8 @@ public class BatteryStatsImpl extends BatteryStats { mLowDischargeAmountSinceCharge += mDischargeUnplugLevel-level-1; mHighDischargeAmountSinceCharge += mDischargeUnplugLevel-level; } - updateDischargeScreenLevelsLocked(screenOn, screenOn); - updateTimeBasesLocked(false, !screenOn, uptime, realtime); + updateDischargeScreenLevelsLocked(screenState, screenState); + updateTimeBasesLocked(false, screenState, uptime, realtime); mChargeStepTracker.init(); mLastChargeStepLevel = level; mMaxChargeStepLevel = level; @@ -11012,6 +11089,9 @@ public class BatteryStatsImpl extends BatteryStats { final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh; mDischargeCounter.addCountLocked(chargeDiff); mDischargeScreenOffCounter.addCountLocked(chargeDiff); + if (isScreenDoze(mScreenState)) { + mDischargeScreenDozeCounter.addCountLocked(chargeDiff); + } } mHistoryCur.batteryChargeUAh = chargeUAh; setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level, chargeUAh); @@ -11054,6 +11134,9 @@ public class BatteryStatsImpl extends BatteryStats { final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh; mDischargeCounter.addCountLocked(chargeDiff); mDischargeScreenOffCounter.addCountLocked(chargeDiff); + if (isScreenDoze(mScreenState)) { + mDischargeScreenDozeCounter.addCountLocked(chargeDiff); + } } mHistoryCur.batteryChargeUAh = chargeUAh; changed = true; @@ -11362,10 +11445,11 @@ public class BatteryStatsImpl extends BatteryStats { return dischargeAmount; } + @Override public int getDischargeAmountScreenOn() { synchronized(this) { int val = mDischargeAmountScreenOn; - if (mOnBattery && mScreenState == Display.STATE_ON + if (mOnBattery && isScreenOn(mScreenState) && mDischargeCurrentLevel < mDischargeScreenOnUnplugLevel) { val += mDischargeScreenOnUnplugLevel-mDischargeCurrentLevel; } @@ -11373,10 +11457,11 @@ public class BatteryStatsImpl extends BatteryStats { } } + @Override public int getDischargeAmountScreenOnSinceCharge() { synchronized(this) { int val = mDischargeAmountScreenOnSinceCharge; - if (mOnBattery && mScreenState == Display.STATE_ON + if (mOnBattery && isScreenOn(mScreenState) && mDischargeCurrentLevel < mDischargeScreenOnUnplugLevel) { val += mDischargeScreenOnUnplugLevel-mDischargeCurrentLevel; } @@ -11384,24 +11469,52 @@ public class BatteryStatsImpl extends BatteryStats { } } + @Override public int getDischargeAmountScreenOff() { synchronized(this) { int val = mDischargeAmountScreenOff; - if (mOnBattery && mScreenState != Display.STATE_ON + if (mOnBattery && isScreenOff(mScreenState) && mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) { val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel; } - return val; + // For backward compatibility, doze discharge is counted into screen off. + return val + getDischargeAmountScreenDoze(); } } + @Override public int getDischargeAmountScreenOffSinceCharge() { synchronized(this) { int val = mDischargeAmountScreenOffSinceCharge; - if (mOnBattery && mScreenState != Display.STATE_ON + if (mOnBattery && isScreenOff(mScreenState) && mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) { val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel; } + // For backward compatibility, doze discharge is counted into screen off. + return val + getDischargeAmountScreenDozeSinceCharge(); + } + } + + @Override + public int getDischargeAmountScreenDoze() { + synchronized(this) { + int val = mDischargeAmountScreenDoze; + if (mOnBattery && isScreenDoze(mScreenState) + && mDischargeCurrentLevel < mDischargeScreenDozeUnplugLevel) { + val += mDischargeScreenDozeUnplugLevel-mDischargeCurrentLevel; + } + return val; + } + } + + @Override + public int getDischargeAmountScreenDozeSinceCharge() { + synchronized(this) { + int val = mDischargeAmountScreenDozeSinceCharge; + if (mOnBattery && isScreenDoze(mScreenState) + && mDischargeCurrentLevel < mDischargeScreenDozeUnplugLevel) { + val += mDischargeScreenDozeUnplugLevel-mDischargeCurrentLevel; + } return val; } } @@ -11759,12 +11872,14 @@ public class BatteryStatsImpl extends BatteryStats { mHighDischargeAmountSinceCharge = in.readInt(); mDischargeAmountScreenOnSinceCharge = in.readInt(); mDischargeAmountScreenOffSinceCharge = in.readInt(); + mDischargeAmountScreenDozeSinceCharge = in.readInt(); mDischargeStepTracker.readFromParcel(in); mChargeStepTracker.readFromParcel(in); mDailyDischargeStepTracker.readFromParcel(in); mDailyChargeStepTracker.readFromParcel(in); mDischargeCounter.readSummaryFromParcelLocked(in); mDischargeScreenOffCounter.readSummaryFromParcelLocked(in); + mDischargeScreenDozeCounter.readSummaryFromParcelLocked(in); int NPKG = in.readInt(); if (NPKG > 0) { mDailyPackageChanges = new ArrayList<>(NPKG); @@ -11787,6 +11902,7 @@ public class BatteryStatsImpl extends BatteryStats { mScreenState = Display.STATE_UNKNOWN; mScreenOnTimer.readSummaryFromParcelLocked(in); + mScreenDozeTimer.readSummaryFromParcelLocked(in); for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) { mScreenBrightnessTimer[i].readSummaryFromParcelLocked(in); } @@ -12180,12 +12296,14 @@ public class BatteryStatsImpl extends BatteryStats { out.writeInt(getHighDischargeAmountSinceCharge()); out.writeInt(getDischargeAmountScreenOnSinceCharge()); out.writeInt(getDischargeAmountScreenOffSinceCharge()); + out.writeInt(getDischargeAmountScreenDozeSinceCharge()); mDischargeStepTracker.writeToParcel(out); mChargeStepTracker.writeToParcel(out); mDailyDischargeStepTracker.writeToParcel(out); mDailyChargeStepTracker.writeToParcel(out); mDischargeCounter.writeSummaryFromParcelLocked(out); mDischargeScreenOffCounter.writeSummaryFromParcelLocked(out); + mDischargeScreenDozeCounter.writeSummaryFromParcelLocked(out); if (mDailyPackageChanges != null) { final int NPKG = mDailyPackageChanges.size(); out.writeInt(NPKG); @@ -12203,6 +12321,7 @@ public class BatteryStatsImpl extends BatteryStats { out.writeLong(mNextMaxDailyDeadline); mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); + mScreenDozeTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) { mScreenBrightnessTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS); } @@ -12635,6 +12754,7 @@ public class BatteryStatsImpl extends BatteryStats { mScreenState = Display.STATE_UNKNOWN; mScreenOnTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase, in); + mScreenDozeTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase, in); for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) { mScreenBrightnessTimer[i] = new StopwatchTimer(mClocks, null, -100-i, null, mOnBatteryTimeBase, in); @@ -12728,10 +12848,13 @@ public class BatteryStatsImpl extends BatteryStats { mDischargeAmountScreenOnSinceCharge = in.readInt(); mDischargeAmountScreenOff = in.readInt(); mDischargeAmountScreenOffSinceCharge = in.readInt(); + mDischargeAmountScreenDoze = in.readInt(); + mDischargeAmountScreenDozeSinceCharge = in.readInt(); mDischargeStepTracker.readFromParcel(in); mChargeStepTracker.readFromParcel(in); mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in); - mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryTimeBase, in); + mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase, in); + mDischargeScreenDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in); mLastWriteTime = in.readLong(); mRpmStats.clear(); @@ -12848,6 +12971,7 @@ public class BatteryStatsImpl extends BatteryStats { mOnBatteryScreenOffTimeBase.writeToParcel(out, uSecUptime, uSecRealtime); mScreenOnTimer.writeToParcel(out, uSecRealtime); + mScreenDozeTimer.writeToParcel(out, uSecRealtime); for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) { mScreenBrightnessTimer[i].writeToParcel(out, uSecRealtime); } @@ -12910,10 +13034,13 @@ public class BatteryStatsImpl extends BatteryStats { out.writeInt(mDischargeAmountScreenOnSinceCharge); out.writeInt(mDischargeAmountScreenOff); out.writeInt(mDischargeAmountScreenOffSinceCharge); + out.writeInt(mDischargeAmountScreenDoze); + out.writeInt(mDischargeAmountScreenDozeSinceCharge); mDischargeStepTracker.writeToParcel(out); mChargeStepTracker.writeToParcel(out); mDischargeCounter.writeToParcel(out); mDischargeScreenOffCounter.writeToParcel(out); + mDischargeScreenDozeCounter.writeToParcel(out); out.writeLong(mLastWriteTime); out.writeInt(mRpmStats.size()); @@ -13020,8 +13147,10 @@ public class BatteryStatsImpl extends BatteryStats { pw.println("mOnBatteryScreenOffTimeBase:"); mOnBatteryScreenOffTimeBase.dump(pw, " "); Printer pr = new PrintWriterPrinter(pw); - pr.println("*** Screen timer:"); + pr.println("*** Screen on timer:"); mScreenOnTimer.logState(pr, " "); + pr.println("*** Screen doze timer:"); + mScreenDozeTimer.logState(pr, " "); for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) { pr.println("*** Screen brightness #" + i + ":"); mScreenBrightnessTimer[i].logState(pr, " "); diff --git a/com/android/internal/os/Zygote.java b/com/android/internal/os/Zygote.java index 5ee0918c..cbc63cf8 100644 --- a/com/android/internal/os/Zygote.java +++ b/com/android/internal/os/Zygote.java @@ -49,6 +49,11 @@ public final class Zygote { /** Make the code Java debuggable by turning off some optimizations. */ public static final int DEBUG_JAVA_DEBUGGABLE = 1 << 8; + /** Turn off the verifier. */ + public static final int DISABLE_VERIFIER = 1 << 9; + /** Only use oat files located in /system. Otherwise use dex/jar/apk . */ + public static final int ONLY_USE_SYSTEM_OAT_FILES = 1 << 10; + /** No external storage should be mounted. */ public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE; /** Default external storage should be mounted. */ diff --git a/com/android/internal/telephony/CallForwardInfo.java b/com/android/internal/telephony/CallForwardInfo.java index dccf3066..e40028fc 100644 --- a/com/android/internal/telephony/CallForwardInfo.java +++ b/com/android/internal/telephony/CallForwardInfo.java @@ -16,12 +16,16 @@ package com.android.internal.telephony; +import android.telecom.Log; + /** * See also RIL_CallForwardInfo in include/telephony/ril.h * * {@hide} */ public class CallForwardInfo { + private static final String TAG = "CallForwardInfo"; + public int status; /*1 = active, 0 = not active */ public int reason; /* from TS 27.007 7.11 "reason" */ public int serviceClass; /* Saum of CommandsInterface.SERVICE_CLASS */ @@ -31,9 +35,9 @@ public class CallForwardInfo { @Override public String toString() { - return super.toString() + (status == 0 ? " not active " : " active ") - + " reason: " + reason - + " serviceClass: " + serviceClass + " " + timeSeconds + " seconds"; - + return "[CallForwardInfo: status=" + (status == 0 ? " not active " : " active ") + + ", reason= " + reason + + ", serviceClass= " + serviceClass + ", timeSec= " + timeSeconds + " seconds" + + ", number=" + Log.pii(number) + "]"; } } diff --git a/com/android/internal/telephony/CarrierKeyDownloadManager.java b/com/android/internal/telephony/CarrierKeyDownloadManager.java index bca337d8..606f7ffd 100644 --- a/com/android/internal/telephony/CarrierKeyDownloadManager.java +++ b/com/android/internal/telephony/CarrierKeyDownloadManager.java @@ -16,8 +16,6 @@ package com.android.internal.telephony; -import static android.preference.PreferenceManager.getDefaultSharedPreferences; - import android.app.AlarmManager; import android.app.DownloadManager; import android.app.PendingIntent; @@ -34,22 +32,30 @@ import android.telephony.ImsiEncryptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; -import android.util.Base64; import android.util.Log; +import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; +import com.android.org.bouncycastle.util.io.pem.PemReader; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; +import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.Reader; +import java.security.PublicKey; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.util.Date; +import static android.preference.PreferenceManager.getDefaultSharedPreferences; + /** * This class contains logic to get Certificates and keep them current. * The class will be instantiated by various Phone implementations. @@ -68,16 +74,19 @@ public class CarrierKeyDownloadManager { private static final String INTENT_KEY_RENEWAL_ALARM_PREFIX = "com.android.internal.telephony.carrier_key_download_alarm"; - private int mKeyAvailability = 0; + @VisibleForTesting + public int mKeyAvailability = 0; public static final String MNC = "MNC"; public static final String MCC = "MCC"; private static final String SEPARATOR = ":"; - private static final String JSON_KEY = "key"; - private static final String JSON_TYPE = "type"; - private static final String JSON_IDENTIFIER = "identifier"; - private static final String JSON_EXPIRATION_DATE = "expiration-date"; + private static final String JSON_CERTIFICATE = "certificate"; + // This is a hack to accomodate Verizon. Verizon insists on using the public-key + // field to store the certificate. We'll just use which-ever is not null. + private static final String JSON_CERTIFICATE_ALTERNATE = "public-key"; + private static final String JSON_TYPE = "key-type"; + private static final String JSON_IDENTIFIER = "key-identifier"; private static final String JSON_CARRIER_KEYS = "carrier-keys"; private static final String JSON_TYPE_VALUE_WLAN = "WLAN"; private static final String JSON_TYPE_VALUE_EPDG = "EPDG"; @@ -89,7 +98,7 @@ public class CarrierKeyDownloadManager { private final Phone mPhone; private final Context mContext; - private final DownloadManager mDownloadManager; + public final DownloadManager mDownloadManager; private String mURL; public CarrierKeyDownloadManager(Phone phone) { @@ -173,14 +182,11 @@ public class CarrierKeyDownloadManager { } /** - * this method resets the alarm. Starts by cleaning up the existing alarms. - * We look at the earliest expiration date, and setup an alarms X days prior. - * If the expiration date is in the past, we'll setup an alarm to run the next day. This - * could happen if the download has failed. + * this method returns the date to be used to decide on when to start downloading the key. + * from the carrier. **/ - private void resetRenewalAlarm() { - cleanupRenewalAlarms(); - int slotId = mPhone.getPhoneId(); + @VisibleForTesting + public long getExpirationDate() { long minExpirationDate = Long.MAX_VALUE; for (int key_type : CARRIER_KEY_TYPES) { if (!isKeyEnabled(key_type)) { @@ -204,6 +210,20 @@ public class CarrierKeyDownloadManager { } else { minExpirationDate = minExpirationDate - DEFAULT_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS; } + return minExpirationDate; + } + + /** + * this method resets the alarm. Starts by cleaning up the existing alarms. + * We look at the earliest expiration date, and setup an alarms X days prior. + * If the expiration date is in the past, we'll setup an alarm to run the next day. This + * could happen if the download has failed. + **/ + @VisibleForTesting + public void resetRenewalAlarm() { + cleanupRenewalAlarms(); + int slotId = mPhone.getPhoneId(); + long minExpirationDate = getExpirationDate(); Log.d(LOG_TAG, "minExpirationDate: " + new Date(minExpirationDate)); final AlarmManager alarmManager = (AlarmManager) mContext.getSystemService( Context.ALARM_SERVICE); @@ -225,21 +245,30 @@ public class CarrierKeyDownloadManager { } /** + * Returns the sim operator. + **/ + @VisibleForTesting + public String getSimOperator() { + final TelephonyManager telephonyManager = + (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); + return telephonyManager.getSimOperator(mPhone.getSubId()); + } + + /** * checks if the download was sent by this particular instance. We do this by including the * slot id in the key. If no value is found, we know that the download was not for this * instance of the phone. **/ - private boolean isValidDownload(String mccMnc) { + @VisibleForTesting + public boolean isValidDownload(String mccMnc) { String mccCurrent = ""; String mncCurrent = ""; String mccSource = ""; String mncSource = ""; - final TelephonyManager telephonyManager = - (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); - String networkOperator = telephonyManager.getNetworkOperator(mPhone.getSubId()); - if (TextUtils.isEmpty(networkOperator) || TextUtils.isEmpty(mccMnc)) { - Log.e(LOG_TAG, "networkOperator or mcc/mnc is empty"); + String simOperator = getSimOperator(); + if (TextUtils.isEmpty(simOperator) || TextUtils.isEmpty(mccMnc)) { + Log.e(LOG_TAG, "simOperator or mcc/mnc is empty"); return false; } @@ -248,8 +277,8 @@ public class CarrierKeyDownloadManager { mncSource = splitValue[1]; Log.d(LOG_TAG, "values from sharedPrefs mcc, mnc: " + mccSource + "," + mncSource); - mccCurrent = networkOperator.substring(0, 3); - mncCurrent = networkOperator.substring(3); + mccCurrent = simOperator.substring(0, 3); + mncCurrent = simOperator.substring(3); Log.d(LOG_TAG, "using values for mcc, mnc: " + mccCurrent + "," + mncCurrent); if (TextUtils.equals(mncSource, mncCurrent) && TextUtils.equals(mccSource, mccCurrent)) { @@ -348,19 +377,20 @@ public class CarrierKeyDownloadManager { * Converts the string into a json object to retreive the nodes. The Json should have 3 nodes, * including the Carrier public key, the key type and the key identifier. Once the nodes have * been extracted, they get persisted to the database. Sample: - * "carrier-keys": [ { "key": "", - * "type": WLAN, - * "identifier": "", - * "expiration-date": 1502577746000 + * "carrier-keys": [ { "certificate": "", + * "key-type": "WLAN", + * "key-identifier": "" * } ] * @param jsonStr the json string. - * @param mccMnc contains the mcc, mnc + * @param mccMnc contains the mcc, mnc. */ - private void parseJsonAndPersistKey(String jsonStr, String mccMnc) { + @VisibleForTesting + public void parseJsonAndPersistKey(String jsonStr, String mccMnc) { if (TextUtils.isEmpty(jsonStr) || TextUtils.isEmpty(mccMnc)) { Log.e(LOG_TAG, "jsonStr or mcc, mnc: is empty"); return; } + PemReader reader = null; try { String mcc = ""; String mnc = ""; @@ -369,10 +399,16 @@ public class CarrierKeyDownloadManager { mnc = splitValue[1]; JSONObject jsonObj = new JSONObject(jsonStr); JSONArray keys = jsonObj.getJSONArray(JSON_CARRIER_KEYS); - for (int i = 0; i < keys.length(); i++) { JSONObject key = keys.getJSONObject(i); - String carrierKey = key.getString(JSON_KEY); + // This is a hack to accomodate Verizon. Verizon insists on using the public-key + // field to store the certificate. We'll just use which-ever is not null. + String cert = null; + if (key.has(JSON_CERTIFICATE)) { + cert = key.getString(JSON_CERTIFICATE); + } else { + cert = key.getString(JSON_CERTIFICATE_ALTERNATE); + } String typeString = key.getString(JSON_TYPE); int type = UNINITIALIZED_KEY_TYPE; if (typeString.equals(JSON_TYPE_VALUE_WLAN)) { @@ -380,13 +416,27 @@ public class CarrierKeyDownloadManager { } else if (typeString.equals(JSON_TYPE_VALUE_EPDG)) { type = TelephonyManager.KEY_TYPE_EPDG; } - long expiration_date = key.getLong(JSON_EXPIRATION_DATE); String identifier = key.getString(JSON_IDENTIFIER); - savePublicKey(carrierKey, type, identifier, expiration_date, - mcc, mnc); + ByteArrayInputStream inStream = new ByteArrayInputStream(cert.getBytes()); + Reader fReader = new BufferedReader(new InputStreamReader(inStream)); + reader = new PemReader(fReader); + Pair<PublicKey, Long> keyInfo = + getKeyInformation(reader.readPemObject().getContent()); + reader.close(); + savePublicKey(keyInfo.first, type, identifier, keyInfo.second, mcc, mnc); } } catch (final JSONException e) { Log.e(LOG_TAG, "Json parsing error: " + e.getMessage()); + } catch (final Exception e) { + Log.e(LOG_TAG, "Exception getting certificate: " + e); + } finally { + try { + if (reader != null) { + reader.close(); + } + } catch (final Exception e) { + Log.e(LOG_TAG, "Exception getting certificate: " + e); + } } } @@ -394,8 +444,8 @@ public class CarrierKeyDownloadManager { * introspects the mKeyAvailability bitmask * @return true if the digit at position k is 1, else false. */ - - private boolean isKeyEnabled(int keyType) { + @VisibleForTesting + public boolean isKeyEnabled(int keyType) { //since keytype has values of 1, 2.... we need to subtract 1 from the keytype. int returnValue = (mKeyAvailability >> (keyType - 1)) & 1; return (returnValue == 1) ? true : false; @@ -427,15 +477,13 @@ public class CarrierKeyDownloadManager { private boolean downloadKey() { Log.d(LOG_TAG, "starting download from: " + mURL); - final TelephonyManager telephonyManager = - (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); String mcc = ""; String mnc = ""; - String networkOperator = telephonyManager.getNetworkOperator(mPhone.getSubId()); + String simOperator = getSimOperator(); - if (!TextUtils.isEmpty(networkOperator)) { - mcc = networkOperator.substring(0, 3); - mnc = networkOperator.substring(3); + if (!TextUtils.isEmpty(simOperator)) { + mcc = simOperator.substring(0, 3); + mnc = simOperator.substring(3); Log.d(LOG_TAG, "using values for mcc, mnc: " + mcc + "," + mnc); } else { Log.e(LOG_TAG, "mcc, mnc: is empty"); @@ -461,11 +509,35 @@ public class CarrierKeyDownloadManager { return true; } - private void savePublicKey(String key, int type, String identifier, long expirationDate, + /** + * Save the public key + * @param certificate certificate that contains the public key. + * @return Pair containing the Public Key and the expiration date. + **/ + @VisibleForTesting + public static Pair<PublicKey, Long> getKeyInformation(byte[] certificate) throws Exception { + InputStream inStream = new ByteArrayInputStream(certificate); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream); + Pair<PublicKey, Long> keyInformation = + new Pair(cert.getPublicKey(), cert.getNotAfter().getTime()); + return keyInformation; + } + + /** + * Save the public key + * @param publicKey public key. + * @param type key-type. + * @param identifier which is an opaque string. + * @param expirationDate expiration date of the key. + * @param mcc + * @param mnc + **/ + @VisibleForTesting + public void savePublicKey(PublicKey publicKey, int type, String identifier, long expirationDate, String mcc, String mnc) { - byte[] keyBytes = Base64.decode(key.getBytes(), Base64.DEFAULT); ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo(mcc, mnc, type, identifier, - keyBytes, new Date(expirationDate)); + publicKey, new Date(expirationDate)); mPhone.setCarrierInfoForImsiEncryption(imsiEncryptionInfo); } } diff --git a/com/android/internal/telephony/CarrierServiceStateTracker.java b/com/android/internal/telephony/CarrierServiceStateTracker.java index 8df201e5..77a39eb9 100644 --- a/com/android/internal/telephony/CarrierServiceStateTracker.java +++ b/com/android/internal/telephony/CarrierServiceStateTracker.java @@ -79,13 +79,8 @@ public class CarrierServiceStateTracker extends Handler { switch (msg.what) { case CARRIER_EVENT_VOICE_REGISTRATION: case CARRIER_EVENT_DATA_REGISTRATION: - handleConfigChanges(); - break; case CARRIER_EVENT_VOICE_DEREGISTRATION: case CARRIER_EVENT_DATA_DEREGISTRATION: - if (isRadioOffOrAirplaneMode()) { - break; - } handleConfigChanges(); break; case NOTIFICATION_EMERGENCY_NETWORK: @@ -317,8 +312,8 @@ public class CarrierServiceStateTracker extends Handler { Rlog.i(LOG_TAG, "PrefNetworkNotification: sendMessage() w/values: " + "," + isPhoneStillRegistered() + "," + mDelay + "," + isGlobalMode() + "," + mSST.isRadioOn()); - if (mDelay == UNINITIALIZED_DELAY_VALUE || isPhoneStillRegistered() - || isGlobalMode()) { + if (mDelay == UNINITIALIZED_DELAY_VALUE || isPhoneStillRegistered() || isGlobalMode() + || isRadioOffOrAirplaneMode()) { return false; } return true; diff --git a/com/android/internal/telephony/ClientWakelockTracker.java b/com/android/internal/telephony/ClientWakelockTracker.java index 5bec60ba..fa71e769 100644 --- a/com/android/internal/telephony/ClientWakelockTracker.java +++ b/com/android/internal/telephony/ClientWakelockTracker.java @@ -18,10 +18,10 @@ package com.android.internal.telephony; import android.os.SystemClock; import android.telephony.ClientRequestStats; -import android.telephony.Rlog; import com.android.internal.annotations.VisibleForTesting; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -119,13 +119,13 @@ public class ClientWakelockTracker { return false; } - void dumpClientRequestTracker() { - Rlog.d(RIL.RILJ_LOG_TAG, "-------mClients---------------"); + void dumpClientRequestTracker(PrintWriter pw) { + pw.println("-------mClients---------------"); synchronized (mClients) { for (String key : mClients.keySet()) { - Rlog.d(RIL.RILJ_LOG_TAG, "Client : " + key); - Rlog.d(RIL.RILJ_LOG_TAG, mClients.get(key).toString()); + pw.println("Client : " + key); + pw.println(mClients.get(key).toString()); } } } -}
\ No newline at end of file +} diff --git a/com/android/internal/telephony/Connection.java b/com/android/internal/telephony/Connection.java index 245f76ce..8c54a31b 100644 --- a/com/android/internal/telephony/Connection.java +++ b/com/android/internal/telephony/Connection.java @@ -34,6 +34,7 @@ import java.util.concurrent.CopyOnWriteArraySet; * {@hide} */ public abstract class Connection { + private static final String TAG = "Connection"; public interface PostDialListener { void onPostDialWait(); @@ -836,6 +837,16 @@ public abstract class Connection { public void setConnectionExtras(Bundle extras) { if (extras != null) { mExtras = new Bundle(extras); + + int previousCount = mExtras.size(); + // Prevent vendors from passing in extras other than primitive types and android API + // parcelables. + mExtras = mExtras.filterValues(); + int filteredCount = mExtras.size(); + if (filteredCount != previousCount) { + Rlog.i(TAG, "setConnectionExtras: filtering " + (previousCount - filteredCount) + + " invalid extras."); + } } else { mExtras = null; } diff --git a/com/android/internal/telephony/DefaultPhoneNotifier.java b/com/android/internal/telephony/DefaultPhoneNotifier.java index c13e5408..98c0a32e 100644 --- a/com/android/internal/telephony/DefaultPhoneNotifier.java +++ b/com/android/internal/telephony/DefaultPhoneNotifier.java @@ -125,6 +125,9 @@ public class DefaultPhoneNotifier implements PhoneNotifier { int subId = sender.getSubId(); try { if (mRegistry != null) { + Rlog.d(LOG_TAG, "notifyCallForwardingChanged: subId=" + subId + ", isCFActive=" + + sender.getCallForwardingIndicator()); + mRegistry.notifyCallForwardingChangedForSubscriber(subId, sender.getCallForwardingIndicator()); } diff --git a/com/android/internal/telephony/GsmCdmaPhone.java b/com/android/internal/telephony/GsmCdmaPhone.java index d95d0183..ad078d67 100644 --- a/com/android/internal/telephony/GsmCdmaPhone.java +++ b/com/android/internal/telephony/GsmCdmaPhone.java @@ -286,7 +286,7 @@ public class GsmCdmaPhone extends Phone { tm.setPhoneType(getPhoneId(), PhoneConstants.PHONE_TYPE_GSM); mIccCardProxy.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS); } else { - mCdmaSubscriptionSource = CdmaSubscriptionSourceManager.SUBSCRIPTION_SOURCE_UNKNOWN; + mCdmaSubscriptionSource = mCdmaSSM.getCdmaSubscriptionSource(); // This is needed to handle phone process crashes mIsPhoneInEcmState = getInEcmMode(); if (mIsPhoneInEcmState) { @@ -505,7 +505,7 @@ public class GsmCdmaPhone extends Phone { ret = PhoneConstants.DataState.DISCONNECTED; } else if (mSST.getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE - && (isPhoneTypeCdma() || + && (isPhoneTypeCdma() || isPhoneTypeCdmaLte() || (isPhoneTypeGsm() && !apnType.equals(PhoneConstants.APN_TYPE_EMERGENCY)))) { // If we're out of service, open TCP sockets may still work // but no data will flow @@ -1063,7 +1063,7 @@ public class GsmCdmaPhone extends Phone { boolean alwaysTryImsForEmergencyCarrierConfig = configManager.getConfigForSubId(getSubId()) .getBoolean(CarrierConfigManager.KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL); - boolean imsUseEnabled = isImsUseEnabled() + boolean useImsForCall = isImsUseEnabled() && imsPhone != null && (imsPhone.isVolteEnabled() || imsPhone.isWifiCallingEnabled() || (imsPhone.isVideoEnabled() && VideoProfile.isVideo(videoState))) @@ -1083,7 +1083,7 @@ public class GsmCdmaPhone extends Phone { boolean useImsForUt = imsPhone != null && imsPhone.isUtEnabled(); if (DBG) { - logd("imsUseEnabled=" + imsUseEnabled + logd("useImsForCall=" + useImsForCall + ", useImsForEmergency=" + useImsForEmergency + ", useImsForUt=" + useImsForUt + ", isUt=" + isUt @@ -1100,13 +1100,13 @@ public class GsmCdmaPhone extends Phone { Phone.checkWfcWifiOnlyModeBeforeDial(mImsPhone, mContext); - if ((imsUseEnabled && (!isUt || useImsForUt)) || useImsForEmergency) { + if ((useImsForCall && !isUt) || (isUt && useImsForUt) || useImsForEmergency) { try { if (DBG) logd("Trying IMS PS call"); return imsPhone.dial(dialString, uusInfo, videoState, intentExtras); } catch (CallStateException e) { if (DBG) logd("IMS PS call exception " + e + - "imsUseEnabled =" + imsUseEnabled + ", imsPhone =" + imsPhone); + "useImsForCall =" + useImsForCall + ", imsPhone =" + imsPhone); // Do not throw a CallStateException and instead fall back to Circuit switch // for emergency calls and MMI codes. if (Phone.CS_FALLBACK.equals(e.getMessage()) || isEmergency) { @@ -2549,6 +2549,7 @@ public class GsmCdmaPhone extends Phone { private void processIccRecordEvents(int eventCode) { switch (eventCode) { case IccRecords.EVENT_CFI: + logi("processIccRecordEvents: EVENT_CFI"); notifyCallForwardingIndicator(); break; } diff --git a/com/android/internal/telephony/InboundSmsHandler.java b/com/android/internal/telephony/InboundSmsHandler.java index 59195f83..391de500 100644 --- a/com/android/internal/telephony/InboundSmsHandler.java +++ b/com/android/internal/telephony/InboundSmsHandler.java @@ -158,6 +158,17 @@ public abstract class InboundSmsHandler extends StateMachine { /** New SMS received as an AsyncResult. */ public static final int EVENT_INJECT_SMS = 8; + /** Update tracker object; used only in waiting state */ + private static final int EVENT_UPDATE_TRACKER = 9; + + /** Timeout in case state machine is stuck in a state for too long; used only in waiting + * state */ + private static final int EVENT_STATE_TIMEOUT = 10; + + /** Timeout duration for EVENT_STATE_TIMEOUT */ + @VisibleForTesting + public static final int STATE_TIMEOUT = 30000; + /** Wakelock release delay when returning to idle state. */ private static final int WAKELOCK_TIMEOUT = 3000; @@ -450,6 +461,7 @@ public abstract class InboundSmsHandler extends StateMachine { // if any broadcasts were sent, transition to waiting state InboundSmsTracker inboundSmsTracker = (InboundSmsTracker) msg.obj; if (processMessagePart(inboundSmsTracker)) { + sendMessage(EVENT_UPDATE_TRACKER, inboundSmsTracker); transitionTo(mWaitingState); } else { // if event is sent from SmsBroadcastUndelivered.broadcastSms(), and @@ -493,18 +505,41 @@ public abstract class InboundSmsHandler extends StateMachine { * {@link IdleState} after any deferred {@link #EVENT_BROADCAST_SMS} messages are handled. */ private class WaitingState extends State { + private InboundSmsTracker mTracker; + + @Override + public void enter() { + if (DBG) log("entering Waiting state"); + mTracker = null; + sendMessageDelayed(EVENT_STATE_TIMEOUT, STATE_TIMEOUT); + } + @Override public void exit() { if (DBG) log("exiting Waiting state"); // Before moving to idle state, set wakelock timeout to WAKE_LOCK_TIMEOUT milliseconds // to give any receivers time to take their own wake locks setWakeLockTimeout(WAKELOCK_TIMEOUT); + if (VDBG) { + if (hasMessages(EVENT_STATE_TIMEOUT)) { + log("exiting Waiting state: removing EVENT_STATE_TIMEOUT from message queue"); + } + if (hasMessages(EVENT_UPDATE_TRACKER)) { + log("exiting Waiting state: removing EVENT_UPDATE_TRACKER from message queue"); + } + } + removeMessages(EVENT_STATE_TIMEOUT); + removeMessages(EVENT_UPDATE_TRACKER); } @Override public boolean processMessage(Message msg) { log("WaitingState.processMessage:" + msg.what); switch (msg.what) { + case EVENT_UPDATE_TRACKER: + mTracker = (InboundSmsTracker) msg.obj; + return HANDLED; + case EVENT_BROADCAST_SMS: // defer until the current broadcast completes deferMessage(msg); @@ -520,6 +555,18 @@ public abstract class InboundSmsHandler extends StateMachine { // not ready to return to idle; ignore return HANDLED; + case EVENT_STATE_TIMEOUT: + // stuck in WaitingState for too long; drop the message and exit this state + if (mTracker != null) { + log("WaitingState.processMessage: EVENT_STATE_TIMEOUT; dropping message"); + dropSms(new SmsBroadcastReceiver(mTracker)); + } else { + log("WaitingState.processMessage: EVENT_STATE_TIMEOUT; mTracker is null " + + "- sending EVENT_BROADCAST_COMPLETE"); + sendMessage(EVENT_BROADCAST_COMPLETE); + } + return HANDLED; + default: // parent state handles the other message types return NOT_HANDLED; diff --git a/com/android/internal/telephony/Phone.java b/com/android/internal/telephony/Phone.java index 28e45565..6acc8743 100644 --- a/com/android/internal/telephony/Phone.java +++ b/com/android/internal/telephony/Phone.java @@ -56,6 +56,7 @@ import android.telephony.SignalStrength; import android.telephony.SubscriptionManager; import android.telephony.VoLteServiceState; import android.text.TextUtils; +import android.util.Log; import com.android.ims.ImsCall; import com.android.ims.ImsConfig; @@ -226,6 +227,9 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { // Key used to read/write "disable DNS server check" pref (used for testing) private static final String DNS_SERVER_CHECK_DISABLED_KEY = "dns_server_check_disabled_key"; + // Integer used to let the calling application know that the we are ignoring auto mode switch. + private static final int ALREADY_IN_AUTO_SELECTION = 1; + /** * This method is invoked when the Phone exits Emergency Callback Mode. */ @@ -1205,6 +1209,11 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { mCi.setNetworkSelectionModeAutomatic(msg); } else { Rlog.d(LOG_TAG, "setNetworkSelectionModeAutomatic - already auto, ignoring"); + // let the calling application know that the we are ignoring automatic mode switch. + if (nsm.message != null) { + nsm.message.arg1 = ALREADY_IN_AUTO_SELECTION; + } + ar.userObj = nsm; handleSetSelectNetwork(ar); } @@ -1789,7 +1798,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { int status = enable ? IccRecords.CALL_FORWARDING_STATUS_ENABLED : IccRecords.CALL_FORWARDING_STATUS_DISABLED; int subId = getSubId(); - Rlog.d(LOG_TAG, "setCallForwardingIndicatorInSharedPref: Storing status = " + status + + Rlog.i(LOG_TAG, "setCallForwardingIndicatorInSharedPref: Storing status = " + status + " in pref " + CF_STATUS + subId); SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); @@ -1831,6 +1840,9 @@ public abstract class Phone extends Handler implements PhoneInternalInterface { if (callForwardingIndicator == IccRecords.CALL_FORWARDING_STATUS_UNKNOWN) { callForwardingIndicator = getCallForwardingIndicatorFromSharedPref(); } + Rlog.v(LOG_TAG, "getCallForwardingIndicator: iccForwardingFlag=" + (r != null + ? r.getVoiceCallForwardingFlag() : "null") + ", sharedPrefFlag=" + + getCallForwardingIndicatorFromSharedPref()); return (callForwardingIndicator == IccRecords.CALL_FORWARDING_STATUS_ENABLED); } diff --git a/com/android/internal/telephony/RIL.java b/com/android/internal/telephony/RIL.java index 8bb2125d..84c2b659 100644 --- a/com/android/internal/telephony/RIL.java +++ b/com/android/internal/telephony/RIL.java @@ -4774,7 +4774,7 @@ public class RIL extends BaseCommands implements CommandsInterface { } pw.println(" mLastNITZTimeInfo=" + Arrays.toString(mLastNITZTimeInfo)); pw.println(" mTestingEmergencyCall=" + mTestingEmergencyCall.get()); - mClientWakelockTracker.dumpClientRequestTracker(); + mClientWakelockTracker.dumpClientRequestTracker(pw); } public List<ClientRequestStats> getClientRequestStats() { diff --git a/com/android/internal/telephony/ServiceStateTracker.java b/com/android/internal/telephony/ServiceStateTracker.java index b3794402..c34cbb29 100644 --- a/com/android/internal/telephony/ServiceStateTracker.java +++ b/com/android/internal/telephony/ServiceStateTracker.java @@ -210,6 +210,7 @@ public class ServiceStateTracker extends Handler { protected static final int EVENT_ALL_DATA_DISCONNECTED = 49; protected static final int EVENT_PHONE_TYPE_SWITCHED = 50; protected static final int EVENT_RADIO_POWER_FROM_CARRIER = 51; + protected static final int EVENT_SIM_NOT_INSERTED = 52; protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; @@ -354,6 +355,14 @@ public class ServiceStateTracker extends Handler { } // update voicemail count and notify message waiting changed mPhone.updateVoiceMail(); + + // cancel notifications if we see SIM_NOT_INSERTED (This happens on bootup before + // the SIM is first detected and then subsequently on SIM removals) + if (mSubscriptionController.getSlotIndex(subId) + == SubscriptionManager.SIM_NOT_INSERTED) { + // this is handled on the main thread to mitigate racing with setNotification(). + sendMessage(obtainMessage(EVENT_SIM_NOT_INSERTED)); + } } } }; @@ -446,12 +455,15 @@ public class ServiceStateTracker extends Handler { public static final int CS_NORMAL_ENABLED = 1005; // Access Control blocks normal voice/sms service public static final int CS_EMERGENCY_ENABLED = 1006; // Access Control blocks emergency call service public static final int CS_REJECT_CAUSE_ENABLED = 2001; // Notify MM rejection cause - public static final int CS_REJECT_CAUSE_DISABLED = 2002; // Cancel MM rejection cause /** Notification id. */ public static final int PS_NOTIFICATION = 888; // Id to update and cancel PS restricted public static final int CS_NOTIFICATION = 999; // Id to update and cancel CS restricted public static final int CS_REJECT_CAUSE_NOTIFICATION = 111; // Id to update and cancel MM // rejection cause + + /** To identify whether EVENT_SIM_READY is received or not */ + private boolean mIsSimReady = false; + private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -1064,6 +1076,11 @@ public class ServiceStateTracker extends Handler { case EVENT_ICC_CHANGED: onUpdateIccAvailability(); + if (mUiccApplcation != null + && mUiccApplcation.getState() != AppState.APPSTATE_READY) { + mIsSimReady = false; + updateSpnDisplay(); + } break; case EVENT_GET_CELL_INFO_LIST: { @@ -1121,6 +1138,7 @@ public class ServiceStateTracker extends Handler { // Reset the mPreviousSubId so we treat a SIM power bounce // as a first boot. See b/19194287 mOnSubscriptionsChangedListener.mPreviousSubId.set(-1); + mIsSimReady = true; pollState(); // Signal strength polling stops when radio is off queueNextSignalStrengthPoll(); @@ -1298,6 +1316,14 @@ public class ServiceStateTracker extends Handler { } break; + case EVENT_SIM_NOT_INSERTED: + if (DBG) log("EVENT_SIM_NOT_INSERTED"); + cancelAllNotifications(); + mMdn = null; + mMin = null; + mIsMinInfoReady = false; + break; + case EVENT_ALL_DATA_DISCONNECTED: int dds = SubscriptionManager.getDefaultDataSubscriptionId(); ProxyController.getInstance().unregisterForAllDataDisconnected(dds, this); @@ -2222,7 +2248,12 @@ public class ServiceStateTracker extends Handler { if (combinedRegState == ServiceState.STATE_OUT_OF_SERVICE || combinedRegState == ServiceState.STATE_EMERGENCY_ONLY) { showPlmn = true; - if (mEmergencyOnly) { + + // Force display no service + final boolean forceDisplayNoService = mPhone.getContext().getResources().getBoolean( + com.android.internal.R.bool.config_display_no_service_when_sim_unready) + && !mIsSimReady; + if (mEmergencyOnly && !forceDisplayNoService) { // No service but emergency call allowed plmn = Resources.getSystem(). getText(com.android.internal.R.string.emergency_calls_only).toString(); @@ -2825,7 +2856,7 @@ public class ServiceStateTracker extends Handler { } if (hasRejectCauseChanged) { - setNotification(mRejectCode == 0 ? CS_REJECT_CAUSE_DISABLED : CS_REJECT_CAUSE_ENABLED); + setNotification(CS_REJECT_CAUSE_ENABLED); } if (hasChanged) { @@ -3833,6 +3864,18 @@ public class ServiceStateTracker extends Handler { } /** + * Cancels all notifications posted to NotificationManager. These notifications for restricted + * state and rejection cause for cs registration are no longer valid after the SIM has been + * removed. + */ + private void cancelAllNotifications() { + if (DBG) log("setNotification: cancelAllNotifications"); + NotificationManager notificationManager = (NotificationManager) + mPhone.getContext().getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.cancelAll(); + } + + /** * Post a notification to NotificationManager for restricted state and * rejection cause for cs registration * @@ -3907,17 +3950,14 @@ public class ServiceStateTracker extends Handler { notificationId = CS_REJECT_CAUSE_NOTIFICATION; int resId = selectResourceForRejectCode(mRejectCode); if (0 == resId) { - // cancel notification because current reject code is not handled. - notifyType = CS_REJECT_CAUSE_DISABLED; + loge("setNotification: mRejectCode=" + mRejectCode + " is not handled."); + return; } else { icon = com.android.internal.R.drawable.stat_notify_mmcc_indication_icn; title = Resources.getSystem().getString(resId); details = null; } break; - case CS_REJECT_CAUSE_DISABLED: - notificationId = CS_REJECT_CAUSE_NOTIFICATION; - break; } if (DBG) { @@ -3941,8 +3981,7 @@ public class ServiceStateTracker extends Handler { NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - if (notifyType == PS_DISABLED || notifyType == CS_DISABLED - || notifyType == CS_REJECT_CAUSE_DISABLED) { + if (notifyType == PS_DISABLED || notifyType == CS_DISABLED) { // cancel previous post notification notificationManager.cancel(notificationId); } else { diff --git a/com/android/internal/telephony/cat/AppInterface.java b/com/android/internal/telephony/cat/AppInterface.java index c78b7f8c..1f2d3a06 100644 --- a/com/android/internal/telephony/cat/AppInterface.java +++ b/com/android/internal/telephony/cat/AppInterface.java @@ -84,6 +84,7 @@ public interface AppInterface { SET_UP_MENU(0x25), SET_UP_CALL(0x10), PROVIDE_LOCAL_INFORMATION(0x26), + LANGUAGE_NOTIFICATION(0x35), OPEN_CHANNEL(0x40), CLOSE_CHANNEL(0x41), RECEIVE_DATA(0x42), diff --git a/com/android/internal/telephony/cat/CatService.java b/com/android/internal/telephony/cat/CatService.java index a242de43..cd7a7561 100644 --- a/com/android/internal/telephony/cat/CatService.java +++ b/com/android/internal/telephony/cat/CatService.java @@ -16,15 +16,21 @@ package com.android.internal.telephony.cat; +import android.app.ActivityManagerNative; +import android.app.IActivityManager; +import android.app.backup.BackupManager; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.res.Configuration; import android.content.res.Resources.NotFoundException; import android.os.AsyncResult; import android.os.Handler; import android.os.HandlerThread; +import android.os.LocaleList; import android.os.Message; +import android.os.RemoteException; import android.os.SystemProperties; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; @@ -450,6 +456,18 @@ public class CatService extends Handler implements AppInterface { ((CallSetupParams) cmdParams).mConfirmMsg.text = message.toString(); } break; + case LANGUAGE_NOTIFICATION: + String language = ((LanguageParams) cmdParams).mLanguage; + ResultCode result = ResultCode.OK; + if (language != null && language.length() > 0) { + try { + changeLanguage(language); + } catch (RemoteException e) { + result = ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS; + } + } + sendTerminalResponse(cmdParams.mCmdDet, result, false, 0, null); + return; case OPEN_CHANNEL: case CLOSE_CHANNEL: case RECEIVE_DATA: @@ -881,8 +899,9 @@ public class CatService extends Handler implements AppInterface { // This sends an intent with CARD_ABSENT (0 - false) /CARD_PRESENT (1 - true). intent.putExtra(AppInterface.CARD_STATUS, cardPresent); intent.setComponent(AppInterface.getDefaultSTKApplication()); + intent.putExtra("SLOT_ID", mSlotId); CatLog.d(this, "Sending Card Status: " - + cardState + " " + "cardPresent: " + cardPresent); + + cardState + " " + "cardPresent: " + cardPresent + "SLOT_ID: " + mSlotId); mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION); } @@ -1006,6 +1025,13 @@ public class CatService extends Handler implements AppInterface { } break; case LAUNCH_BROWSER: + if (resMsg.mResCode == ResultCode.LAUNCH_BROWSER_ERROR) { + // Additional info for Default URL unavailable. + resMsg.setAdditionalInfo(0x04); + } else { + resMsg.mIncludeAdditionalInfo = false; + resMsg.mAdditionalInfo = 0; + } break; // 3GPP TS.102.223: Open Channel alpha confirmation should not send TR case OPEN_CHANNEL: @@ -1121,4 +1147,13 @@ public class CatService extends Handler implements AppInterface { mCmdIf.reportStkServiceIsRunning(null); } } + + private void changeLanguage(String language) throws RemoteException { + IActivityManager am = ActivityManagerNative.getDefault(); + Configuration config = am.getConfiguration(); + config.setLocales(new LocaleList(new Locale(language), LocaleList.getDefault())); + config.userSetLocale = true; + am.updatePersistentConfiguration(config); + BackupManager.dataChanged("com.android.providers.settings"); + } } diff --git a/com/android/internal/telephony/cat/CommandParams.java b/com/android/internal/telephony/cat/CommandParams.java index 7dfedab8..59cd4148 100644 --- a/com/android/internal/telephony/cat/CommandParams.java +++ b/com/android/internal/telephony/cat/CommandParams.java @@ -150,6 +150,15 @@ class CallSetupParams extends CommandParams { } } +class LanguageParams extends CommandParams { + String mLanguage; + + LanguageParams(CommandDetails cmdDet, String lang) { + super(cmdDet); + mLanguage = lang; + } +} + class SelectItemParams extends CommandParams { Menu mMenu = null; boolean mLoadTitleIcon = false; diff --git a/com/android/internal/telephony/cat/CommandParamsFactory.java b/com/android/internal/telephony/cat/CommandParamsFactory.java index 3dd53376..eb928885 100644 --- a/com/android/internal/telephony/cat/CommandParamsFactory.java +++ b/com/android/internal/telephony/cat/CommandParamsFactory.java @@ -19,12 +19,15 @@ package com.android.internal.telephony.cat; import android.graphics.Bitmap; import android.os.Handler; import android.os.Message; +import android.text.TextUtils; import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.uicc.IccFileHandler; import java.util.Iterator; import java.util.List; +import java.util.Locale; + import static com.android.internal.telephony.cat.CatCmdMessage. SetupEventListConstants.USER_ACTIVITY_EVENT; import static com.android.internal.telephony.cat.CatCmdMessage. @@ -47,6 +50,8 @@ class CommandParamsFactory extends Handler { private int mIconLoadState = LOAD_NO_ICON; private RilMessageDecoder mCaller = null; private boolean mloadIcon = false; + private String mSavedLanguage; + private String mRequestedLanguage; // constants static final int MSG_ID_LOAD_ICON_DONE = 1; @@ -66,6 +71,10 @@ class CommandParamsFactory extends Handler { static final int DTTZ_SETTING = 0x03; static final int LANGUAGE_SETTING = 0x04; + // Command Qualifier value for language notification command + static final int NON_SPECIFIC_LANGUAGE = 0x00; + static final int SPECIFIC_LANGUAGE = 0x01; + // As per TS 102.223 Annex C, Structure of CAT communications, // the APDU length can be max 255 bytes. This leaves only 239 bytes for user // input string. CMD details TLV + Device IDs TLV + Result TLV + Other @@ -203,6 +212,9 @@ class CommandParamsFactory extends Handler { case PROVIDE_LOCAL_INFORMATION: cmdPending = processProvideLocalInfo(cmdDet, ctlvs); break; + case LANGUAGE_NOTIFICATION: + cmdPending = processLanguageNotification(cmdDet, ctlvs); + break; case OPEN_CHANNEL: case CLOSE_CHANNEL: case RECEIVE_DATA: @@ -1014,6 +1026,67 @@ class CommandParamsFactory extends Handler { return false; } + /** + * Processes LANGUAGE_NOTIFICATION proactive command from the SIM card. + * + * The SPECIFIC_LANGUAGE notification sets the specified language. + * The NON_SPECIFIC_LANGUAGE notification restores the last specifically set language. + * + * @param cmdDet Command Details object retrieved from the proactive command object + * @param ctlvs List of ComprehensionTlv objects following Command Details + * object and Device Identities object within the proactive command + * @return false. This function always returns false meaning that the command + * processing is not pending and additional asynchronous processing + * is not required. + */ + private boolean processLanguageNotification(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) + throws ResultException { + CatLog.d(this, "process Language Notification"); + + String desiredLanguage = null; + String currentLanguage = Locale.getDefault().getLanguage(); + switch (cmdDet.commandQualifier) { + case NON_SPECIFIC_LANGUAGE: + if (!TextUtils.isEmpty(mSavedLanguage) && (!TextUtils.isEmpty(mRequestedLanguage) + && mRequestedLanguage.equals(currentLanguage))) { + CatLog.d(this, "Non-specific language notification changes the language " + + "setting back to " + mSavedLanguage); + desiredLanguage = mSavedLanguage; + } + + mSavedLanguage = null; + mRequestedLanguage = null; + break; + case SPECIFIC_LANGUAGE: + ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.LANGUAGE, ctlvs); + if (ctlv != null) { + int valueLen = ctlv.getLength(); + if (valueLen != 2) { + throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); + } + + byte[] rawValue = ctlv.getRawValue(); + int valueIndex = ctlv.getValueIndex(); + desiredLanguage = GsmAlphabet.gsm8BitUnpackedToString(rawValue, valueIndex, 2); + + if (TextUtils.isEmpty(mSavedLanguage) || (!TextUtils.isEmpty(mRequestedLanguage) + && !mRequestedLanguage.equals(currentLanguage))) { + mSavedLanguage = currentLanguage; + } + mRequestedLanguage = desiredLanguage; + CatLog.d(this, "Specific language notification changes the language setting to " + + mRequestedLanguage); + } + break; + default: + CatLog.d(this, "LN[" + cmdDet.commandQualifier + "] Command Not Supported"); + break; + } + + mCmdParams = new LanguageParams(cmdDet, desiredLanguage); + return false; + } + private boolean processBIPClient(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) throws ResultException { AppInterface.CommandType commandType = diff --git a/com/android/internal/telephony/cdma/SmsMessage.java b/com/android/internal/telephony/cdma/SmsMessage.java index 629173df..7a53ef63 100644 --- a/com/android/internal/telephony/cdma/SmsMessage.java +++ b/com/android/internal/telephony/cdma/SmsMessage.java @@ -161,7 +161,7 @@ public class SmsMessage extends SmsMessageBase { // Second byte is the MSG_LEN, length of the message // See 3GPP2 C.S0023 3.4.27 - int size = data[1]; + int size = data[1] & 0xFF; // Note: Data may include trailing FF's. That's OK; message // should still parse correctly. diff --git a/com/android/internal/telephony/dataconnection/DcTracker.java b/com/android/internal/telephony/dataconnection/DcTracker.java index f9b00178..fb756cd9 100644 --- a/com/android/internal/telephony/dataconnection/DcTracker.java +++ b/com/android/internal/telephony/dataconnection/DcTracker.java @@ -149,8 +149,6 @@ public class DcTracker extends Handler { private static final boolean DATA_STALL_SUSPECTED = true; private static final boolean DATA_STALL_NOT_SUSPECTED = false; - private String RADIO_RESET_PROPERTY = "gsm.radioreset"; - private static final String INTENT_RECONNECT_ALARM = "com.android.internal.telephony.data-reconnect"; private static final String INTENT_RECONNECT_ALARM_EXTRA_TYPE = "reconnect_alarm_extra_type"; @@ -2246,7 +2244,7 @@ public class DcTracker extends Handler { intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); // Get current sub id. - int subId = SubscriptionManager.getDefaultDataSubscriptionId(); + int subId = mPhone.getSubId(); intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); if (DBG) { @@ -4546,13 +4544,11 @@ public class DcTracker extends Handler { public static final int CLEANUP = 1; public static final int REREGISTER = 2; public static final int RADIO_RESTART = 3; - public static final int RADIO_RESTART_WITH_PROP = 4; private static boolean isAggressiveRecovery(int value) { return ((value == RecoveryAction.CLEANUP) || (value == RecoveryAction.REREGISTER) || - (value == RecoveryAction.RADIO_RESTART) || - (value == RecoveryAction.RADIO_RESTART_WITH_PROP)); + (value == RecoveryAction.RADIO_RESTART)); } } @@ -4598,23 +4594,6 @@ public class DcTracker extends Handler { EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART, mSentSinceLastRecv); if (DBG) log("restarting radio"); - putRecoveryAction(RecoveryAction.RADIO_RESTART_WITH_PROP); - restartRadio(); - break; - case RecoveryAction.RADIO_RESTART_WITH_PROP: - // This is in case radio restart has not recovered the data. - // It will set an additional "gsm.radioreset" property to tell - // RIL or system to take further action. - // The implementation of hard reset recovery action is up to OEM product. - // Once RADIO_RESET property is consumed, it is expected to set back - // to false by RIL. - EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART_WITH_PROP, -1); - if (DBG) log("restarting radio with gsm.radioreset to true"); - SystemProperties.set(RADIO_RESET_PROPERTY, "true"); - // give 1 sec so property change can be notified. - try { - Thread.sleep(1000); - } catch (InterruptedException e) {} restartRadio(); putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST); break; diff --git a/com/android/internal/telephony/gsm/GsmSmsAddress.java b/com/android/internal/telephony/gsm/GsmSmsAddress.java index 2fbf7ed5..bd8c83e6 100644 --- a/com/android/internal/telephony/gsm/GsmSmsAddress.java +++ b/com/android/internal/telephony/gsm/GsmSmsAddress.java @@ -17,6 +17,7 @@ package com.android.internal.telephony.gsm; import android.telephony.PhoneNumberUtils; + import java.text.ParseException; import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.SmsAddress; @@ -71,8 +72,11 @@ public class GsmSmsAddress extends SmsAddress { // Make sure the final unused BCD digit is 0xf origBytes[length - 1] |= 0xf0; } - address = PhoneNumberUtils.calledPartyBCDToString(origBytes, - OFFSET_TOA, length - OFFSET_TOA); + address = PhoneNumberUtils.calledPartyBCDToString( + origBytes, + OFFSET_TOA, + length - OFFSET_TOA, + PhoneNumberUtils.BCD_EXTENDED_TYPE_CALLED_PARTY); // And restore origBytes origBytes[length - 1] = lastByte; diff --git a/com/android/internal/telephony/gsm/SmsMessage.java b/com/android/internal/telephony/gsm/SmsMessage.java index d4098d94..1ca19e01 100644 --- a/com/android/internal/telephony/gsm/SmsMessage.java +++ b/com/android/internal/telephony/gsm/SmsMessage.java @@ -535,8 +535,8 @@ public class SmsMessage extends SmsMessageBase { } else { // SC address try { - ret = PhoneNumberUtils - .calledPartyBCDToString(mPdu, mCur, len); + ret = PhoneNumberUtils.calledPartyBCDToString( + mPdu, mCur, len, PhoneNumberUtils.BCD_EXTENDED_TYPE_CALLED_PARTY); } catch (RuntimeException tr) { Rlog.d(LOG_TAG, "invalid SC address: ", tr); ret = null; diff --git a/com/android/internal/telephony/imsphone/ImsPhone.java b/com/android/internal/telephony/imsphone/ImsPhone.java index 45dc0b27..03d83dfc 100644 --- a/com/android/internal/telephony/imsphone/ImsPhone.java +++ b/com/android/internal/telephony/imsphone/ImsPhone.java @@ -1038,6 +1038,9 @@ public class ImsPhone extends ImsPhoneBase { break; case ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE: error = CommandException.Error.RADIO_NOT_AVAILABLE; + break; + case ImsReasonInfo.CODE_FDN_BLOCKED: + error = CommandException.Error.FDN_CHECK_FAILURE; default: break; } diff --git a/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java index ab30878b..f837b563 100644 --- a/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java +++ b/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java @@ -2566,7 +2566,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN; if (isHandoverFromWifi && imsCall.isVideoCall()) { - if (mNotifyHandoverVideoFromWifiToLTE) { + if (mNotifyHandoverVideoFromWifiToLTE && mIsDataEnabled) { log("onCallHandover :: notifying of WIFI to LTE handover."); conn.onConnectionEvent( TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE, null); @@ -2575,7 +2575,7 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { if (!mIsDataEnabled && mIsViLteDataMetered) { // Call was downgraded from WIFI to LTE and data is metered; downgrade the // call now. - downgradeVideoCall(ImsReasonInfo.CODE_DATA_DISABLED, conn); + downgradeVideoCall(ImsReasonInfo.CODE_WIFI_LOST, conn); } } } else { @@ -3535,8 +3535,9 @@ public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { // If the carrier supports downgrading to voice, then we can simply issue a // downgrade to voice instead of terminating the call. modifyVideoCall(imsCall, VideoProfile.STATE_AUDIO_ONLY); - } else if (mSupportPauseVideo) { - // The carrier supports video pause signalling, so pause the video. + } else if (mSupportPauseVideo && reasonCode != ImsReasonInfo.CODE_WIFI_LOST) { + // The carrier supports video pause signalling, so pause the video if we didn't just + // lose wifi; in that case just disconnect. mShouldUpdateImsConfigOnDisconnect = true; conn.pauseVideo(VideoPauseTracker.SOURCE_DATA_ENABLED); } else { diff --git a/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java index 4e3957ed..9c99055c 100644 --- a/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java +++ b/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java @@ -40,6 +40,7 @@ import android.text.SpannableStringBuilder; import android.text.TextUtils; import com.android.ims.ImsException; +import com.android.ims.ImsReasonInfo; import com.android.ims.ImsSsInfo; import com.android.ims.ImsUtInterface; import com.android.internal.telephony.CallForwardInfo; @@ -1172,6 +1173,14 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode { } private CharSequence getErrorMessage(AsyncResult ar) { + if (ar.exception instanceof CommandException) { + CommandException.Error err = ((CommandException) (ar.exception)).getCommandError(); + if (err == CommandException.Error.FDN_CHECK_FAILURE) { + Rlog.i(LOG_TAG, "FDN_CHECK_FAILURE"); + return mContext.getText(com.android.internal.R.string.mmiFdnError); + } + } + return mContext.getText(com.android.internal.R.string.mmiError); } @@ -1216,18 +1225,15 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode { if (err.getCommandError() == CommandException.Error.PASSWORD_INCORRECT) { sb.append(mContext.getText( com.android.internal.R.string.passwordIncorrect)); + } else if (err.getCommandError() == CommandException.Error.FDN_CHECK_FAILURE) { + sb.append(mContext.getText(com.android.internal.R.string.mmiFdnError)); } else if (err.getMessage() != null) { sb.append(err.getMessage()); } else { sb.append(mContext.getText(com.android.internal.R.string.mmiError)); } - } else { - ImsException error = (ImsException) ar.exception; - if (error.getMessage() != null) { - sb.append(error.getMessage()); - } else { - sb.append(getErrorMessage(ar)); - } + } else if (ar.exception instanceof ImsException) { + sb.append(getImsErrorMessage(ar)); } } else if (isActivate()) { mState = State.COMPLETE; @@ -1360,12 +1366,7 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode { mState = State.FAILED; if (ar.exception instanceof ImsException) { - ImsException error = (ImsException) ar.exception; - if (error.getMessage() != null) { - sb.append(error.getMessage()); - } else { - sb.append(getErrorMessage(ar)); - } + sb.append(getImsErrorMessage(ar)); } else { sb.append(getErrorMessage(ar)); @@ -1421,21 +1422,14 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode { StringBuilder sb = new StringBuilder(getScString()); sb.append("\n"); + mState = State.FAILED; if (ar.exception != null) { - mState = State.FAILED; - if (ar.exception instanceof ImsException) { - ImsException error = (ImsException) ar.exception; - if (error.getMessage() != null) { - sb.append(error.getMessage()); - } else { - sb.append(getErrorMessage(ar)); - } + sb.append(getImsErrorMessage(ar)); } else { sb.append(getErrorMessage(ar)); } } else { - mState = State.FAILED; ImsSsInfo ssInfo = null; if (ar.result instanceof Bundle) { Rlog.d(LOG_TAG, "onSuppSvcQueryComplete: Received CLIP/COLP/COLR Response."); @@ -1486,12 +1480,7 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode { mState = State.FAILED; if (ar.exception instanceof ImsException) { - ImsException error = (ImsException) ar.exception; - if (error.getMessage() != null) { - sb.append(error.getMessage()); - } else { - sb.append(getErrorMessage(ar)); - } + sb.append(getImsErrorMessage(ar)); } else { sb.append(getErrorMessage(ar)); } @@ -1525,14 +1514,8 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode { mState = State.FAILED; if (ar.exception != null) { - if (ar.exception instanceof ImsException) { - ImsException error = (ImsException) ar.exception; - if (error.getMessage() != null) { - sb.append(error.getMessage()); - } else { - sb.append(getErrorMessage(ar)); - } + sb.append(getImsErrorMessage(ar)); } } else { Bundle ssInfo = (Bundle) ar.result; @@ -1623,12 +1606,7 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode { mState = State.FAILED; if (ar.exception instanceof ImsException) { - ImsException error = (ImsException) ar.exception; - if (error.getMessage() != null) { - sb.append(error.getMessage()); - } else { - sb.append(getErrorMessage(ar)); - } + sb.append(getImsErrorMessage(ar)); } else { sb.append(getErrorMessage(ar)); } @@ -1676,6 +1654,17 @@ public final class ImsPhoneMmiCode extends Handler implements MmiCode { return sb; } + private CharSequence getImsErrorMessage(AsyncResult ar) { + ImsException error = (ImsException) ar.exception; + if (error.getCode() == ImsReasonInfo.CODE_FDN_BLOCKED) { + return mContext.getText(com.android.internal.R.string.mmiFdnError); + } else if (error.getMessage() != null) { + return error.getMessage(); + } else { + return getErrorMessage(ar); + } + } + @Override public ResultReceiver getUssdCallbackReceiver() { return this.mCallbackReceiver; diff --git a/com/android/internal/telephony/uicc/AdnRecord.java b/com/android/internal/telephony/uicc/AdnRecord.java index 203236ca..4414cafb 100644 --- a/com/android/internal/telephony/uicc/AdnRecord.java +++ b/com/android/internal/telephony/uicc/AdnRecord.java @@ -19,8 +19,8 @@ package com.android.internal.telephony.uicc; import android.os.Parcel; import android.os.Parcelable; import android.telephony.PhoneNumberUtils; -import android.text.TextUtils; import android.telephony.Rlog; +import android.text.TextUtils; import com.android.internal.telephony.GsmAlphabet; @@ -248,7 +248,8 @@ public class AdnRecord implements Parcelable { Rlog.w(LOG_TAG, "[buildAdnString] Max length of tag is " + footerOffset); return null; } else { - bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(mNumber); + bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD( + mNumber, PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN); System.arraycopy(bcdNumber, 0, adnString, footerOffset + ADN_TON_AND_NPI, bcdNumber.length); @@ -289,7 +290,10 @@ public class AdnRecord implements Parcelable { } mNumber += PhoneNumberUtils.calledPartyBCDFragmentToString( - extRecord, 2, 0xff & extRecord[1]); + extRecord, + 2, + 0xff & extRecord[1], + PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN); // We don't support ext record chaining. @@ -327,7 +331,10 @@ public class AdnRecord implements Parcelable { // the ME (see note 2)." mNumber = PhoneNumberUtils.calledPartyBCDToString( - record, footerOffset + 1, numberLength); + record, + footerOffset + 1, + numberLength, + PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN); mExtRecord = 0xff & record[record.length - 1]; diff --git a/com/android/internal/telephony/uicc/SIMRecords.java b/com/android/internal/telephony/uicc/SIMRecords.java index 724b4781..dad1ee2b 100644 --- a/com/android/internal/telephony/uicc/SIMRecords.java +++ b/com/android/internal/telephony/uicc/SIMRecords.java @@ -563,7 +563,8 @@ public class SIMRecords extends IccRecords { // Spec reference for EF_CFIS contents, TS 51.011 section 10.3.46. if (enable && !TextUtils.isEmpty(dialNumber)) { logv("EF_CFIS: updating cf number, " + Rlog.pii(LOG_TAG, dialNumber)); - byte[] bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(dialNumber); + byte[] bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD( + dialNumber, PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN); System.arraycopy(bcdNumber, 0, mEfCfis, CFIS_TON_NPI_OFFSET, bcdNumber.length); diff --git a/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java b/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java index e50f40c0..bfa458b8 100644 --- a/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java +++ b/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java @@ -54,7 +54,10 @@ public class UiccCarrierPrivilegeRules extends Handler { private static final String LOG_TAG = "UiccCarrierPrivilegeRules"; private static final boolean DBG = false; - private static final String AID = "A00000015141434C00"; + private static final String ARAM_AID = "A00000015141434C00"; + private static final String ARAD_AID = "A00000015144414300"; + private static final int ARAM = 1; + private static final int ARAD = 0; private static final int CLA = 0x80; private static final int COMMAND = 0xCA; private static final int P1 = 0xFF; @@ -184,18 +187,21 @@ public class UiccCarrierPrivilegeRules extends Handler { private String mStatusMessage; // Only used for debugging. private int mChannelId; // Channel Id for communicating with UICC. private int mRetryCount; // Number of retries for open logical channel. + private boolean mCheckedRules = false; // Flag that used to mark whether get rules from ARA-D. + private int mAIDInUse; // Message component to identify which AID is currently in-use. private final Runnable mRetryRunnable = new Runnable() { @Override public void run() { - openChannel(); + openChannel(mAIDInUse); } }; - private void openChannel() { + private void openChannel(int aidId) { // Send open logical channel request. + String aid = (aidId == ARAD) ? ARAD_AID : ARAM_AID; int p2 = 0x00; - mUiccCard.iccOpenLogicalChannel(AID, p2, /* supported p2 value */ - obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, null)); + mUiccCard.iccOpenLogicalChannel(aid, p2, /* supported p2 value */ + obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, 0, aidId, null)); } public UiccCarrierPrivilegeRules(UiccCard uiccCard, Message loadedCallback) { @@ -207,7 +213,9 @@ public class UiccCarrierPrivilegeRules extends Handler { mRules = ""; mAccessRules = new ArrayList<>(); - openChannel(); + // Open logical channel with ARA_D. + mAIDInUse = ARAD; + openChannel(mAIDInUse); } /** @@ -391,6 +399,7 @@ public class UiccCarrierPrivilegeRules extends Handler { @Override public void handleMessage(Message msg) { AsyncResult ar; + mAIDInUse = msg.arg2; // 0 means ARA-D and 1 means ARA-M. switch (msg.what) { @@ -400,23 +409,34 @@ public class UiccCarrierPrivilegeRules extends Handler { if (ar.exception == null && ar.result != null) { mChannelId = ((int[]) ar.result)[0]; mUiccCard.iccTransmitApduLogicalChannel(mChannelId, CLA, COMMAND, P1, P2, P3, - DATA, obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE, - mChannelId)); + DATA, obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE, mChannelId, + mAIDInUse)); } else { // MISSING_RESOURCE could be due to logical channels temporarily unavailable, // so we retry up to MAX_RETRY times, with an interval of RETRY_INTERVAL_MS. if (ar.exception instanceof CommandException && mRetryCount < MAX_RETRY && ((CommandException) (ar.exception)).getCommandError() - == CommandException.Error.MISSING_RESOURCE) { + == CommandException.Error.MISSING_RESOURCE) { mRetryCount++; removeCallbacks(mRetryRunnable); postDelayed(mRetryRunnable, RETRY_INTERVAL_MS); } else { - // if rules cannot be read from ARA applet, - // fallback to PKCS15-based ARF. - log("No ARA, try ARF next."); - mUiccPkcs15 = new UiccPkcs15(mUiccCard, - obtainMessage(EVENT_PKCS15_READ_DONE)); + if (mAIDInUse == ARAD) { + // Open logical channel with ARA_M. + mRules = ""; + openChannel(1); + } + if (mAIDInUse == ARAM) { + if (mCheckedRules) { + updateState(STATE_LOADED, "Success!"); + } else { + // if rules cannot be read from both ARA_D and ARA_M applet, + // fallback to PKCS15-based ARF. + log("No ARA, try ARF next."); + mUiccPkcs15 = new UiccPkcs15(mUiccCard, + obtainMessage(EVENT_PKCS15_READ_DONE)); + } + } } } break; @@ -432,34 +452,49 @@ public class UiccCarrierPrivilegeRules extends Handler { mRules += IccUtils.bytesToHexString(response.payload) .toUpperCase(Locale.US); if (isDataComplete()) { - mAccessRules = parseRules(mRules); - updateState(STATE_LOADED, "Success!"); + mAccessRules.addAll(parseRules(mRules)); + if (mAIDInUse == ARAD) { + mCheckedRules = true; + } else { + updateState(STATE_LOADED, "Success!"); + } } else { mUiccCard.iccTransmitApduLogicalChannel(mChannelId, CLA, COMMAND, P1, P2_EXTENDED_DATA, P3, DATA, obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE, - mChannelId)); + mChannelId, mAIDInUse)); break; } } catch (IllegalArgumentException | IndexOutOfBoundsException ex) { - updateState(STATE_ERROR, "Error parsing rules: " + ex); + if (mAIDInUse == ARAM) { + updateState(STATE_ERROR, "Error parsing rules: " + ex); + } } } else { - String errorMsg = "Invalid response: payload=" + response.payload - + " sw1=" + response.sw1 + " sw2=" + response.sw2; - updateState(STATE_ERROR, errorMsg); + if (mAIDInUse == ARAM) { + String errorMsg = "Invalid response: payload=" + response.payload + + " sw1=" + response.sw1 + " sw2=" + response.sw2; + updateState(STATE_ERROR, errorMsg); + } } } else { - updateState(STATE_ERROR, "Error reading value from SIM."); + if (mAIDInUse == ARAM) { + updateState(STATE_ERROR, "Error reading value from SIM."); + } } mUiccCard.iccCloseLogicalChannel(mChannelId, obtainMessage( - EVENT_CLOSE_LOGICAL_CHANNEL_DONE)); + EVENT_CLOSE_LOGICAL_CHANNEL_DONE, 0, mAIDInUse)); mChannelId = -1; break; case EVENT_CLOSE_LOGICAL_CHANNEL_DONE: log("EVENT_CLOSE_LOGICAL_CHANNEL_DONE"); + if (mAIDInUse == ARAD) { + // Close logical channel with ARA_D and then open logical channel with ARA_M. + mRules = ""; + openChannel(1); + } break; case EVENT_PKCS15_READ_DONE: @@ -492,7 +527,7 @@ public class UiccCarrierPrivilegeRules extends Handler { String lengthBytes = allRules.parseLength(mRules); log("isDataComplete lengthBytes: " + lengthBytes); if (mRules.length() == TAG_ALL_REF_AR_DO.length() + lengthBytes.length() + - allRules.length) { + allRules.length) { log("isDataComplete yes"); return true; } else { @@ -522,7 +557,7 @@ public class UiccCarrierPrivilegeRules extends Handler { if (accessRule != null) { accessRules.add(accessRule); } else { - Rlog.e(LOG_TAG, "Skip unrecognized rule." + refArDo.value); + Rlog.e(LOG_TAG, "Skip unrecognized rule." + refArDo.value); } } return accessRules; @@ -644,15 +679,15 @@ public class UiccCarrierPrivilegeRules extends Handler { * Converts state into human readable format. */ private String getStateString(int state) { - switch (state) { - case STATE_LOADING: - return "STATE_LOADING"; - case STATE_LOADED: - return "STATE_LOADED"; - case STATE_ERROR: - return "STATE_ERROR"; - default: - return "UNKNOWN"; - } + switch (state) { + case STATE_LOADING: + return "STATE_LOADING"; + case STATE_LOADED: + return "STATE_LOADED"; + case STATE_ERROR: + return "STATE_ERROR"; + default: + return "UNKNOWN"; + } } -} +}
\ No newline at end of file diff --git a/com/android/internal/util/BitUtils.java b/com/android/internal/util/BitUtils.java index 28f12ebf..ba80aeae 100644 --- a/com/android/internal/util/BitUtils.java +++ b/com/android/internal/util/BitUtils.java @@ -93,6 +93,10 @@ public final class BitUtils { return s & 0xffff; } + public static int uint16(byte hi, byte lo) { + return ((hi & 0xff) << 8) | (lo & 0xff); + } + public static long uint32(int i) { return i & 0xffffffffL; } diff --git a/com/android/internal/util/RingBuffer.java b/com/android/internal/util/RingBuffer.java new file mode 100644 index 00000000..ad84353f --- /dev/null +++ b/com/android/internal/util/RingBuffer.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2017 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 static com.android.internal.util.Preconditions.checkArgumentPositive; + +import java.lang.reflect.Array; +import java.util.Arrays; + +/** + * A simple ring buffer structure with bounded capacity backed by an array. + * Events can always be added at the logical end of the buffer. If the buffer is + * full, oldest events are dropped when new events are added. + * {@hide} + */ +public class RingBuffer<T> { + + // Array for storing events. + private final T[] mBuffer; + // Cursor keeping track of the logical end of the array. This cursor never + // wraps and instead keeps track of the total number of append() operations. + private long mCursor = 0; + + public RingBuffer(Class<T> c, int capacity) { + checkArgumentPositive(capacity, "A RingBuffer cannot have 0 capacity"); + // Java cannot create generic arrays without a runtime hint. + mBuffer = (T[]) Array.newInstance(c, capacity); + } + + public int size() { + return (int) Math.min(mBuffer.length, (long) mCursor); + } + + public void append(T t) { + mBuffer[indexOf(mCursor++)] = t; + } + + public T[] toArray() { + // Only generic way to create a T[] from another T[] + T[] out = Arrays.copyOf(mBuffer, size(), (Class<T[]>) mBuffer.getClass()); + // Reverse iteration from youngest event to oldest event. + long inCursor = mCursor - 1; + int outIdx = out.length - 1; + while (outIdx >= 0) { + out[outIdx--] = (T) mBuffer[indexOf(inCursor--)]; + } + return out; + } + + private int indexOf(long cursor) { + return (int) Math.abs(cursor % mBuffer.length); + } +} diff --git a/com/android/internal/widget/LinearLayoutManager.java b/com/android/internal/widget/LinearLayoutManager.java index d82c7466..0000a74f 100644 --- a/com/android/internal/widget/LinearLayoutManager.java +++ b/com/android/internal/widget/LinearLayoutManager.java @@ -168,10 +168,6 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements /** * Constructor used when layout manager is set in XML by RecyclerView attribute * "layoutManager". Defaults to vertical orientation. - * - * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation - * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout - * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd */ public LinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { diff --git a/com/android/internal/widget/LockPatternUtils.java b/com/android/internal/widget/LockPatternUtils.java index b8ef82b8..4be6b28a 100644 --- a/com/android/internal/widget/LockPatternUtils.java +++ b/com/android/internal/widget/LockPatternUtils.java @@ -303,7 +303,7 @@ public class LockPatternUtils { } public void reportFailedPasswordAttempt(int userId) { - if (userId == USER_FRP && frpCredentialEnabled()) { + if (userId == USER_FRP && frpCredentialEnabled(mContext)) { return; } getDevicePolicyManager().reportFailedPasswordAttempt(userId); @@ -311,7 +311,7 @@ public class LockPatternUtils { } public void reportSuccessfulPasswordAttempt(int userId) { - if (userId == USER_FRP && frpCredentialEnabled()) { + if (userId == USER_FRP && frpCredentialEnabled(mContext)) { return; } getDevicePolicyManager().reportSuccessfulPasswordAttempt(userId); @@ -319,21 +319,21 @@ public class LockPatternUtils { } public void reportPasswordLockout(int timeoutMs, int userId) { - if (userId == USER_FRP && frpCredentialEnabled()) { + if (userId == USER_FRP && frpCredentialEnabled(mContext)) { return; } getTrustManager().reportUnlockLockout(timeoutMs, userId); } public int getCurrentFailedPasswordAttempts(int userId) { - if (userId == USER_FRP && frpCredentialEnabled()) { + if (userId == USER_FRP && frpCredentialEnabled(mContext)) { return 0; } return getDevicePolicyManager().getCurrentFailedPasswordAttempts(userId); } public int getMaximumFailedPasswordsForWipe(int userId) { - if (userId == USER_FRP && frpCredentialEnabled()) { + if (userId == USER_FRP && frpCredentialEnabled(mContext)) { return 0; } return getDevicePolicyManager().getMaximumFailedPasswordsForWipe( @@ -1774,11 +1774,12 @@ public class LockPatternUtils { return getLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 0, UserHandle.USER_SYSTEM) != 0; } - public static boolean userOwnsFrpCredential(UserInfo info) { - return info != null && info.isPrimary() && info.isAdmin() && frpCredentialEnabled(); + public static boolean userOwnsFrpCredential(Context context, UserInfo info) { + return info != null && info.isPrimary() && info.isAdmin() && frpCredentialEnabled(context); } - public static boolean frpCredentialEnabled() { - return FRP_CREDENTIAL_ENABLED; + public static boolean frpCredentialEnabled(Context context) { + return FRP_CREDENTIAL_ENABLED && context.getResources().getBoolean( + com.android.internal.R.bool.config_enableCredentialFactoryResetProtection); } } diff --git a/com/android/internal/widget/Magnifier.java b/com/android/internal/widget/Magnifier.java new file mode 100644 index 00000000..86e7b38a --- /dev/null +++ b/com/android/internal/widget/Magnifier.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2017 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.widget; + +import android.annotation.FloatRange; +import android.annotation.NonNull; +import android.annotation.UiThread; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.Handler; +import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.PixelCopy; +import android.view.View; +import android.view.ViewRootImpl; +import android.widget.ImageView; +import android.widget.PopupWindow; + +import com.android.internal.R; +import com.android.internal.util.Preconditions; + +/** + * Android magnifier widget. Can be used by any view which is attached to window. + */ +public final class Magnifier { + private static final String LOG_TAG = "magnifier"; + // The view for which this magnifier is attached. + private final View mView; + // The window containing the magnifier. + private final PopupWindow mWindow; + // The center coordinates of the window containing the magnifier. + private final Point mWindowCoords = new Point(); + // The width of the window containing the magnifier. + private final int mWindowWidth; + // The height of the window containing the magnifier. + private final int mWindowHeight; + // The bitmap used to display the contents of the magnifier. + private final Bitmap mBitmap; + // The center coordinates of the content that is to be magnified. + private final Point mCenterZoomCoords = new Point(); + // The callback of the pixel copy request will be invoked on this Handler when + // the copy is finished. + private final Handler mPixelCopyHandler = Handler.getMain(); + + /** + * Initializes a magnifier. + * + * @param view the view for which this magnifier is attached + */ + @UiThread + public Magnifier(@NonNull View view) { + mView = Preconditions.checkNotNull(view); + final Context context = mView.getContext(); + final View content = LayoutInflater.from(context).inflate(R.layout.magnifier, null); + mWindowWidth = context.getResources().getDimensionPixelSize(R.dimen.magnifier_width); + mWindowHeight = context.getResources().getDimensionPixelSize(R.dimen.magnifier_height); + final float elevation = context.getResources().getDimension(R.dimen.magnifier_elevation); + + mWindow = new PopupWindow(context); + mWindow.setContentView(content); + mWindow.setWidth(mWindowWidth); + mWindow.setHeight(mWindowHeight); + mWindow.setElevation(elevation); + mWindow.setTouchable(false); + mWindow.setBackgroundDrawable(null); + + mBitmap = Bitmap.createBitmap(mWindowWidth, mWindowHeight, Bitmap.Config.ARGB_8888); + getImageView().setImageBitmap(mBitmap); + } + + /** + * Shows the magnifier on the screen. + * + * @param centerXOnScreen horizontal coordinate of the center point of the magnifier source + * @param centerYOnScreen vertical coordinate of the center point of the magnifier source + * @param scale the scale at which the magnifier zooms on the source content + */ + public void show(@FloatRange(from=0) float centerXOnScreen, + @FloatRange(from=0) float centerYOnScreen, + @FloatRange(from=1, to=10) float scale) { + maybeResizeBitmap(scale); + configureCoordinates(centerXOnScreen, centerYOnScreen); + performPixelCopy(); + + if (mWindow.isShowing()) { + mWindow.update(mWindowCoords.x, mWindowCoords.y, mWindow.getWidth(), + mWindow.getHeight()); + } else { + mWindow.showAtLocation(mView.getRootView(), Gravity.NO_GRAVITY, + mWindowCoords.x, mWindowCoords.y); + } + } + + /** + * Dismisses the magnifier from the screen. + */ + public void dismiss() { + mWindow.dismiss(); + } + + /** + * @return the height of the magnifier window. + */ + public int getHeight() { + return mWindowHeight; + } + + /** + * @return the width of the magnifier window. + */ + public int getWidth() { + return mWindowWidth; + } + + private void maybeResizeBitmap(float scale) { + final int bitmapWidth = (int) (mWindowWidth / scale); + final int bitmapHeight = (int) (mWindowHeight / scale); + if (mBitmap.getWidth() != bitmapWidth || mBitmap.getHeight() != bitmapHeight) { + mBitmap.reconfigure(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888); + getImageView().setImageBitmap(mBitmap); + } + } + + private void configureCoordinates(float posXOnScreen, float posYOnScreen) { + mCenterZoomCoords.x = (int) posXOnScreen; + mCenterZoomCoords.y = (int) posYOnScreen; + + final int verticalMagnifierOffset = mView.getContext().getResources().getDimensionPixelSize( + R.dimen.magnifier_offset); + final int availableTopSpace = (mCenterZoomCoords.y - mWindowHeight / 2) + - verticalMagnifierOffset - (mBitmap.getHeight() / 2); + + mWindowCoords.x = mCenterZoomCoords.x - mWindowWidth / 2; + mWindowCoords.y = mCenterZoomCoords.y - mWindowHeight / 2 + + verticalMagnifierOffset * (availableTopSpace > 0 ? -1 : 1); + } + + private void performPixelCopy() { + int startX = mCenterZoomCoords.x - mBitmap.getWidth() / 2; + // Clamp startX value to avoid distorting the rendering of the magnifier content. + if (startX < 0) { + startX = 0; + } else if (startX + mBitmap.getWidth() > mView.getWidth()) { + startX = mView.getWidth() - mBitmap.getWidth(); + } + + final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2; + final ViewRootImpl viewRootImpl = mView.getViewRootImpl(); + + if (viewRootImpl != null && viewRootImpl.mSurface != null + && viewRootImpl.mSurface.isValid()) { + PixelCopy.request( + viewRootImpl.mSurface, + new Rect(startX, startY, startX + mBitmap.getWidth(), + startY + mBitmap.getHeight()), + mBitmap, + result -> getImageView().invalidate(), + mPixelCopyHandler); + } else { + Log.d(LOG_TAG, "Could not perform PixelCopy request"); + } + } + + private ImageView getImageView() { + return mWindow.getContentView().findViewById(R.id.magnifier_image); + } +} diff --git a/com/android/keyguard/KeyguardDisplayManager.java b/com/android/keyguard/KeyguardDisplayManager.java index 8de1d317..2bc0e45c 100644 --- a/com/android/keyguard/KeyguardDisplayManager.java +++ b/com/android/keyguard/KeyguardDisplayManager.java @@ -15,6 +15,8 @@ */ package com.android.keyguard; +import static android.view.Display.INVALID_DISPLAY; + import android.app.Presentation; import android.content.Context; import android.content.DialogInterface; @@ -28,16 +30,21 @@ import android.view.Display; import android.view.View; import android.view.WindowManager; +// TODO(multi-display): Support multiple external displays public class KeyguardDisplayManager { protected static final String TAG = "KeyguardDisplayManager"; private static boolean DEBUG = KeyguardConstants.DEBUG; + + private final ViewMediatorCallback mCallback; + private final MediaRouter mMediaRouter; + private final Context mContext; + Presentation mPresentation; - private MediaRouter mMediaRouter; - private Context mContext; private boolean mShowing; - public KeyguardDisplayManager(Context context) { + public KeyguardDisplayManager(Context context, ViewMediatorCallback callback) { mContext = context; + mCallback = callback; mMediaRouter = (MediaRouter) mContext.getSystemService(Context.MEDIA_ROUTER_SERVICE); } @@ -90,6 +97,7 @@ public class KeyguardDisplayManager { }; protected void updateDisplays(boolean showing) { + Presentation originalPresentation = mPresentation; if (showing) { MediaRouter.RouteInfo route = mMediaRouter.getSelectedRoute( MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY); @@ -121,6 +129,13 @@ public class KeyguardDisplayManager { mPresentation = null; } } + + // mPresentation is only updated when the display changes + if (mPresentation != originalPresentation) { + final int displayId = mPresentation != null + ? mPresentation.getDisplay().getDisplayId() : INVALID_DISPLAY; + mCallback.onSecondaryDisplayShowingChanged(displayId); + } } private final static class KeyguardPresentation extends Presentation { diff --git a/com/android/keyguard/KeyguardSimPinView.java b/com/android/keyguard/KeyguardSimPinView.java index 7225ba99..432b4061 100644 --- a/com/android/keyguard/KeyguardSimPinView.java +++ b/com/android/keyguard/KeyguardSimPinView.java @@ -66,7 +66,11 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { // again when the PUK locked SIM is re-entered. case ABSENT: { KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(mSubId); - mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); + // onSimStateChanged callback can fire when the SIM PIN lock is not currently + // active and mCallback is null. + if (mCallback != null) { + mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); + } break; } default: diff --git a/com/android/keyguard/KeyguardSimPukView.java b/com/android/keyguard/KeyguardSimPukView.java index 171cf236..7f79008b 100644 --- a/com/android/keyguard/KeyguardSimPukView.java +++ b/com/android/keyguard/KeyguardSimPukView.java @@ -72,7 +72,11 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { // move into the READY state and the PUK lock keyguard should be removed. case READY: { KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(mSubId); - mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); + // mCallback can be null if onSimStateChanged callback is called when keyguard + // isn't active. + if (mCallback != null) { + mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); + } break; } default: diff --git a/com/android/keyguard/KeyguardUpdateMonitor.java b/com/android/keyguard/KeyguardUpdateMonitor.java index d95402cd..d83a6c60 100644 --- a/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/com/android/keyguard/KeyguardUpdateMonitor.java @@ -16,6 +16,8 @@ package com.android.keyguard; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.ACTION_USER_UNLOCKED; import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN; import static android.os.BatteryManager.BATTERY_STATUS_FULL; @@ -452,6 +454,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { */ public void setKeyguardGoingAway(boolean goingAway) { mKeyguardGoingAway = goingAway; + updateFingerprintListeningState(); } /** @@ -1069,6 +1072,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { cb.onDreamingStateChanged(mIsDreaming); } } + updateFingerprintListeningState(); } /** @@ -1772,7 +1776,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { public void onTaskStackChangedBackground() { try { ActivityManager.StackInfo info = ActivityManager.getService().getStackInfo( - ActivityManager.StackId.ASSISTANT_STACK_ID); + WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT); if (info == null) { return; } diff --git a/com/android/keyguard/ViewMediatorCallback.java b/com/android/keyguard/ViewMediatorCallback.java index 327d2189..b194de43 100644 --- a/com/android/keyguard/ViewMediatorCallback.java +++ b/com/android/keyguard/ViewMediatorCallback.java @@ -88,4 +88,9 @@ public interface ViewMediatorCallback { * {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}. */ int getBouncerPromptReason(); + + /** + * Invoked when the secondary display showing a keyguard window changes. + */ + void onSecondaryDisplayShowingChanged(int displayId); } diff --git a/com/android/layoutlib/bridge/Bridge.java b/com/android/layoutlib/bridge/Bridge.java index 5dca8e7f..0cfc1811 100644 --- a/com/android/layoutlib/bridge/Bridge.java +++ b/com/android/layoutlib/bridge/Bridge.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * 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. @@ -14,659 +14,62 @@ * limitations under the License. */ -package com.android.layoutlib.bridge; - -import com.android.ide.common.rendering.api.Capability; -import com.android.ide.common.rendering.api.DrawableParams; -import com.android.ide.common.rendering.api.Features; -import com.android.ide.common.rendering.api.LayoutLog; -import com.android.ide.common.rendering.api.RenderSession; +package com.android.layoutlib.bridge;import com.android.ide.common.rendering.api.RenderSession; import com.android.ide.common.rendering.api.Result; import com.android.ide.common.rendering.api.Result.Status; import com.android.ide.common.rendering.api.SessionParams; -import com.android.layoutlib.bridge.android.RenderParamsFlags; -import com.android.layoutlib.bridge.impl.RenderDrawable; -import com.android.layoutlib.bridge.impl.RenderSessionImpl; -import com.android.layoutlib.bridge.util.DynamicIdMap; -import com.android.ninepatch.NinePatchChunk; -import com.android.resources.ResourceType; -import com.android.tools.layoutlib.create.MethodAdapter; -import com.android.tools.layoutlib.create.OverrideMethod; -import com.android.util.Pair; - -import android.annotation.NonNull; -import android.content.res.BridgeAssetManager; -import android.graphics.Bitmap; -import android.graphics.FontFamily_Delegate; -import android.graphics.Typeface; -import android.graphics.Typeface_Delegate; -import android.icu.util.ULocale; -import android.os.Looper; -import android.os.Looper_Accessor; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewParent; - -import java.io.File; -import java.lang.ref.SoftReference; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.Arrays; -import java.util.EnumMap; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; -import java.util.WeakHashMap; -import java.util.concurrent.locks.ReentrantLock; -import libcore.io.MemoryMappedFile_Delegate; - -import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; /** - * Main entry point of the LayoutLib Bridge. - * <p/>To use this bridge, simply instantiate an object of type {@link Bridge} and call - * {@link #createSession(SessionParams)} + * Legacy Bridge used in the SDK version of layoutlib */ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { + private static final String SDK_NOT_SUPPORTED = "The SDK layoutlib version is not supported"; + private static final Result NOT_SUPPORTED_RESULT = + Status.NOT_IMPLEMENTED.createResult(SDK_NOT_SUPPORTED); + private static BufferedImage sImage; - private static final String ICU_LOCALE_DIRECTION_RTL = "right-to-left"; + private static class BridgeRenderSession extends RenderSession { - public static class StaticMethodNotImplementedException extends RuntimeException { - private static final long serialVersionUID = 1L; + @Override + public synchronized BufferedImage getImage() { + if (sImage == null) { + sImage = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = sImage.createGraphics(); + g.clearRect(0, 0, 500, 500); + g.drawString(SDK_NOT_SUPPORTED, 20, 20); + g.dispose(); + } - public StaticMethodNotImplementedException(String msg) { - super(msg); + return sImage; } - } - - /** - * Lock to ensure only one rendering/inflating happens at a time. - * This is due to some singleton in the Android framework. - */ - private final static ReentrantLock sLock = new ReentrantLock(); - - /** - * Maps from id to resource type/name. This is for com.android.internal.R - */ - @SuppressWarnings("deprecation") - private final static Map<Integer, Pair<ResourceType, String>> sRMap = new HashMap<>(); - /** - * Reverse map compared to sRMap, resource type -> (resource name -> id). - * This is for com.android.internal.R. - */ - private final static Map<ResourceType, Map<String, Integer>> sRevRMap = new EnumMap<>(ResourceType.class); - - // framework resources are defined as 0x01XX#### where XX is the resource type (layout, - // drawable, etc...). Using FF as the type allows for 255 resource types before we get a - // collision which should be fine. - private final static int DYNAMIC_ID_SEED_START = 0x01ff0000; - private final static DynamicIdMap sDynamicIds = new DynamicIdMap(DYNAMIC_ID_SEED_START); - - private final static Map<Object, Map<String, SoftReference<Bitmap>>> sProjectBitmapCache = - new WeakHashMap<>(); - private final static Map<Object, Map<String, SoftReference<NinePatchChunk>>> sProject9PatchCache = - new WeakHashMap<>(); - - private final static Map<String, SoftReference<Bitmap>> sFrameworkBitmapCache = new HashMap<>(); - private final static Map<String, SoftReference<NinePatchChunk>> sFramework9PatchCache = - new HashMap<>(); - - private static Map<String, Map<String, Integer>> sEnumValueMap; - private static Map<String, String> sPlatformProperties; - - /** - * A default log than prints to stdout/stderr. - */ - private final static LayoutLog sDefaultLog = new LayoutLog() { @Override - public void error(String tag, String message, Object data) { - System.err.println(message); + public Result render(long timeout, boolean forceMeasure) { + return NOT_SUPPORTED_RESULT; } @Override - public void error(String tag, String message, Throwable throwable, Object data) { - System.err.println(message); + public Result measure(long timeout) { + return NOT_SUPPORTED_RESULT; } @Override - public void warning(String tag, String message, Object data) { - System.out.println(message); - } - }; - - /** - * Current log. - */ - private static LayoutLog sCurrentLog = sDefaultLog; - - private static final int LAST_SUPPORTED_FEATURE = Features.THEME_PREVIEW_NAVIGATION_BAR; - - @Override - public int getApiLevel() { - return com.android.ide.common.rendering.api.Bridge.API_CURRENT; - } - - @SuppressWarnings("deprecation") - @Override - @Deprecated - public EnumSet<Capability> getCapabilities() { - // The Capability class is deprecated and frozen. All Capabilities enumerated there are - // supported by this version of LayoutLibrary. So, it's safe to use EnumSet.allOf() - return EnumSet.allOf(Capability.class); - } - - @Override - public boolean supports(int feature) { - return feature <= LAST_SUPPORTED_FEATURE; - } - - @Override - public boolean init(Map<String,String> platformProperties, - File fontLocation, - Map<String, Map<String, Integer>> enumValueMap, - LayoutLog log) { - sPlatformProperties = platformProperties; - sEnumValueMap = enumValueMap; - - BridgeAssetManager.initSystem(); - - // When DEBUG_LAYOUT is set and is not 0 or false, setup a default listener - // on static (native) methods which prints the signature on the console and - // throws an exception. - // This is useful when testing the rendering in ADT to identify static native - // methods that are ignored -- layoutlib_create makes them returns 0/false/null - // which is generally OK yet might be a problem, so this is how you'd find out. - // - // Currently layoutlib_create only overrides static native method. - // Static non-natives are not overridden and thus do not get here. - final String debug = System.getenv("DEBUG_LAYOUT"); - if (debug != null && !debug.equals("0") && !debug.equals("false")) { - - OverrideMethod.setDefaultListener(new MethodAdapter() { - @Override - public void onInvokeV(String signature, boolean isNative, Object caller) { - sDefaultLog.error(null, "Missing Stub: " + signature + - (isNative ? " (native)" : ""), null /*data*/); - - if (debug.equalsIgnoreCase("throw")) { - // Throwing this exception doesn't seem that useful. It breaks - // the layout editor yet doesn't display anything meaningful to the - // user. Having the error in the console is just as useful. We'll - // throw it only if the environment variable is "throw" or "THROW". - throw new StaticMethodNotImplementedException(signature); - } - } - }); - } - - // load the fonts. - FontFamily_Delegate.setFontLocation(fontLocation.getAbsolutePath()); - MemoryMappedFile_Delegate.setDataDir(fontLocation.getAbsoluteFile().getParentFile()); - - // now parse com.android.internal.R (and only this one as android.R is a subset of - // the internal version), and put the content in the maps. - try { - Class<?> r = com.android.internal.R.class; - // Parse the styleable class first, since it may contribute to attr values. - parseStyleable(); - - for (Class<?> inner : r.getDeclaredClasses()) { - if (inner == com.android.internal.R.styleable.class) { - // Already handled the styleable case. Not skipping attr, as there may be attrs - // that are not referenced from styleables. - continue; - } - String resTypeName = inner.getSimpleName(); - ResourceType resType = ResourceType.getEnum(resTypeName); - if (resType != null) { - Map<String, Integer> fullMap = null; - switch (resType) { - case ATTR: - fullMap = sRevRMap.get(ResourceType.ATTR); - break; - case STRING: - case STYLE: - // Slightly less than thousand entries in each. - fullMap = new HashMap<>(1280); - // no break. - default: - if (fullMap == null) { - fullMap = new HashMap<>(); - } - sRevRMap.put(resType, fullMap); - } - - for (Field f : inner.getDeclaredFields()) { - // only process static final fields. Since the final attribute may have - // been altered by layoutlib_create, we only check static - if (!isValidRField(f)) { - continue; - } - Class<?> type = f.getType(); - if (!type.isArray()) { - Integer value = (Integer) f.get(null); - //noinspection deprecation - sRMap.put(value, Pair.of(resType, f.getName())); - fullMap.put(f.getName(), value); - } - } - } - } - } catch (Exception throwable) { - if (log != null) { - log.error(LayoutLog.TAG_BROKEN, - "Failed to load com.android.internal.R from the layout library jar", - throwable, null); - } - return false; + public Result getResult() { + return NOT_SUPPORTED_RESULT; } - - return true; - } - - /** - * Tests if the field is pubic, static and one of int or int[]. - */ - private static boolean isValidRField(Field field) { - int modifiers = field.getModifiers(); - boolean isAcceptable = Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers); - Class<?> type = field.getType(); - return isAcceptable && type == int.class || - (type.isArray() && type.getComponentType() == int.class); - } - private static void parseStyleable() throws Exception { - // R.attr doesn't contain all the needed values. There are too many resources in the - // framework for all to be in the R class. Only the ones specified manually in - // res/values/symbols.xml are put in R class. Since, we need to create a map of all attr - // values, we try and find them from the styleables. - - // There were 1500 elements in this map at M timeframe. - Map<String, Integer> revRAttrMap = new HashMap<>(2048); - sRevRMap.put(ResourceType.ATTR, revRAttrMap); - // There were 2000 elements in this map at M timeframe. - Map<String, Integer> revRStyleableMap = new HashMap<>(3072); - sRevRMap.put(ResourceType.STYLEABLE, revRStyleableMap); - Class<?> c = com.android.internal.R.styleable.class; - Field[] fields = c.getDeclaredFields(); - // Sort the fields to bring all arrays to the beginning, so that indices into the array are - // able to refer back to the arrays (i.e. no forward references). - Arrays.sort(fields, (o1, o2) -> { - if (o1 == o2) { - return 0; - } - Class<?> t1 = o1.getType(); - Class<?> t2 = o2.getType(); - if (t1.isArray() && !t2.isArray()) { - return -1; - } else if (t2.isArray() && !t1.isArray()) { - return 1; - } - return o1.getName().compareTo(o2.getName()); - }); - Map<String, int[]> styleables = new HashMap<>(); - for (Field field : fields) { - if (!isValidRField(field)) { - // Only consider public static fields that are int or int[]. - // Don't check the final flag as it may have been modified by layoutlib_create. - continue; - } - String name = field.getName(); - if (field.getType().isArray()) { - int[] styleableValue = (int[]) field.get(null); - styleables.put(name, styleableValue); - continue; - } - // Not an array. - String arrayName = name; - int[] arrayValue = null; - int index; - while ((index = arrayName.lastIndexOf('_')) >= 0) { - // Find the name of the corresponding styleable. - // Search in reverse order so that attrs like LinearLayout_Layout_layout_gravity - // are mapped to LinearLayout_Layout and not to LinearLayout. - arrayName = arrayName.substring(0, index); - arrayValue = styleables.get(arrayName); - if (arrayValue != null) { - break; - } - } - index = (Integer) field.get(null); - if (arrayValue != null) { - String attrName = name.substring(arrayName.length() + 1); - int attrValue = arrayValue[index]; - //noinspection deprecation - sRMap.put(attrValue, Pair.of(ResourceType.ATTR, attrName)); - revRAttrMap.put(attrName, attrValue); - } - //noinspection deprecation - sRMap.put(index, Pair.of(ResourceType.STYLEABLE, name)); - revRStyleableMap.put(name, index); - } - } @Override - public boolean dispose() { - BridgeAssetManager.clearSystem(); - - // dispose of the default typeface. - Typeface_Delegate.resetDefaults(); - Typeface.sDynamicTypefaceCache.evictAll(); - sProject9PatchCache.clear(); - sProjectBitmapCache.clear(); - - return true; - } - - /** - * Starts a layout session by inflating and rendering it. The method returns a - * {@link RenderSession} on which further actions can be taken. - * <p/> - * If {@link SessionParams} includes the {@link RenderParamsFlags#FLAG_DO_NOT_RENDER_ON_CREATE}, - * this method will only inflate the layout but will NOT render it. - * @param params the {@link SessionParams} object with all the information necessary to create - * the scene. - * @return a new {@link RenderSession} object that contains the result of the layout. - * @since 5 - */ - @Override public RenderSession createSession(SessionParams params) { - try { - Result lastResult; - RenderSessionImpl scene = new RenderSessionImpl(params); - try { - prepareThread(); - lastResult = scene.init(params.getTimeout()); - if (lastResult.isSuccess()) { - lastResult = scene.inflate(); - - boolean doNotRenderOnCreate = Boolean.TRUE.equals( - params.getFlag(RenderParamsFlags.FLAG_DO_NOT_RENDER_ON_CREATE)); - if (lastResult.isSuccess() && !doNotRenderOnCreate) { - lastResult = scene.render(true /*freshRender*/); - } - } - } finally { - scene.release(); - cleanupThread(); - } - - return new BridgeRenderSession(scene, lastResult); - } catch (Throwable t) { - // get the real cause of the exception. - Throwable t2 = t; - while (t2.getCause() != null) { - t2 = t2.getCause(); - } - return new BridgeRenderSession(null, - ERROR_UNKNOWN.createResult(t2.getMessage(), t)); - } - } - - @Override - public Result renderDrawable(DrawableParams params) { - try { - Result lastResult; - RenderDrawable action = new RenderDrawable(params); - try { - prepareThread(); - lastResult = action.init(params.getTimeout()); - if (lastResult.isSuccess()) { - lastResult = action.render(); - } - } finally { - action.release(); - cleanupThread(); - } - - return lastResult; - } catch (Throwable t) { - // get the real cause of the exception. - Throwable t2 = t; - while (t2.getCause() != null) { - t2 = t.getCause(); - } - return ERROR_UNKNOWN.createResult(t2.getMessage(), t); - } + return new BridgeRenderSession(); } @Override - public void clearCaches(Object projectKey) { - if (projectKey != null) { - sProjectBitmapCache.remove(projectKey); - sProject9PatchCache.remove(projectKey); - } - } - - @Override - public Result getViewParent(Object viewObject) { - if (viewObject instanceof View) { - return Status.SUCCESS.createResult(((View)viewObject).getParent()); - } - - throw new IllegalArgumentException("viewObject is not a View"); - } - - @Override - public Result getViewIndex(Object viewObject) { - if (viewObject instanceof View) { - View view = (View) viewObject; - ViewParent parentView = view.getParent(); - - if (parentView instanceof ViewGroup) { - Status.SUCCESS.createResult(((ViewGroup) parentView).indexOfChild(view)); - } - - return Status.SUCCESS.createResult(); - } - - throw new IllegalArgumentException("viewObject is not a View"); - } - - @Override - public boolean isRtl(String locale) { - return isLocaleRtl(locale); - } - - public static boolean isLocaleRtl(String locale) { - if (locale == null) { - locale = ""; - } - ULocale uLocale = new ULocale(locale); - return uLocale.getCharacterOrientation().equals(ICU_LOCALE_DIRECTION_RTL); - } - - /** - * Returns the lock for the bridge - */ - public static ReentrantLock getLock() { - return sLock; - } - - /** - * Prepares the current thread for rendering. - * - * Note that while this can be called several time, the first call to {@link #cleanupThread()} - * will do the clean-up, and make the thread unable to do further scene actions. - */ - public synchronized static void prepareThread() { - // we need to make sure the Looper has been initialized for this thread. - // this is required for View that creates Handler objects. - if (Looper.myLooper() == null) { - Looper.prepareMainLooper(); - } - } - - /** - * Cleans up thread-specific data. After this, the thread cannot be used for scene actions. - * <p> - * Note that it doesn't matter how many times {@link #prepareThread()} was called, a single - * call to this will prevent the thread from doing further scene actions - */ - public synchronized static void cleanupThread() { - // clean up the looper - Looper_Accessor.cleanupThread(); - } - - public static LayoutLog getLog() { - return sCurrentLog; - } - - public static void setLog(LayoutLog log) { - // check only the thread currently owning the lock can do this. - if (!sLock.isHeldByCurrentThread()) { - throw new IllegalStateException("scene must be acquired first. see #acquire(long)"); - } - - if (log != null) { - sCurrentLog = log; - } else { - sCurrentLog = sDefaultLog; - } - } - - /** - * Returns details of a framework resource from its integer value. - * @param value the integer value - * @return a Pair containing the resource type and name, or null if the id - * does not match any resource. - */ - @SuppressWarnings("deprecation") - public static Pair<ResourceType, String> resolveResourceId(int value) { - Pair<ResourceType, String> pair = sRMap.get(value); - if (pair == null) { - pair = sDynamicIds.resolveId(value); - } - return pair; - } - - /** - * Returns the integer id of a framework resource, from a given resource type and resource name. - * <p/> - * If no resource is found, it creates a dynamic id for the resource. - * - * @param type the type of the resource - * @param name the name of the resource. - * - * @return an {@link Integer} containing the resource id. - */ - @NonNull - public static Integer getResourceId(ResourceType type, String name) { - Map<String, Integer> map = sRevRMap.get(type); - Integer value = null; - if (map != null) { - value = map.get(name); - } - - return value == null ? sDynamicIds.getId(type, name) : value; - - } - - /** - * Returns the list of possible enums for a given attribute name. - */ - public static Map<String, Integer> getEnumValues(String attributeName) { - if (sEnumValueMap != null) { - return sEnumValueMap.get(attributeName); - } - - return null; - } - - /** - * Returns the platform build properties. - */ - public static Map<String, String> getPlatformProperties() { - return sPlatformProperties; - } - - /** - * Returns the bitmap for a specific path, from a specific project cache, or from the - * framework cache. - * @param value the path of the bitmap - * @param projectKey the key of the project, or null to query the framework cache. - * @return the cached Bitmap or null if not found. - */ - public static Bitmap getCachedBitmap(String value, Object projectKey) { - if (projectKey != null) { - Map<String, SoftReference<Bitmap>> map = sProjectBitmapCache.get(projectKey); - if (map != null) { - SoftReference<Bitmap> ref = map.get(value); - if (ref != null) { - return ref.get(); - } - } - } else { - SoftReference<Bitmap> ref = sFrameworkBitmapCache.get(value); - if (ref != null) { - return ref.get(); - } - } - - return null; - } - - /** - * Sets a bitmap in a project cache or in the framework cache. - * @param value the path of the bitmap - * @param bmp the Bitmap object - * @param projectKey the key of the project, or null to put the bitmap in the framework cache. - */ - public static void setCachedBitmap(String value, Bitmap bmp, Object projectKey) { - if (projectKey != null) { - Map<String, SoftReference<Bitmap>> map = - sProjectBitmapCache.computeIfAbsent(projectKey, k -> new HashMap<>()); - - map.put(value, new SoftReference<>(bmp)); - } else { - sFrameworkBitmapCache.put(value, new SoftReference<>(bmp)); - } - } - - /** - * Returns the 9 patch chunk for a specific path, from a specific project cache, or from the - * framework cache. - * @param value the path of the 9 patch - * @param projectKey the key of the project, or null to query the framework cache. - * @return the cached 9 patch or null if not found. - */ - public static NinePatchChunk getCached9Patch(String value, Object projectKey) { - if (projectKey != null) { - Map<String, SoftReference<NinePatchChunk>> map = sProject9PatchCache.get(projectKey); - - if (map != null) { - SoftReference<NinePatchChunk> ref = map.get(value); - if (ref != null) { - return ref.get(); - } - } - } else { - SoftReference<NinePatchChunk> ref = sFramework9PatchCache.get(value); - if (ref != null) { - return ref.get(); - } - } - - return null; - } - - /** - * Sets a 9 patch chunk in a project cache or in the framework cache. - * @param value the path of the 9 patch - * @param ninePatch the 9 patch object - * @param projectKey the key of the project, or null to put the bitmap in the framework cache. - */ - public static void setCached9Patch(String value, NinePatchChunk ninePatch, Object projectKey) { - if (projectKey != null) { - Map<String, SoftReference<NinePatchChunk>> map = - sProject9PatchCache.computeIfAbsent(projectKey, k -> new HashMap<>()); - - map.put(value, new SoftReference<>(ninePatch)); - } else { - sFramework9PatchCache.put(value, new SoftReference<>(ninePatch)); - } + public int getApiLevel() { + return 0; } } diff --git a/com/android/layoutlib/bridge/android/BridgeContext.java b/com/android/layoutlib/bridge/android/BridgeContext.java index 4c6c9d48..4a75be98 100644 --- a/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/com/android/layoutlib/bridge/android/BridgeContext.java @@ -40,7 +40,6 @@ import org.xmlpull.v1.XmlPullParserException; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.Notification; import android.app.SystemServiceRegistry_Accessor; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -86,7 +85,6 @@ import android.util.TypedValue; import android.view.BridgeInflater; import android.view.Display; import android.view.DisplayAdjustments; -import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; @@ -610,45 +608,35 @@ public class BridgeContext extends Context { @Override public Object getSystemService(String service) { - if (LAYOUT_INFLATER_SERVICE.equals(service)) { - return mBridgeInflater; - } - - if (TEXT_SERVICES_MANAGER_SERVICE.equals(service)) { - // we need to return a valid service to avoid NPE - return TextServicesManager.getInstance(); - } + switch (service) { + case LAYOUT_INFLATER_SERVICE: + return mBridgeInflater; - if (WINDOW_SERVICE.equals(service)) { - return mWindowManager; - } - - // needed by SearchView - if (INPUT_METHOD_SERVICE.equals(service)) { - return null; - } + case TEXT_SERVICES_MANAGER_SERVICE: + // we need to return a valid service to avoid NPE + return TextServicesManager.getInstance(); - if (POWER_SERVICE.equals(service)) { - return new PowerManager(this, new BridgePowerManager(), new Handler()); - } + case WINDOW_SERVICE: + return mWindowManager; - if (DISPLAY_SERVICE.equals(service)) { - return mDisplayManager; - } + case POWER_SERVICE: + return new PowerManager(this, new BridgePowerManager(), new Handler()); - if (ACCESSIBILITY_SERVICE.equals(service)) { - return AccessibilityManager.getInstance(this); - } + case DISPLAY_SERVICE: + return mDisplayManager; - if (AUTOFILL_MANAGER_SERVICE.equals(service)) { - return null; - } + case ACCESSIBILITY_SERVICE: + return AccessibilityManager.getInstance(this); - if (AUDIO_SERVICE.equals(service)) { - return null; + case INPUT_METHOD_SERVICE: // needed by SearchView + case AUTOFILL_MANAGER_SERVICE: + case AUDIO_SERVICE: + case TEXT_CLASSIFICATION_SERVICE: + return null; + default: + assert false : "Unsupported Service: " + service; } - assert false : "Unsupported Service: " + service; return null; } @@ -657,13 +645,13 @@ public class BridgeContext extends Context { return SystemServiceRegistry_Accessor.getSystemServiceName(serviceClass); } - @Override - public final BridgeTypedArray obtainStyledAttributes(int[] attrs) { - return obtainStyledAttributes(0, attrs); - } - @Override - public final BridgeTypedArray obtainStyledAttributes(int resId, int[] attrs) + /** + * Same as Context#obtainStyledAttributes. We do not override the base method to give the + * original Context the chance to override the theme when needed. + */ + @Nullable + public final BridgeTypedArray internalObtainStyledAttributes(int resId, int[] attrs) throws Resources.NotFoundException { StyleResourceValue style = null; // get the StyleResourceValue based on the resId; @@ -715,13 +703,12 @@ public class BridgeContext extends Context { return typeArrayAndPropertiesPair.getFirst(); } - @Override - public final BridgeTypedArray obtainStyledAttributes(AttributeSet set, int[] attrs) { - return obtainStyledAttributes(set, attrs, 0, 0); - } - - @Override - public BridgeTypedArray obtainStyledAttributes(AttributeSet set, int[] attrs, + /** + * Same as Context#obtainStyledAttributes. We do not override the base method to give the + * original Context the chance to override the theme when needed. + */ + @Nullable + public BridgeTypedArray internalObtainStyledAttributes(@Nullable AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) { PropertiesMap defaultPropMap = null; diff --git a/com/android/layoutlib/bridge/bars/AppCompatActionBar.java b/com/android/layoutlib/bridge/bars/AppCompatActionBar.java index cdcf0ea1..bc77685e 100644 --- a/com/android/layoutlib/bridge/bars/AppCompatActionBar.java +++ b/com/android/layoutlib/bridge/bars/AppCompatActionBar.java @@ -26,6 +26,7 @@ import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.impl.ResourceHelper; import com.android.resources.ResourceType; +import com.android.tools.layoutlib.annotations.NotNull; import android.annotation.NonNull; import android.annotation.Nullable; @@ -33,11 +34,18 @@ import android.content.Context; import android.graphics.drawable.Drawable; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; import android.view.View; import android.widget.FrameLayout; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.List; + +import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX; +import static com.android.resources.ResourceType.MENU; /** @@ -50,6 +58,7 @@ public class AppCompatActionBar extends BridgeActionBar { private static final String WINDOW_ACTION_BAR_CLASS = "android.support.v7.internal.app.WindowDecorActionBar"; // This is used on v23.1.1 and later. private static final String WINDOW_ACTION_BAR_CLASS_NEW = "android.support.v7.app.WindowDecorActionBar"; + private Class<?> mWindowActionBarClass; /** @@ -90,6 +99,7 @@ public class AppCompatActionBar extends BridgeActionBar { constructorParams, constructorArgs); mWindowActionBarClass = mWindowDecorActionBar == null ? null : mWindowDecorActionBar.getClass(); + inflateMenus(); setupActionBar(); } catch (Exception e) { Bridge.getLog().warning(LayoutLog.TAG_BROKEN, @@ -165,6 +175,51 @@ public class AppCompatActionBar extends BridgeActionBar { } } + private void inflateMenus() { + List<String> menuNames = getCallBack().getMenuIdNames(); + if (menuNames.isEmpty()) { + return; + } + + if (menuNames.size() > 1) { + // Supporting multiple menus means that we would need to instantiate our own supportlib + // MenuInflater instances using reflection + Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, + "Support Toolbar does not currently support multiple menus in the preview.", + null, null, null); + } + + String name = menuNames.get(0); + int id; + if (name.startsWith(ANDROID_NS_NAME_PREFIX)) { + // Framework menu. + name = name.substring(ANDROID_NS_NAME_PREFIX.length()); + id = mBridgeContext.getFrameworkResourceValue(MENU, name, -1); + } else { + // Project menu. + id = mBridgeContext.getProjectResourceValue(MENU, name, -1); + } + if (id < 1) { + return; + } + // Get toolbar decorator + Object mDecorToolbar = getFieldValue(mWindowDecorActionBar, "mDecorToolbar"); + if (mDecorToolbar == null) { + return; + } + + Class<?> mDecorToolbarClass = mDecorToolbar.getClass(); + Context themedContext = (Context)invoke( + getMethod(mWindowActionBarClass, "getThemedContext"), + mWindowDecorActionBar); + MenuInflater inflater = new MenuInflater(themedContext); + Menu menuBuilder = (Menu)invoke(getMethod(mDecorToolbarClass, "getMenu"), mDecorToolbar); + inflater.inflate(id, menuBuilder); + + // Set the actual menu + invoke(findMethod(mDecorToolbarClass, "setMenu"), mDecorToolbar, menuBuilder, null); + } + @Override public void createMenuPopup() { // it's hard to add menus to appcompat's actionbar, since it'll use a lot of reflection. @@ -181,13 +236,53 @@ public class AppCompatActionBar extends BridgeActionBar { return null; } + /** + * Same as getMethod but doesn't require the parameterTypes. This allows us to call methods + * without having to get all the types for the parameters when we do not need them + */ @Nullable - private static Object invoke(Method method, Object owner, Object... args) { + private static Method findMethod(@Nullable Class<?> owner, @NotNull String name) { + if (owner == null) { + return null; + } + for (Method method : owner.getMethods()) { + if (name.equals(method.getName())) { + return method; + } + } + + return null; + } + + @Nullable + private static Object getFieldValue(@Nullable Object instance, @NotNull String name) { + if (instance == null) { + return null; + } + + Class<?> instanceClass = instance.getClass(); try { - return method == null ? null : method.invoke(owner, args); - } catch (InvocationTargetException e) { + Field field = instanceClass.getDeclaredField(name); + boolean accesible = field.isAccessible(); + if (!accesible) { + field.setAccessible(true); + } + try { + return field.get(instance); + } finally { + field.setAccessible(accesible); + } + } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); - } catch (IllegalAccessException e) { + } + return null; + } + + @Nullable + private static Object invoke(@Nullable Method method, Object owner, Object... args) { + try { + return method == null ? null : method.invoke(owner, args); + } catch (InvocationTargetException | IllegalAccessException e) { e.printStackTrace(); } return null; diff --git a/com/android/layoutlib/bridge/impl/GcSnapshot.java b/com/android/layoutlib/bridge/impl/GcSnapshot.java index 3ad859c6..7526e090 100644 --- a/com/android/layoutlib/bridge/impl/GcSnapshot.java +++ b/com/android/layoutlib/bridge/impl/GcSnapshot.java @@ -19,7 +19,6 @@ package com.android.layoutlib.bridge.impl; import com.android.ide.common.rendering.api.LayoutLog; import com.android.layoutlib.bridge.Bridge; -import android.annotation.NonNull; import android.graphics.Bitmap_Delegate; import android.graphics.Canvas; import android.graphics.ColorFilter_Delegate; @@ -40,13 +39,11 @@ import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; -import java.awt.Transparency; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; -import java.lang.ref.SoftReference; import java.util.ArrayList; /** @@ -69,7 +66,7 @@ public class GcSnapshot { private final int mFlags; /** list of layers. The first item in the list is always the */ - private final ArrayList<Layer> mLayers = new ArrayList<>(); + private final ArrayList<Layer> mLayers = new ArrayList<Layer>(); /** temp transform in case transformation are set before a Graphics2D exists */ private AffineTransform mTransform = null; @@ -85,13 +82,6 @@ public class GcSnapshot { private final Layer mLocalLayer; private final Paint_Delegate mLocalLayerPaint; private final Rect mLayerBounds; - /** - * Cached buffer to be used for tinting operations. This buffer is usually used many times - * and there is no need to creating it every time. - */ - private SoftReference<BufferedImage> mCachedLayerBuffer = new SoftReference<>(null); - private Rectangle2D.Float mCachedClipRect = new Rectangle2D.Float(); - public interface Drawable { void draw(Graphics2D graphics, Paint_Delegate paint); @@ -310,11 +300,12 @@ public class GcSnapshot { Layer baseLayer = mLayers.get(0); // create the image for the layer - BufferedImage layerImage = - baseLayer.getGraphics().getDeviceConfiguration().createCompatibleImage( - baseLayer.getImage().getWidth(), baseLayer.getImage().getHeight(), - (mFlags & Canvas.HAS_ALPHA_LAYER_SAVE_FLAG) != 0 ? - Transparency.TRANSLUCENT : Transparency.OPAQUE); + BufferedImage layerImage = new BufferedImage( + baseLayer.getImage().getWidth(), + baseLayer.getImage().getHeight(), + (mFlags & Canvas.HAS_ALPHA_LAYER_SAVE_FLAG) != 0 ? + BufferedImage.TYPE_INT_ARGB : + BufferedImage.TYPE_INT_RGB); // create a graphics for it so that drawing can be done. Graphics2D layerGraphics = layerImage.createGraphics(); @@ -361,8 +352,6 @@ public class GcSnapshot { } public void dispose() { - mCachedLayerBuffer.clear(); - for (Layer layer : mLayers) { layer.getGraphics().dispose(); } @@ -538,12 +527,7 @@ public class GcSnapshot { } public boolean clipRect(float left, float top, float right, float bottom, int regionOp) { - if (mCachedClipRect == null) { - mCachedClipRect = new Rectangle2D.Float(left, top, right - left, bottom - top); - } else { - mCachedClipRect.setRect(left, top, right - left, bottom - top); - } - return clip(mCachedClipRect, regionOp); + return clip(new Rectangle2D.Float(left, top, right - left, bottom - top), regionOp); } /** @@ -656,16 +640,17 @@ public class GcSnapshot { height = layer.getImage().getHeight(); } - // get a Graphics2D object configured with the drawing parameters, but no shader. - Graphics2D configuredGraphics = createCustomGraphics(originalGraphics, paint, - true /*compositeOnly*/, forceMode); - - // Create or re-use a temporary image to which the color filter will be applied. - BufferedImage image = getTemporaryBuffer(configuredGraphics, width, height); + // Create a temporary image to which the color filter will be applied. + BufferedImage image = new BufferedImage(width, height, + BufferedImage.TYPE_INT_ARGB); Graphics2D imageBaseGraphics = (Graphics2D) image.getGraphics(); // Configure the Graphics2D object with drawing parameters and shader. - Graphics2D imageGraphics = createCustomGraphics(imageBaseGraphics, paint, compositeOnly, + Graphics2D imageGraphics = createCustomGraphics( + imageBaseGraphics, paint, compositeOnly, AlphaComposite.SRC_OVER); + // get a Graphics2D object configured with the drawing parameters, but no shader. + Graphics2D configuredGraphics = createCustomGraphics(originalGraphics, paint, + true /*compositeOnly*/, forceMode); try { // The main draw operation. // We translate the operation to take into account that the rendering does not @@ -692,28 +677,6 @@ public class GcSnapshot { } } - /** - * Returns a temporary buffer sized width * height and configured with the given - * {@link Graphics2D} device configuration. - */ - @NonNull - private BufferedImage getTemporaryBuffer(@NonNull Graphics2D configuredGraphics, - int width, int height) { - BufferedImage cachedImage = mCachedLayerBuffer.get(); - if (cachedImage == null || - width > cachedImage.getWidth() || height > cachedImage.getHeight() || - !configuredGraphics.getDeviceConfiguration().getColorModel() - .isCompatibleSampleModel(cachedImage.getSampleModel())) { - // The current cached image is not valid or does not exist - cachedImage = configuredGraphics.getDeviceConfiguration() - .createCompatibleImage(width, height); - mCachedLayerBuffer = new SoftReference<>(cachedImage); - } else { - cachedImage = cachedImage.getSubimage(0, 0, width, height); - } - return cachedImage; - } - private void drawOnGraphics(Graphics2D g, Drawable drawable, Paint_Delegate paint, Layer layer) { try { diff --git a/com/android/providers/settings/SettingsProtoDumpUtil.java b/com/android/providers/settings/SettingsProtoDumpUtil.java index ec6f8319..67fb4d9f 100644 --- a/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -720,9 +720,6 @@ class SettingsProtoDumpUtil { Settings.Global.DEVICE_IDLE_CONSTANTS, GlobalSettingsProto.DEVICE_IDLE_CONSTANTS); dumpSetting(s, p, - Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH, - GlobalSettingsProto.DEVICE_IDLE_CONSTANTS_WATCH); - dumpSetting(s, p, Settings.Global.APP_IDLE_CONSTANTS, GlobalSettingsProto.APP_IDLE_CONSTANTS); dumpSetting(s, p, diff --git a/com/android/providers/settings/SettingsProvider.java b/com/android/providers/settings/SettingsProvider.java index a463db6f..36f9b840 100644 --- a/com/android/providers/settings/SettingsProvider.java +++ b/com/android/providers/settings/SettingsProvider.java @@ -2896,7 +2896,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 149; + private static final int SETTINGS_VERSION = 150; private final int mUserId; @@ -3470,9 +3470,25 @@ public class SettingsProvider extends ContentProvider { true, SettingsState.SYSTEM_PACKAGE_NAME); } } - currentVersion = 149; } + + if (currentVersion == 149) { + // Version 150: Set a default value for mobile data always on + final SettingsState globalSettings = getGlobalSettingsLocked(); + final Setting currentSetting = globalSettings.getSettingLocked( + Settings.Global.MOBILE_DATA_ALWAYS_ON); + if (currentSetting.isNull()) { + globalSettings.insertSettingLocked( + Settings.Global.MOBILE_DATA_ALWAYS_ON, + getContext().getResources().getBoolean( + R.bool.def_mobile_data_always_on) ? "1" : "0", + null, true, SettingsState.SYSTEM_PACKAGE_NAME); + } + + currentVersion = 150; + } + // vXXX: Add new settings above this point. if (currentVersion != newVersion) { diff --git a/com/android/server/BatteryService.java b/com/android/server/BatteryService.java index 83bd9ebd..5106c8d7 100644 --- a/com/android/server/BatteryService.java +++ b/com/android/server/BatteryService.java @@ -20,6 +20,7 @@ import android.app.ActivityManagerInternal; import android.database.ContentObserver; import android.os.BatteryStats; +import android.os.PowerManager; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.ShellCommand; @@ -291,6 +292,8 @@ public final class BatteryService extends SystemService { if (mActivityManagerInternal.isSystemReady()) { Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false); + intent.putExtra(Intent.EXTRA_REASON, + PowerManager.SHUTDOWN_LOW_BATTERY); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivityAsUser(intent, UserHandle.CURRENT); } @@ -310,6 +313,8 @@ public final class BatteryService extends SystemService { if (mActivityManagerInternal.isSystemReady()) { Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false); + intent.putExtra(Intent.EXTRA_REASON, + PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivityAsUser(intent, UserHandle.CURRENT); } diff --git a/com/android/server/ConnectivityService.java b/com/android/server/ConnectivityService.java index bfe50404..348c7997 100644 --- a/com/android/server/ConnectivityService.java +++ b/com/android/server/ConnectivityService.java @@ -19,6 +19,7 @@ package com.android.server; import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.NETID_UNSET; +import static android.net.ConnectivityManager.TYPE_ETHERNET; import static android.net.ConnectivityManager.TYPE_NONE; import static android.net.ConnectivityManager.TYPE_VPN; import static android.net.ConnectivityManager.getNetworkTypeName; @@ -90,6 +91,7 @@ import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; +import android.os.ServiceManager; import android.os.ServiceSpecificException; import android.os.SystemClock; import android.os.UserHandle; @@ -128,7 +130,6 @@ import com.android.server.connectivity.DataConnectionStats; import com.android.server.connectivity.KeepaliveTracker; import com.android.server.connectivity.LingerMonitor; import com.android.server.connectivity.MockableSystemProperties; -import com.android.server.connectivity.Nat464Xlat; import com.android.server.connectivity.NetworkAgentInfo; import com.android.server.connectivity.NetworkDiagnostics; import com.android.server.connectivity.NetworkMonitor; @@ -781,6 +782,13 @@ public class ConnectivityService extends IConnectivityManager.Stub mNetworksDefined++; // used only in the log() statement below. } + // Do the same for Ethernet, since it's often not specified in the configs, although many + // devices can use it via USB host adapters. + if (mNetConfigs[TYPE_ETHERNET] == null && hasService(Context.ETHERNET_SERVICE)) { + mLegacyTypeTracker.addSupportedType(TYPE_ETHERNET); + mNetworksDefined++; + } + if (VDBG) log("mNetworksDefined=" + mNetworksDefined); mProtectedNetworks = new ArrayList<Integer>(); @@ -2205,7 +2213,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // A network factory has connected. Send it all current NetworkRequests. for (NetworkRequestInfo nri : mNetworkRequests.values()) { if (nri.request.isListen()) continue; - NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId); + NetworkAgentInfo nai = getNetworkForRequest(nri.request.requestId); ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, (nai != null ? nai.getCurrentScore() : 0), 0, nri.request); } @@ -2282,9 +2290,9 @@ public class ConnectivityService extends IConnectivityManager.Stub // Remove all previously satisfied requests. for (int i = 0; i < nai.numNetworkRequests(); i++) { NetworkRequest request = nai.requestAt(i); - NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(request.requestId); + NetworkAgentInfo currentNetwork = getNetworkForRequest(request.requestId); if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) { - mNetworkForRequestId.remove(request.requestId); + clearNetworkForRequest(request.requestId); sendUpdatedScoreToFactories(request, 0); } } @@ -2360,7 +2368,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } rematchAllNetworksAndRequests(null, 0); - if (nri.request.isRequest() && mNetworkForRequestId.get(nri.request.requestId) == null) { + if (nri.request.isRequest() && getNetworkForRequest(nri.request.requestId) == null) { sendUpdatedScoreToFactories(nri.request, 0); } } @@ -2415,7 +2423,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // 2. Unvalidated WiFi will not be reaped when validated cellular // is currently satisfying the request. This is desirable when // WiFi ends up validating and out scoring cellular. - mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() < + getNetworkForRequest(nri.request.requestId).getCurrentScore() < nai.getCurrentScoreAsValidated())) { return false; } @@ -2442,7 +2450,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (mNetworkRequests.get(nri.request) == null) { return; } - if (mNetworkForRequestId.get(nri.request.requestId) != null) { + if (getNetworkForRequest(nri.request.requestId) != null) { return; } if (VDBG || (DBG && nri.request.isRequest())) { @@ -2482,7 +2490,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mNetworkRequestInfoLogs.log("RELEASE " + nri); if (nri.request.isRequest()) { boolean wasKept = false; - NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId); + NetworkAgentInfo nai = getNetworkForRequest(nri.request.requestId); if (nai != null) { boolean wasBackgroundNetwork = nai.isBackgroundNetwork(); nai.removeRequest(nri.request.requestId); @@ -2499,7 +2507,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } else { wasKept = true; } - mNetworkForRequestId.remove(nri.request.requestId); + clearNetworkForRequest(nri.request.requestId); if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) { // Went from foreground to background. updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities); @@ -4296,7 +4304,8 @@ public class ConnectivityService extends IConnectivityManager.Stub * and the are the highest scored network available. * the are keyed off the Requests requestId. */ - // TODO: Yikes, this is accessed on multiple threads: add synchronization. + // NOTE: Accessed on multiple threads, must be synchronized on itself. + @GuardedBy("mNetworkForRequestId") private final SparseArray<NetworkAgentInfo> mNetworkForRequestId = new SparseArray<NetworkAgentInfo>(); @@ -4326,8 +4335,26 @@ public class ConnectivityService extends IConnectivityManager.Stub // priority networks like Wi-Fi are active. private final NetworkRequest mDefaultMobileDataRequest; + private NetworkAgentInfo getNetworkForRequest(int requestId) { + synchronized (mNetworkForRequestId) { + return mNetworkForRequestId.get(requestId); + } + } + + private void clearNetworkForRequest(int requestId) { + synchronized (mNetworkForRequestId) { + mNetworkForRequestId.remove(requestId); + } + } + + private void setNetworkForRequest(int requestId, NetworkAgentInfo nai) { + synchronized (mNetworkForRequestId) { + mNetworkForRequestId.put(requestId, nai); + } + } + private NetworkAgentInfo getDefaultNetwork() { - return mNetworkForRequestId.get(mDefaultRequest.requestId); + return getNetworkForRequest(mDefaultRequest.requestId); } private boolean isDefaultNetwork(NetworkAgentInfo nai) { @@ -4881,7 +4908,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // requests or not, and doesn't affect the network's score. if (nri.request.isListen()) continue; - final NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId); + final NetworkAgentInfo currentNetwork = getNetworkForRequest(nri.request.requestId); final boolean satisfies = newNetwork.satisfies(nri.request); if (newNetwork == currentNetwork && satisfies) { if (VDBG) { @@ -4913,7 +4940,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (VDBG) log(" accepting network in place of null"); } newNetwork.unlingerRequest(nri.request); - mNetworkForRequestId.put(nri.request.requestId, newNetwork); + setNetworkForRequest(nri.request.requestId, newNetwork); if (!newNetwork.addRequest(nri.request)) { Slog.wtf(TAG, "BUG: " + newNetwork.name() + " already has " + nri.request); } @@ -4947,7 +4974,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } newNetwork.removeRequest(nri.request.requestId); if (currentNetwork == newNetwork) { - mNetworkForRequestId.remove(nri.request.requestId); + clearNetworkForRequest(nri.request.requestId); sendUpdatedScoreToFactories(nri.request, 0); } else { Slog.wtf(TAG, "BUG: Removing request " + nri.request.requestId + " from " + @@ -5522,6 +5549,11 @@ public class ConnectivityService extends IConnectivityManager.Stub return new WakeupMessage(c, h, s, cmd, 0, 0, obj); } + @VisibleForTesting + public boolean hasService(String name) { + return ServiceManager.checkService(name) != null; + } + private void logDefaultNetworkEvent(NetworkAgentInfo newNai, NetworkAgentInfo prevNai) { int newNetid = NETID_UNSET; int prevNetid = NETID_UNSET; diff --git a/com/android/server/DeviceIdleController.java b/com/android/server/DeviceIdleController.java index abbc89e4..2d9baf61 100644 --- a/com/android/server/DeviceIdleController.java +++ b/com/android/server/DeviceIdleController.java @@ -307,6 +307,12 @@ public class DeviceIdleController extends SystemService */ private int[] mTempWhitelistAppIdArray = new int[0]; + /** + * Apps in the system whitelist that have been taken out (probably because the user wanted to). + * They can be restored back by calling restoreAppToSystemWhitelist(String). + */ + private ArrayMap<String, Integer> mRemovedFromSystemWhitelistApps = new ArrayMap<>(); + private static final int EVENT_NULL = 0; private static final int EVENT_NORMAL = 1; private static final int EVENT_LIGHT_IDLE = 2; @@ -760,17 +766,15 @@ public class DeviceIdleController extends SystemService public long NOTIFICATION_WHITELIST_DURATION; private final ContentResolver mResolver; - private final boolean mHasWatch; + private final boolean mSmallBatteryDevice; private final KeyValueListParser mParser = new KeyValueListParser(','); public Constants(Handler handler, ContentResolver resolver) { super(handler); mResolver = resolver; - mHasWatch = getContext().getPackageManager().hasSystemFeature( - PackageManager.FEATURE_WATCH); - mResolver.registerContentObserver(Settings.Global.getUriFor( - mHasWatch ? Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH - : Settings.Global.DEVICE_IDLE_CONSTANTS), + mSmallBatteryDevice = ActivityManager.isSmallBatteryDevice(); + mResolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.DEVICE_IDLE_CONSTANTS), false, this); updateConstants(); } @@ -784,8 +788,7 @@ public class DeviceIdleController extends SystemService synchronized (DeviceIdleController.this) { try { mParser.setString(Settings.Global.getString(mResolver, - mHasWatch ? Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH - : Settings.Global.DEVICE_IDLE_CONSTANTS)); + Settings.Global.DEVICE_IDLE_CONSTANTS)); } catch (IllegalArgumentException e) { // Failed to parse the settings string, log this and move on // with defaults. @@ -815,7 +818,7 @@ public class DeviceIdleController extends SystemService MIN_DEEP_MAINTENANCE_TIME = mParser.getLong( KEY_MIN_DEEP_MAINTENANCE_TIME, !COMPRESS_TIME ? 30 * 1000L : 5 * 1000L); - long inactiveTimeoutDefault = (mHasWatch ? 15 : 30) * 60 * 1000L; + long inactiveTimeoutDefault = (mSmallBatteryDevice ? 15 : 30) * 60 * 1000L; INACTIVE_TIMEOUT = mParser.getLong(KEY_INACTIVE_TIMEOUT, !COMPRESS_TIME ? inactiveTimeoutDefault : (inactiveTimeoutDefault / 10)); SENSING_TIMEOUT = mParser.getLong(KEY_SENSING_TIMEOUT, @@ -825,7 +828,7 @@ public class DeviceIdleController extends SystemService LOCATION_ACCURACY = mParser.getFloat(KEY_LOCATION_ACCURACY, 20); MOTION_INACTIVE_TIMEOUT = mParser.getLong(KEY_MOTION_INACTIVE_TIMEOUT, !COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L); - long idleAfterInactiveTimeout = (mHasWatch ? 15 : 30) * 60 * 1000L; + long idleAfterInactiveTimeout = (mSmallBatteryDevice ? 15 : 30) * 60 * 1000L; IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getLong(KEY_IDLE_AFTER_INACTIVE_TIMEOUT, !COMPRESS_TIME ? idleAfterInactiveTimeout : (idleAfterInactiveTimeout / 10)); @@ -1162,6 +1165,38 @@ public class DeviceIdleController extends SystemService } } + @Override public void removeSystemPowerWhitelistApp(String name) { + if (DEBUG) { + Slog.d(TAG, "removeAppFromSystemWhitelist(name = " + name + ")"); + } + getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, + null); + long ident = Binder.clearCallingIdentity(); + try { + removeSystemPowerWhitelistAppInternal(name); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override public void restoreSystemPowerWhitelistApp(String name) { + if (DEBUG) { + Slog.d(TAG, "restoreAppToSystemWhitelist(name = " + name + ")"); + } + getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, + null); + long ident = Binder.clearCallingIdentity(); + try { + restoreSystemPowerWhitelistAppInternal(name); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + public String[] getRemovedSystemPowerWhitelistApps() { + return getRemovedSystemPowerWhitelistAppsInternal(); + } + @Override public String[] getSystemPowerWhitelistExceptIdle() { return getSystemPowerWhitelistExceptIdleInternal(); } @@ -1504,6 +1539,42 @@ public class DeviceIdleController extends SystemService } } + void resetSystemPowerWhitelistInternal() { + synchronized (this) { + mPowerSaveWhitelistApps.putAll(mRemovedFromSystemWhitelistApps); + mRemovedFromSystemWhitelistApps.clear(); + reportPowerSaveWhitelistChangedLocked(); + updateWhitelistAppIdsLocked(); + writeConfigFileLocked(); + } + } + + public boolean restoreSystemPowerWhitelistAppInternal(String name) { + synchronized (this) { + if (!mRemovedFromSystemWhitelistApps.containsKey(name)) { + return false; + } + mPowerSaveWhitelistApps.put(name, mRemovedFromSystemWhitelistApps.remove(name)); + reportPowerSaveWhitelistChangedLocked(); + updateWhitelistAppIdsLocked(); + writeConfigFileLocked(); + return true; + } + } + + public boolean removeSystemPowerWhitelistAppInternal(String name) { + synchronized (this) { + if (!mPowerSaveWhitelistApps.containsKey(name)) { + return false; + } + mRemovedFromSystemWhitelistApps.put(name, mPowerSaveWhitelistApps.remove(name)); + reportPowerSaveWhitelistChangedLocked(); + updateWhitelistAppIdsLocked(); + writeConfigFileLocked(); + return true; + } + } + public boolean addPowerSaveWhitelistExceptIdleInternal(String name) { synchronized (this) { try { @@ -1565,6 +1636,17 @@ public class DeviceIdleController extends SystemService } } + public String[] getRemovedSystemPowerWhitelistAppsInternal() { + synchronized (this) { + int size = mRemovedFromSystemWhitelistApps.size(); + final String[] apps = new String[size]; + for (int i = 0; i < size; i++) { + apps[i] = mRemovedFromSystemWhitelistApps.keyAt(i); + } + return apps; + } + } + public String[] getUserPowerWhitelistInternal() { synchronized (this) { int size = mPowerSaveWhitelistUserApps.size(); @@ -2481,21 +2563,31 @@ public class DeviceIdleController extends SystemService } String tagName = parser.getName(); - if (tagName.equals("wl")) { - String name = parser.getAttributeValue(null, "n"); - if (name != null) { - try { - ApplicationInfo ai = pm.getApplicationInfo(name, - PackageManager.MATCH_ANY_USER); - mPowerSaveWhitelistUserApps.put(ai.packageName, - UserHandle.getAppId(ai.uid)); - } catch (PackageManager.NameNotFoundException e) { + switch (tagName) { + case "wl": + String name = parser.getAttributeValue(null, "n"); + if (name != null) { + try { + ApplicationInfo ai = pm.getApplicationInfo(name, + PackageManager.MATCH_ANY_USER); + mPowerSaveWhitelistUserApps.put(ai.packageName, + UserHandle.getAppId(ai.uid)); + } catch (PackageManager.NameNotFoundException e) { + } } - } - } else { - Slog.w(TAG, "Unknown element under <config>: " - + parser.getName()); - XmlUtils.skipCurrentTag(parser); + break; + case "un-wl": + final String packageName = parser.getAttributeValue(null, "n"); + if (mPowerSaveWhitelistApps.containsKey(packageName)) { + mRemovedFromSystemWhitelistApps.put(packageName, + mPowerSaveWhitelistApps.remove(packageName)); + } + break; + default: + Slog.w(TAG, "Unknown element under <config>: " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + break; } } @@ -2556,6 +2648,11 @@ public class DeviceIdleController extends SystemService out.attribute(null, "n", name); out.endTag(null, "wl"); } + for (int i = 0; i < mRemovedFromSystemWhitelistApps.size(); i++) { + out.startTag(null, "un-wl"); + out.attribute(null, "n", mRemovedFromSystemWhitelistApps.keyAt(i)); + out.endTag(null, "un-wl"); + } out.endTag(null, "config"); out.endDocument(); } @@ -2584,6 +2681,13 @@ public class DeviceIdleController extends SystemService pw.println(" Print currently whitelisted apps."); pw.println(" whitelist [package ...]"); pw.println(" Add (prefix with +) or remove (prefix with -) packages."); + pw.println(" sys-whitelist [package ...|reset]"); + pw.println(" Prefix the package with '-' to remove it from the system whitelist or '+'" + + " to put it back in the system whitelist."); + pw.println(" Note that only packages that were" + + " earlier removed from the system whitelist can be added back."); + pw.println(" reset will reset the whitelist to the original state"); + pw.println(" Prints the system whitelist if no arguments are specified"); pw.println(" except-idle-whitelist [package ...|reset]"); pw.println(" Prefix the package with '+' to add it to whitelist or " + "'=' to check if it is already whitelisted"); @@ -2944,6 +3048,50 @@ public class DeviceIdleController extends SystemService } finally { Binder.restoreCallingIdentity(token); } + } else if ("sys-whitelist".equals(cmd)) { + String arg = shell.getNextArg(); + if (arg != null) { + getContext().enforceCallingOrSelfPermission( + android.Manifest.permission.DEVICE_POWER, null); + final long token = Binder.clearCallingIdentity(); + try { + if ("reset".equals(arg)) { + resetSystemPowerWhitelistInternal(); + } else { + do { + if (arg.length() < 1 + || (arg.charAt(0) != '-' && arg.charAt(0) != '+')) { + pw.println("Package must be prefixed with + or - " + arg); + return -1; + } + final char op = arg.charAt(0); + final String pkg = arg.substring(1); + switch (op) { + case '+': + if (restoreSystemPowerWhitelistAppInternal(pkg)) { + pw.println("Restored " + pkg); + } + break; + case '-': + if (removeSystemPowerWhitelistAppInternal(pkg)) { + pw.println("Removed " + pkg); + } + break; + } + } while ((arg = shell.getNextArg()) != null); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } else { + synchronized (this) { + for (int j=0; j<mPowerSaveWhitelistApps.size(); j++) { + pw.print(mPowerSaveWhitelistApps.keyAt(j)); + pw.print(","); + pw.println(mPowerSaveWhitelistApps.valueAt(j)); + } + } + } } else { return shell.handleDefaultCommands(cmd); } @@ -3027,6 +3175,14 @@ public class DeviceIdleController extends SystemService pw.println(mPowerSaveWhitelistApps.keyAt(i)); } } + size = mRemovedFromSystemWhitelistApps.size(); + if (size > 0) { + pw.println(" Removed from whitelist system apps:"); + for (int i = 0; i < size; i++) { + pw.print(" "); + pw.println(mRemovedFromSystemWhitelistApps.keyAt(i)); + } + } size = mPowerSaveWhitelistUserApps.size(); if (size > 0) { pw.println(" Whitelist user apps:"); diff --git a/com/android/server/DiskStatsService.java b/com/android/server/DiskStatsService.java index 800081e5..2d2c6b0b 100644 --- a/com/android/server/DiskStatsService.java +++ b/com/android/server/DiskStatsService.java @@ -202,6 +202,8 @@ public class DiskStatsService extends Binder { JSONObject json = new JSONObject(jsonString); pw.print("App Size: "); pw.println(json.getLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY)); + pw.print("App Data Size: "); + pw.println(json.getLong(DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY)); pw.print("App Cache Size: "); pw.println(json.getLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY)); pw.print("Photos Size: "); @@ -220,6 +222,8 @@ public class DiskStatsService extends Binder { pw.println(json.getJSONArray(DiskStatsFileLogger.PACKAGE_NAMES_KEY)); pw.print("App Sizes: "); pw.println(json.getJSONArray(DiskStatsFileLogger.APP_SIZES_KEY)); + pw.print("App Data Sizes: "); + pw.println(json.getJSONArray(DiskStatsFileLogger.APP_DATA_KEY)); pw.print("Cache Sizes: "); pw.println(json.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY)); } catch (IOException | JSONException e) { @@ -235,6 +239,8 @@ public class DiskStatsService extends Binder { proto.write(DiskStatsCachedValuesProto.AGG_APPS_SIZE, json.getLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY)); + proto.write(DiskStatsCachedValuesProto.AGG_APPS_DATA_SIZE, + json.getLong(DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY)); proto.write(DiskStatsCachedValuesProto.AGG_APPS_CACHE_SIZE, json.getLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY)); proto.write(DiskStatsCachedValuesProto.PHOTOS_SIZE, @@ -252,22 +258,26 @@ public class DiskStatsService extends Binder { JSONArray packageNamesArray = json.getJSONArray(DiskStatsFileLogger.PACKAGE_NAMES_KEY); JSONArray appSizesArray = json.getJSONArray(DiskStatsFileLogger.APP_SIZES_KEY); + JSONArray appDataSizesArray = json.getJSONArray(DiskStatsFileLogger.APP_DATA_KEY); JSONArray cacheSizesArray = json.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY); final int len = packageNamesArray.length(); - if (len == appSizesArray.length() && len == cacheSizesArray.length()) { + if (len == appSizesArray.length() + && len == appDataSizesArray.length() + && len == cacheSizesArray.length()) { for (int i = 0; i < len; i++) { long packageToken = proto.start(DiskStatsCachedValuesProto.APP_SIZES); proto.write(DiskStatsAppSizesProto.PACKAGE_NAME, packageNamesArray.getString(i)); proto.write(DiskStatsAppSizesProto.APP_SIZE, appSizesArray.getLong(i)); + proto.write(DiskStatsAppSizesProto.APP_DATA_SIZE, appDataSizesArray.getLong(i)); proto.write(DiskStatsAppSizesProto.CACHE_SIZE, cacheSizesArray.getLong(i)); proto.end(packageToken); } } else { - Slog.wtf(TAG, "Sizes of packageNamesArray, appSizesArray and cacheSizesArray " - + "are not the same"); + Slog.wtf(TAG, "Sizes of packageNamesArray, appSizesArray, appDataSizesArray " + + " and cacheSizesArray are not the same"); } proto.end(cachedValuesToken); diff --git a/com/android/server/IpSecService.java b/com/android/server/IpSecService.java index 30568313..2e1f142a 100644 --- a/com/android/server/IpSecService.java +++ b/com/android/server/IpSecService.java @@ -33,6 +33,7 @@ import android.net.IpSecSpiResponse; import android.net.IpSecTransform; import android.net.IpSecTransformResponse; import android.net.IpSecUdpEncapResponse; +import android.net.NetworkUtils; import android.net.util.NetdService; import android.os.Binder; import android.os.IBinder; @@ -42,11 +43,14 @@ import android.os.ServiceSpecificException; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; +import android.text.TextUtils; import android.util.Log; import android.util.Slog; import android.util.SparseArray; + import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; + import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; @@ -54,6 +58,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.concurrent.atomic.AtomicInteger; + import libcore.io.IoUtils; /** @hide */ @@ -252,7 +257,11 @@ public class IpSecService extends IIpSecService.Stub { return (mReferenceCount.get() > 0); } - public void checkOwnerOrSystemAndThrow() { + /** + * Ensures that the caller is either the owner of this resource or has the system UID and + * throws a SecurityException otherwise. + */ + public void checkOwnerOrSystem() { if (uid != Binder.getCallingUid() && android.os.Process.SYSTEM_UID != Binder.getCallingUid()) { throw new SecurityException("Only the owner may access managed resources!"); @@ -335,12 +344,12 @@ public class IpSecService extends IIpSecService.Stub { private class ManagedResourceArray<T extends ManagedResource> { SparseArray<T> mArray = new SparseArray<>(); - T get(int key) { + T getAndCheckOwner(int key) { T val = mArray.get(key); // The value should never be null unless the resource doesn't exist // (since we do not allow null resources to be added). if (val != null) { - val.checkOwnerOrSystemAndThrow(); + val.checkOwnerOrSystem(); } return val; } @@ -405,12 +414,8 @@ public class IpSecService extends IIpSecService.Stub { .ipSecDeleteSecurityAssociation( mResourceId, direction, - (mConfig.getLocalAddress() != null) - ? mConfig.getLocalAddress().getHostAddress() - : "", - (mConfig.getRemoteAddress() != null) - ? mConfig.getRemoteAddress().getHostAddress() - : "", + mConfig.getLocalAddress(), + mConfig.getRemoteAddress(), spi); } catch (ServiceSpecificException e) { // FIXME: get the error code and throw is at an IOException from Errno Exception @@ -638,11 +643,45 @@ public class IpSecService extends IIpSecService.Stub { } } + /** + * Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be + * a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1. + */ + private static void checkInetAddress(String inetAddress) { + if (TextUtils.isEmpty(inetAddress)) { + throw new IllegalArgumentException("Unspecified address"); + } + + InetAddress checkAddr = NetworkUtils.numericToInetAddress(inetAddress); + + if (checkAddr.isAnyLocalAddress()) { + throw new IllegalArgumentException("Inappropriate wildcard address: " + inetAddress); + } + } + + /** + * Checks the user-provided direction field and throws an IllegalArgumentException if it is not + * DIRECTION_IN or DIRECTION_OUT + */ + private static void checkDirection(int direction) { + switch (direction) { + case IpSecTransform.DIRECTION_OUT: + case IpSecTransform.DIRECTION_IN: + return; + } + throw new IllegalArgumentException("Invalid Direction: " + direction); + } + @Override /** Get a new SPI and maintain the reservation in the system server */ public synchronized IpSecSpiResponse reserveSecurityParameterIndex( int direction, String remoteAddress, int requestedSpi, IBinder binder) throws RemoteException { + checkDirection(direction); + checkInetAddress(remoteAddress); + /* requestedSpi can be anything in the int range, so no check is needed. */ + checkNotNull(binder, "Null Binder passed to reserveSecurityParameterIndex"); + int resourceId = mNextResourceId.getAndIncrement(); int spi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX; @@ -651,9 +690,7 @@ public class IpSecService extends IIpSecService.Stub { try { if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).spi.isAvailable()) { return new IpSecSpiResponse( - IpSecManager.Status.RESOURCE_UNAVAILABLE, - INVALID_RESOURCE_ID, - spi); + IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi); } spi = mSrvConfig @@ -686,7 +723,7 @@ public class IpSecService extends IIpSecService.Stub { throws RemoteException { // We want to non-destructively get so that we can check credentials before removing // this from the records. - T record = resArray.get(resourceId); + T record = resArray.getAndCheckOwner(resourceId); if (record == null) { throw new IllegalArgumentException( @@ -751,6 +788,8 @@ public class IpSecService extends IIpSecService.Stub { throw new IllegalArgumentException( "Specified port number must be a valid non-reserved UDP port"); } + checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket"); + int resourceId = mNextResourceId.getAndIncrement(); FileDescriptor sockFd = null; try { @@ -792,6 +831,68 @@ public class IpSecService extends IIpSecService.Stub { } /** + * Checks an IpSecConfig parcel to ensure that the contents are sane and throws an + * IllegalArgumentException if they are not. + */ + private void checkIpSecConfig(IpSecConfig config) { + if (config.getLocalAddress() == null) { + throw new IllegalArgumentException("Invalid null Local InetAddress"); + } + + if (config.getRemoteAddress() == null) { + throw new IllegalArgumentException("Invalid null Remote InetAddress"); + } + + switch (config.getMode()) { + case IpSecTransform.MODE_TRANSPORT: + if (!config.getLocalAddress().isEmpty()) { + throw new IllegalArgumentException("Non-empty Local Address"); + } + // Must be valid, and not a wildcard + checkInetAddress(config.getRemoteAddress()); + break; + case IpSecTransform.MODE_TUNNEL: + break; + default: + throw new IllegalArgumentException( + "Invalid IpSecTransform.mode: " + config.getMode()); + } + + switch (config.getEncapType()) { + case IpSecTransform.ENCAP_NONE: + break; + case IpSecTransform.ENCAP_ESPINUDP: + case IpSecTransform.ENCAP_ESPINUDP_NON_IKE: + if (mUdpSocketRecords.getAndCheckOwner( + config.getEncapSocketResourceId()) == null) { + throw new IllegalStateException( + "No Encapsulation socket for Resource Id: " + + config.getEncapSocketResourceId()); + } + + int port = config.getEncapRemotePort(); + if (port <= 0 || port > 0xFFFF) { + throw new IllegalArgumentException("Invalid remote UDP port: " + port); + } + break; + default: + throw new IllegalArgumentException("Invalid Encap Type: " + config.getEncapType()); + } + + for (int direction : DIRECTIONS) { + IpSecAlgorithm crypt = config.getEncryption(direction); + IpSecAlgorithm auth = config.getAuthentication(direction); + if (crypt == null && auth == null) { + throw new IllegalArgumentException("Encryption and Authentication are both null"); + } + + if (mSpiRecords.getAndCheckOwner(config.getSpiResourceId(direction)) == null) { + throw new IllegalStateException("No SPI for specified Resource Id"); + } + } + } + + /** * Create a transport mode transform, which represent two security associations (one in each * direction) in the kernel. The transform will be cached by the system server and must be freed * when no longer needed. It is possible to free one, deleting the SA from underneath sockets @@ -801,17 +902,19 @@ public class IpSecService extends IIpSecService.Stub { @Override public synchronized IpSecTransformResponse createTransportModeTransform( IpSecConfig c, IBinder binder) throws RemoteException { + checkIpSecConfig(c); + checkNotNull(binder, "Null Binder passed to createTransportModeTransform"); int resourceId = mNextResourceId.getAndIncrement(); if (!mUserQuotaTracker.getUserRecord(Binder.getCallingUid()).transform.isAvailable()) { return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE); } SpiRecord[] spis = new SpiRecord[DIRECTIONS.length]; - // TODO: Basic input validation here since it's coming over the Binder + int encapType, encapLocalPort = 0, encapRemotePort = 0; UdpSocketRecord socketRecord = null; encapType = c.getEncapType(); if (encapType != IpSecTransform.ENCAP_NONE) { - socketRecord = mUdpSocketRecords.get(c.getEncapLocalResourceId()); + socketRecord = mUdpSocketRecords.getAndCheckOwner(c.getEncapSocketResourceId()); encapLocalPort = socketRecord.getPort(); encapRemotePort = c.getEncapRemotePort(); } @@ -820,23 +923,18 @@ public class IpSecService extends IIpSecService.Stub { IpSecAlgorithm auth = c.getAuthentication(direction); IpSecAlgorithm crypt = c.getEncryption(direction); - spis[direction] = mSpiRecords.get(c.getSpiResourceId(direction)); + spis[direction] = mSpiRecords.getAndCheckOwner(c.getSpiResourceId(direction)); int spi = spis[direction].getSpi(); try { - mSrvConfig.getNetdInstance() + mSrvConfig + .getNetdInstance() .ipSecAddSecurityAssociation( resourceId, c.getMode(), direction, - (c.getLocalAddress() != null) - ? c.getLocalAddress().getHostAddress() - : "", - (c.getRemoteAddress() != null) - ? c.getRemoteAddress().getHostAddress() - : "", - (c.getNetwork() != null) - ? c.getNetwork().getNetworkHandle() - : 0, + c.getLocalAddress(), + c.getRemoteAddress(), + (c.getNetwork() != null) ? c.getNetwork().getNetworkHandle() : 0, spi, (auth != null) ? auth.getName() : "", (auth != null) ? auth.getKey() : null, @@ -879,7 +977,7 @@ public class IpSecService extends IIpSecService.Stub { // Synchronize liberally here because we are using ManagedResources in this block TransformRecord info; // FIXME: this code should be factored out into a security check + getter - info = mTransformRecords.get(resourceId); + info = mTransformRecords.getAndCheckOwner(resourceId); if (info == null) { throw new IllegalArgumentException("Transform " + resourceId + " is not active"); @@ -899,12 +997,8 @@ public class IpSecService extends IIpSecService.Stub { socket.getFileDescriptor(), resourceId, direction, - (c.getLocalAddress() != null) - ? c.getLocalAddress().getHostAddress() - : "", - (c.getRemoteAddress() != null) - ? c.getRemoteAddress().getHostAddress() - : "", + c.getLocalAddress(), + c.getRemoteAddress(), info.getSpiRecord(direction).getSpi()); } } catch (ServiceSpecificException e) { diff --git a/com/android/server/LocationManagerService.java b/com/android/server/LocationManagerService.java index 340d672d..0fd59eaa 100644 --- a/com/android/server/LocationManagerService.java +++ b/com/android/server/LocationManagerService.java @@ -18,7 +18,6 @@ package com.android.server; import android.app.ActivityManager; import android.annotation.NonNull; -import android.content.pm.PackageManagerInternal; import android.util.ArrayMap; import android.util.ArraySet; import com.android.internal.content.PackageMonitor; @@ -56,6 +55,7 @@ import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.Signature; diff --git a/com/android/server/NetworkManagementService.java b/com/android/server/NetworkManagementService.java index 2f95aa2c..ba3afc31 100644 --- a/com/android/server/NetworkManagementService.java +++ b/com/android/server/NetworkManagementService.java @@ -1140,17 +1140,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub } @Override - public void setInterfaceIpv6NdOffload(String iface, boolean enable) { - mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - try { - mConnector.execute( - "interface", "ipv6ndoffload", iface, (enable ? "enable" : "disable")); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); - } - } - - @Override public void addRoute(int netId, RouteInfo route) { modifyRoute("add", "" + netId, route); } @@ -1991,8 +1980,12 @@ public class NetworkManagementService extends INetworkManagementService.Stub final String[] domainStrs = domains == null ? new String[0] : domains.split(" "); final int[] params = { sampleValidity, successThreshold, minSamples, maxSamples }; + final boolean useTls = false; + final String tlsHostname = ""; + final String[] tlsFingerprints = new String[0]; try { - mNetdService.setResolverConfiguration(netId, servers, domainStrs, params); + mNetdService.setResolverConfiguration(netId, servers, domainStrs, params, + useTls, tlsHostname, tlsFingerprints); } catch (RemoteException e) { throw new RuntimeException(e); } diff --git a/com/android/server/StorageManagerService.java b/com/android/server/StorageManagerService.java index c0fcfd07..55391b3e 100644 --- a/com/android/server/StorageManagerService.java +++ b/com/android/server/StorageManagerService.java @@ -111,8 +111,6 @@ import com.android.internal.util.HexDump; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.internal.widget.LockPatternUtils; -import com.android.server.NativeDaemonConnector.Command; -import com.android.server.NativeDaemonConnector.SensitiveArg; import com.android.server.pm.PackageManagerService; import com.android.server.storage.AppFuseBridge; @@ -138,7 +136,6 @@ import java.security.spec.KeySpec; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -161,8 +158,7 @@ import javax.crypto.spec.PBEKeySpec; * watch for and manage dynamically added storage, such as SD cards and USB mass * storage. Also decides how storage should be presented to users on the device. */ -class StorageManagerService extends IStorageManager.Stub - implements INativeDaemonConnectorCallbacks, Watchdog.Monitor { +class StorageManagerService extends IStorageManager.Stub implements Watchdog.Monitor { // Static direct instance pointer for the tightly-coupled idle service to use static StorageManagerService sSelf = null; @@ -206,18 +202,12 @@ class StorageManagerService extends IStorageManager.Stub } } - /** Flag to enable binder-based interface to vold */ - private static final boolean ENABLE_BINDER = true; - private static final boolean DEBUG_EVENTS = false; private static final boolean DEBUG_OBB = false; // Disable this since it messes up long-running cryptfs operations. private static final boolean WATCHDOG_ENABLE = false; - /** Flag to enable ASECs */ - private static final boolean ASEC_ENABLE = false; - /** * Our goal is for all Android devices to be usable as development devices, * which includes the new Direct Boot mode added in N. For devices that @@ -232,66 +222,9 @@ class StorageManagerService extends IStorageManager.Stub private static final String TAG_STORAGE_BENCHMARK = "storage_benchmark"; private static final String TAG_STORAGE_TRIM = "storage_trim"; - private static final String VOLD_TAG = "VoldConnector"; - private static final String CRYPTD_TAG = "CryptdConnector"; - - /** Maximum number of ASEC containers allowed to be mounted. */ - private static final int MAX_CONTAINERS = 250; - /** Magic value sent by MoveTask.cpp */ private static final int MOVE_STATUS_COPY_FINISHED = 82; - /* - * Internal vold response code constants - */ - class VoldResponseCode { - /* - * 100 series - Requestion action was initiated; expect another reply - * before proceeding with a new command. - */ - public static final int VolumeListResult = 110; - public static final int AsecListResult = 111; - public static final int StorageUsersListResult = 112; - public static final int CryptfsGetfieldResult = 113; - - /* - * 200 series - Requestion action has been successfully completed. - */ - public static final int ShareStatusResult = 210; - public static final int AsecPathResult = 211; - public static final int ShareEnabledResult = 212; - - /* - * 400 series - Command was accepted, but the requested action - * did not take place. - */ - public static final int OpFailedNoMedia = 401; - public static final int OpFailedMediaBlank = 402; - public static final int OpFailedMediaCorrupt = 403; - public static final int OpFailedVolNotMounted = 404; - public static final int OpFailedStorageBusy = 405; - public static final int OpFailedStorageNotFound = 406; - - /* - * 600 series - Unsolicited broadcasts. - */ - public static final int DISK_CREATED = 640; - public static final int DISK_SIZE_CHANGED = 641; - public static final int DISK_LABEL_CHANGED = 642; - public static final int DISK_SCANNED = 643; - public static final int DISK_SYS_PATH_CHANGED = 644; - public static final int DISK_DESTROYED = 649; - - public static final int VOLUME_CREATED = 650; - public static final int VOLUME_STATE_CHANGED = 651; - public static final int VOLUME_FS_TYPE_CHANGED = 652; - public static final int VOLUME_FS_UUID_CHANGED = 653; - public static final int VOLUME_FS_LABEL_CHANGED = 654; - public static final int VOLUME_PATH_CHANGED = 655; - public static final int VOLUME_INTERNAL_PATH_CHANGED = 656; - public static final int VOLUME_DESTROYED = 659; - } - private static final int VERSION_INIT = 1; private static final int VERSION_ADD_PRIMARY = 2; private static final int VERSION_FIX_PRIMARY = 3; @@ -453,17 +386,6 @@ class StorageManagerService extends IStorageManager.Stub } } - private static String escapeNull(String arg) { - if (TextUtils.isEmpty(arg)) { - return "!"; - } else { - if (arg.indexOf('\0') != -1 || arg.indexOf(' ') != -1) { - throw new IllegalArgumentException(arg); - } - return arg; - } - } - /** List of crypto types. * These must match CRYPT_TYPE_XXX in cryptfs.h AND their * corresponding commands in CommandListener.cpp */ @@ -472,12 +394,6 @@ class StorageManagerService extends IStorageManager.Stub private final Context mContext; - private final NativeDaemonConnector mConnector; - private final NativeDaemonConnector mCryptConnector; - - private final Thread mConnectorThread; - private final Thread mCryptConnectorThread; - private volatile IVold mVold; private volatile boolean mSystemReady = false; @@ -489,20 +405,6 @@ class StorageManagerService extends IStorageManager.Stub private final Callbacks mCallbacks; private final LockPatternUtils mLockPatternUtils; - // Two connectors - mConnector & mCryptConnector - private final CountDownLatch mConnectedSignal = new CountDownLatch(2); - private final CountDownLatch mAsecsScanned = new CountDownLatch(1); - - private final Object mUnmountLock = new Object(); - @GuardedBy("mUnmountLock") - private CountDownLatch mUnmountSignal; - - /** - * Private hash of currently mounted secure containers. - * Used as a lock in methods to manipulate secure containers. - */ - final private HashSet<String> mAsecMountSet = new HashSet<String>(); - /** * The size of the crypto algorithm key in bits for OBB files. Currently * Twofish is used which takes 128-bit keys. @@ -616,7 +518,7 @@ class StorageManagerService extends IStorageManager.Stub if (DEBUG_OBB) Slog.i(TAG, "onServiceDisconnected"); } - }; + } // Used in the ObbActionHandler private IMediaContainerService mContainerService = null; @@ -655,13 +557,6 @@ class StorageManagerService extends IStorageManager.Stub break; } case H_FSTRIM: { - if (!isReady()) { - Slog.i(TAG, "fstrim requested, but no daemon connection yet; trying again"); - sendMessageDelayed(obtainMessage(H_FSTRIM, msg.obj), - DateUtils.SECOND_IN_MILLIS); - break; - } - Slog.i(TAG, "Running fstrim idle maintenance"); // Remember when we kicked it off @@ -687,12 +582,8 @@ class StorageManagerService extends IStorageManager.Stub final IStorageShutdownObserver obs = (IStorageShutdownObserver) msg.obj; boolean success = false; try { - if (ENABLE_BINDER) { - mVold.shutdown(); - success = true; - } else { - success = mConnector.execute("volume", "shutdown").isClassOk(); - } + mVold.shutdown(); + success = true; } catch (Exception e) { Slog.wtf(TAG, e); } @@ -711,12 +602,7 @@ class StorageManagerService extends IStorageManager.Stub break; } try { - if (ENABLE_BINDER) { - mVold.mount(vol.id, vol.mountFlags, vol.mountUserId); - } else { - mConnector.execute("volume", "mount", vol.id, vol.mountFlags, - vol.mountUserId); - } + mVold.mount(vol.id, vol.mountFlags, vol.mountUserId); } catch (Exception e) { Slog.wtf(TAG, e); } @@ -778,11 +664,7 @@ class StorageManagerService extends IStorageManager.Stub if (Intent.ACTION_USER_ADDED.equals(action)) { final UserManager um = mContext.getSystemService(UserManager.class); final int userSerialNumber = um.getUserSerialNumber(userId); - if (ENABLE_BINDER) { - mVold.onUserAdded(userId, userSerialNumber); - } else { - mConnector.execute("volume", "user_added", userId, userSerialNumber); - } + mVold.onUserAdded(userId, userSerialNumber); } else if (Intent.ACTION_USER_REMOVED.equals(action)) { synchronized (mVolumes) { final int size = mVolumes.size(); @@ -794,11 +676,7 @@ class StorageManagerService extends IStorageManager.Stub } } } - if (ENABLE_BINDER) { - mVold.onUserRemoved(userId); - } else { - mConnector.execute("volume", "user_removed", userId); - } + mVold.onUserRemoved(userId); } } catch (Exception e) { Slog.wtf(TAG, e); @@ -806,22 +684,6 @@ class StorageManagerService extends IStorageManager.Stub } }; - @Override - public void waitForAsecScan() { - waitForLatch(mAsecsScanned, "mAsecsScanned"); - } - - private void waitForReady() { - waitForLatch(mConnectedSignal, "mConnectedSignal"); - } - - private void waitForLatch(CountDownLatch latch, String condition) { - try { - waitForLatch(latch, condition, -1); - } catch (TimeoutException ignored) { - } - } - private void waitForLatch(CountDownLatch latch, String condition, long timeoutMillis) throws TimeoutException { final long startMillis = SystemClock.elapsedRealtime(); @@ -843,14 +705,6 @@ class StorageManagerService extends IStorageManager.Stub } } - private boolean isReady() { - try { - return mConnectedSignal.await(0, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - return false; - } - } - private void handleSystemReady() { initIfReadyAndConnected(); resetIfReadyAndConnected(); @@ -917,19 +771,10 @@ class StorageManagerService extends IStorageManager.Stub for (UserInfo user : users) { try { if (initLocked) { - if (ENABLE_BINDER) { - mVold.lockUserKey(user.id); - } else { - mCryptConnector.execute("cryptfs", "lock_user_key", user.id); - } + mVold.lockUserKey(user.id); } else { - if (ENABLE_BINDER) { - mVold.unlockUserKey(user.id, user.serialNumber, encodeBytes(null), - encodeBytes(null)); - } else { - mCryptConnector.execute("cryptfs", "unlock_user_key", user.id, - user.serialNumber, "!", "!"); - } + mVold.unlockUserKey(user.id, user.serialNumber, encodeBytes(null), + encodeBytes(null)); } } catch (Exception e) { Slog.wtf(TAG, e); @@ -956,26 +801,14 @@ class StorageManagerService extends IStorageManager.Stub } try { - if (ENABLE_BINDER) { - mVold.reset(); - } else { - mConnector.execute("volume", "reset"); - } + mVold.reset(); // Tell vold about all existing and started users for (UserInfo user : users) { - if (ENABLE_BINDER) { - mVold.onUserAdded(user.id, user.serialNumber); - } else { - mConnector.execute("volume", "user_added", user.id, user.serialNumber); - } + mVold.onUserAdded(user.id, user.serialNumber); } for (int userId : systemUnlockedUsers) { - if (ENABLE_BINDER) { - mVold.onUserStarted(userId); - } else { - mConnector.execute("volume", "user_started", userId); - } + mVold.onUserStarted(userId); } } catch (Exception e) { Slog.wtf(TAG, e); @@ -990,11 +823,7 @@ class StorageManagerService extends IStorageManager.Stub // staging area is ready so it's ready for zygote-forked apps to // bind mount against. try { - if (ENABLE_BINDER) { - mVold.onUserStarted(userId); - } else { - mConnector.execute("volume", "user_started", userId); - } + mVold.onUserStarted(userId); } catch (Exception e) { Slog.wtf(TAG, e); } @@ -1020,11 +849,7 @@ class StorageManagerService extends IStorageManager.Stub Slog.d(TAG, "onCleanupUser " + userId); try { - if (ENABLE_BINDER) { - mVold.onUserStopped(userId); - } else { - mConnector.execute("volume", "user_stopped", userId); - } + mVold.onUserStopped(userId); } catch (Exception e) { Slog.wtf(TAG, e); } @@ -1050,10 +875,6 @@ class StorageManagerService extends IStorageManager.Stub return mLastMaintenance; } - /** - * Callback from NativeDaemonConnector - */ - @Override public void onDaemonConnected() { mDaemonConnected = true; mHandler.obtainMessage(H_DAEMON_CONNECTED).sendToTarget(); @@ -1063,29 +884,11 @@ class StorageManagerService extends IStorageManager.Stub initIfReadyAndConnected(); resetIfReadyAndConnected(); - /* - * Now that we've done our initialization, release - * the hounds! - */ - mConnectedSignal.countDown(); - if (mConnectedSignal.getCount() != 0) { - // More daemons need to connect - return; - } - // On an encrypted device we can't see system properties yet, so pull // the system locale out of the mount service. if ("".equals(SystemProperties.get("vold.encrypt_progress"))) { copyLocaleFromMountService(); } - - // Let package manager load internal ASECs. - if (ASEC_ENABLE) { - mPms.scanAvailableAsecs(); - } - - // Notify people waiting for ASECs to be scanned that it's done. - mAsecsScanned.countDown(); } private void copyLocaleFromMountService() { @@ -1114,148 +917,6 @@ class StorageManagerService extends IStorageManager.Stub SystemProperties.set("persist.sys.locale", locale.toLanguageTag()); } - /** - * Callback from NativeDaemonConnector - */ - @Override - public boolean onCheckHoldWakeLock(int code) { - return false; - } - - /** - * Callback from NativeDaemonConnector - */ - @Override - public boolean onEvent(int code, String raw, String[] cooked) { - synchronized (mLock) { - try { - return onEventLocked(code, raw, cooked); - } catch (RemoteException e) { - throw e.rethrowAsRuntimeException(); - } - } - } - - private boolean onEventLocked(int code, String raw, String[] cooked) throws RemoteException { - switch (code) { - case VoldResponseCode.DISK_CREATED: { - if (cooked.length != 3) break; - final String diskId = cooked[1]; - final int flags = Integer.parseInt(cooked[2]); - mListener.onDiskCreated(diskId, flags); - break; - } - case VoldResponseCode.DISK_SIZE_CHANGED: { - if (cooked.length != 3) break; - final DiskInfo disk = mDisks.get(cooked[1]); - if (disk != null) { - disk.size = Long.parseLong(cooked[2]); - } - break; - } - case VoldResponseCode.DISK_LABEL_CHANGED: { - final DiskInfo disk = mDisks.get(cooked[1]); - if (disk != null) { - final StringBuilder builder = new StringBuilder(); - for (int i = 2; i < cooked.length; i++) { - builder.append(cooked[i]).append(' '); - } - disk.label = builder.toString().trim(); - } - break; - } - case VoldResponseCode.DISK_SCANNED: { - if (cooked.length != 2) break; - final String diskId = cooked[1]; - mListener.onDiskScanned(diskId); - break; - } - case VoldResponseCode.DISK_SYS_PATH_CHANGED: { - if (cooked.length != 3) break; - final DiskInfo disk = mDisks.get(cooked[1]); - if (disk != null) { - disk.sysPath = cooked[2]; - } - break; - } - case VoldResponseCode.DISK_DESTROYED: { - if (cooked.length != 2) break; - final String diskId = cooked[1]; - mListener.onDiskDestroyed(diskId); - break; - } - - case VoldResponseCode.VOLUME_CREATED: { - final String volId = cooked[1]; - final int type = Integer.parseInt(cooked[2]); - final String diskId = TextUtils.nullIfEmpty(cooked[3]); - final String partGuid = TextUtils.nullIfEmpty(cooked[4]); - mListener.onVolumeCreated(volId, type, diskId, partGuid); - break; - } - case VoldResponseCode.VOLUME_STATE_CHANGED: { - if (cooked.length != 3) break; - final String volId = cooked[1]; - final int state = Integer.parseInt(cooked[2]); - mListener.onVolumeStateChanged(volId, state); - break; - } - case VoldResponseCode.VOLUME_FS_TYPE_CHANGED: { - if (cooked.length != 3) break; - final VolumeInfo vol = mVolumes.get(cooked[1]); - if (vol != null) { - vol.fsType = cooked[2]; - } - break; - } - case VoldResponseCode.VOLUME_FS_UUID_CHANGED: { - if (cooked.length != 3) break; - final VolumeInfo vol = mVolumes.get(cooked[1]); - if (vol != null) { - vol.fsUuid = cooked[2]; - } - break; - } - case VoldResponseCode.VOLUME_FS_LABEL_CHANGED: { - final VolumeInfo vol = mVolumes.get(cooked[1]); - if (vol != null) { - final StringBuilder builder = new StringBuilder(); - for (int i = 2; i < cooked.length; i++) { - builder.append(cooked[i]).append(' '); - } - vol.fsLabel = builder.toString().trim(); - } - // TODO: notify listeners that label changed - break; - } - case VoldResponseCode.VOLUME_PATH_CHANGED: { - if (cooked.length != 3) break; - final String volId = cooked[1]; - final String path = cooked[2]; - mListener.onVolumePathChanged(volId, path); - break; - } - case VoldResponseCode.VOLUME_INTERNAL_PATH_CHANGED: { - if (cooked.length != 3) break; - final String volId = cooked[1]; - final String internalPath = cooked[2]; - mListener.onVolumeInternalPathChanged(volId, internalPath); - break; - } - case VoldResponseCode.VOLUME_DESTROYED: { - if (cooked.length != 2) break; - final String volId = cooked[1]; - mListener.onVolumeDestroyed(volId); - break; - } - default: { - Slog.d(TAG, "Unhandled vold event " + code); - } - } - - return true; - } - private final IVoldListener mListener = new IVoldListener.Stub() { @Override public void onDiskCreated(String diskId, int flags) { @@ -1654,24 +1315,6 @@ class StorageManagerService extends IStorageManager.Stub LocalServices.addService(StorageManagerInternal.class, mStorageManagerInternal); - /* - * Create the connection to vold with a maximum queue of twice the - * amount of containers we'd ever expect to have. This keeps an - * "asec list" from blocking a thread repeatedly. - */ - - mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25, - null); - mConnector.setDebug(true); - mConnector.setWarnIfHeld(mLock); - mConnectorThread = new Thread(mConnector, VOLD_TAG); - - // Reuse parameters from first connector since they are tested and safe - mCryptConnector = new NativeDaemonConnector(this, "cryptd", - MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null); - mCryptConnector.setDebug(true); - mCryptConnectorThread = new Thread(mCryptConnector, CRYPTD_TAG); - final IntentFilter userFilter = new IntentFilter(); userFilter.addAction(Intent.ACTION_USER_ADDED); userFilter.addAction(Intent.ACTION_USER_REMOVED); @@ -1689,8 +1332,6 @@ class StorageManagerService extends IStorageManager.Stub private void start() { connect(); - mConnectorThread.start(); - mCryptConnectorThread.start(); } private void connect() { @@ -1713,6 +1354,7 @@ class StorageManagerService extends IStorageManager.Stub mVold = IVold.Stub.asInterface(binder); try { mVold.setListener(mListener); + onDaemonConnected(); return; } catch (RemoteException e) { Slog.w(TAG, "vold listener rejected; trying again", e); @@ -1864,62 +1506,15 @@ class StorageManagerService extends IStorageManager.Stub } @Override - public boolean isUsbMassStorageConnected() { - throw new UnsupportedOperationException(); - } - - @Override - public void setUsbMassStorageEnabled(boolean enable) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isUsbMassStorageEnabled() { - throw new UnsupportedOperationException(); - } - - @Override - public String getVolumeState(String mountPoint) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isExternalStorageEmulated() { - throw new UnsupportedOperationException(); - } - - @Override - public int mountVolume(String path) { - mount(findVolumeIdForPathOrThrow(path)); - return 0; - } - - @Override - public void unmountVolume(String path, boolean force, boolean removeEncryption) { - unmount(findVolumeIdForPathOrThrow(path)); - } - - @Override - public int formatVolume(String path) { - format(findVolumeIdForPathOrThrow(path)); - return 0; - } - - @Override public void mount(String volId) { enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); - waitForReady(); final VolumeInfo vol = findVolumeByIdOrThrow(volId); if (isMountDisallowed(vol)) { throw new SecurityException("Mounting " + volId + " restricted by policy"); } try { - if (ENABLE_BINDER) { - mVold.mount(vol.id, vol.mountFlags, vol.mountUserId); - } else { - mConnector.execute("volume", "mount", vol.id, vol.mountFlags, vol.mountUserId); - } + mVold.mount(vol.id, vol.mountFlags, vol.mountUserId); } catch (Exception e) { Slog.wtf(TAG, e); } @@ -1928,31 +1523,10 @@ class StorageManagerService extends IStorageManager.Stub @Override public void unmount(String volId) { enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); - waitForReady(); final VolumeInfo vol = findVolumeByIdOrThrow(volId); - - // TODO: expand PMS to know about multiple volumes - if (vol.isPrimaryPhysical()) { - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mUnmountLock) { - mUnmountSignal = new CountDownLatch(1); - mPms.updateExternalMediaStatus(false, true); - waitForLatch(mUnmountSignal, "mUnmountSignal"); - mUnmountSignal = null; - } - } finally { - Binder.restoreCallingIdentity(ident); - } - } - try { - if (ENABLE_BINDER) { - mVold.unmount(vol.id); - } else { - mConnector.execute("volume", "unmount", vol.id); - } + mVold.unmount(vol.id); } catch (Exception e) { Slog.wtf(TAG, e); } @@ -1961,15 +1535,10 @@ class StorageManagerService extends IStorageManager.Stub @Override public void format(String volId) { enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); - waitForReady(); final VolumeInfo vol = findVolumeByIdOrThrow(volId); try { - if (ENABLE_BINDER) { - mVold.format(vol.id, "auto"); - } else { - mConnector.execute("volume", "format", vol.id, "auto"); - } + mVold.format(vol.id, "auto"); } catch (Exception e) { Slog.wtf(TAG, e); } @@ -1978,7 +1547,6 @@ class StorageManagerService extends IStorageManager.Stub @Override public long benchmark(String volId) { enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); - waitForReady(); // TODO: refactor for callers to provide a listener try { @@ -2022,15 +1590,10 @@ class StorageManagerService extends IStorageManager.Stub @Override public void partitionPublic(String diskId) { enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); - waitForReady(); final CountDownLatch latch = findOrCreateDiskScanLatch(diskId); try { - if (ENABLE_BINDER) { - mVold.partition(diskId, IVold.PARTITION_TYPE_PUBLIC, -1); - } else { - mConnector.execute("volume", "partition", diskId, "public"); - } + mVold.partition(diskId, IVold.PARTITION_TYPE_PUBLIC, -1); waitForLatch(latch, "partitionPublic", 3 * DateUtils.MINUTE_IN_MILLIS); } catch (Exception e) { Slog.wtf(TAG, e); @@ -2041,15 +1604,10 @@ class StorageManagerService extends IStorageManager.Stub public void partitionPrivate(String diskId) { enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); enforceAdminUser(); - waitForReady(); final CountDownLatch latch = findOrCreateDiskScanLatch(diskId); try { - if (ENABLE_BINDER) { - mVold.partition(diskId, IVold.PARTITION_TYPE_PRIVATE, -1); - } else { - mConnector.execute("volume", "partition", diskId, "private"); - } + mVold.partition(diskId, IVold.PARTITION_TYPE_PRIVATE, -1); waitForLatch(latch, "partitionPrivate", 3 * DateUtils.MINUTE_IN_MILLIS); } catch (Exception e) { Slog.wtf(TAG, e); @@ -2060,15 +1618,10 @@ class StorageManagerService extends IStorageManager.Stub public void partitionMixed(String diskId, int ratio) { enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); enforceAdminUser(); - waitForReady(); final CountDownLatch latch = findOrCreateDiskScanLatch(diskId); try { - if (ENABLE_BINDER) { - mVold.partition(diskId, IVold.PARTITION_TYPE_MIXED, ratio); - } else { - mConnector.execute("volume", "partition", diskId, "mixed", ratio); - } + mVold.partition(diskId, IVold.PARTITION_TYPE_MIXED, ratio); waitForLatch(latch, "partitionMixed", 3 * DateUtils.MINUTE_IN_MILLIS); } catch (Exception e) { Slog.wtf(TAG, e); @@ -2078,7 +1631,6 @@ class StorageManagerService extends IStorageManager.Stub @Override public void setVolumeNickname(String fsUuid, String nickname) { enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); - waitForReady(); Preconditions.checkNotNull(fsUuid); synchronized (mLock) { @@ -2092,7 +1644,6 @@ class StorageManagerService extends IStorageManager.Stub @Override public void setVolumeUserFlags(String fsUuid, int flags, int mask) { enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); - waitForReady(); Preconditions.checkNotNull(fsUuid); synchronized (mLock) { @@ -2106,7 +1657,6 @@ class StorageManagerService extends IStorageManager.Stub @Override public void forgetVolume(String fsUuid) { enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); - waitForReady(); Preconditions.checkNotNull(fsUuid); @@ -2131,7 +1681,6 @@ class StorageManagerService extends IStorageManager.Stub @Override public void forgetAllVolumes() { enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); - waitForReady(); synchronized (mLock) { for (int i = 0; i < mRecords.size(); i++) { @@ -2155,11 +1704,7 @@ class StorageManagerService extends IStorageManager.Stub private void forgetPartition(String partGuid) { try { - if (ENABLE_BINDER) { - mVold.forgetPartition(partGuid); - } else { - mConnector.execute("volume", "forget_partition", partGuid); - } + mVold.forgetPartition(partGuid); } catch (Exception e) { Slog.wtf(TAG, e); } @@ -2168,14 +1713,6 @@ class StorageManagerService extends IStorageManager.Stub @Override public void fstrim(int flags) { enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); - waitForReady(); - - String cmd; - if ((flags & StorageManager.FSTRIM_FLAG_DEEP) != 0) { - cmd = "dodtrim"; - } else { - cmd = "dotrim"; - } try { mVold.fstrim(flags, new IVoldTaskListener.Stub() { @@ -2212,27 +1749,8 @@ class StorageManagerService extends IStorageManager.Stub } private void remountUidExternalStorage(int uid, int mode) { - waitForReady(); - - String modeName = "none"; - switch (mode) { - case Zygote.MOUNT_EXTERNAL_DEFAULT: { - modeName = "default"; - } break; - case Zygote.MOUNT_EXTERNAL_READ: { - modeName = "read"; - } break; - case Zygote.MOUNT_EXTERNAL_WRITE: { - modeName = "write"; - } break; - } - try { - if (ENABLE_BINDER) { - mVold.remountUid(uid, mode); - } else { - mConnector.execute("volume", "remount_uid", uid, modeName); - } + mVold.remountUid(uid, mode); } catch (Exception e) { Slog.wtf(TAG, e); } @@ -2241,7 +1759,6 @@ class StorageManagerService extends IStorageManager.Stub @Override public void setDebugFlags(int flags, int mask) { enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); - waitForReady(); if ((mask & StorageManager.DEBUG_EMULATE_FBE) != 0) { if (!EMULATE_FBE_SUPPORTED) { @@ -2331,7 +1848,6 @@ class StorageManagerService extends IStorageManager.Stub @Override public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) { enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); - waitForReady(); final VolumeInfo from; final VolumeInfo to; @@ -2403,33 +1919,6 @@ class StorageManagerService extends IStorageManager.Stub } } - @Override - public int[] getStorageUsers(String path) { - enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); - waitForReady(); - try { - final String[] r = NativeDaemonEvent.filterMessageList( - mConnector.executeForList("storage", "users", path), - VoldResponseCode.StorageUsersListResult); - - // FMT: <pid> <process name> - int[] data = new int[r.length]; - for (int i = 0; i < r.length; i++) { - String[] tok = r[i].split(" "); - try { - data[i] = Integer.parseInt(tok[0]); - } catch (NumberFormatException nfe) { - Slog.e(TAG, String.format("Error parsing pid %s", tok[0])); - return new int[0]; - } - } - return data; - } catch (NativeDaemonConnectorException e) { - Slog.e(TAG, "Failed to retrieve storage users list", e); - return new int[0]; - } - } - private void warnOnNotMounted() { synchronized (mLock) { for (int i = 0; i < mVolumes.size(); i++) { @@ -2444,304 +1933,6 @@ class StorageManagerService extends IStorageManager.Stub Slog.w(TAG, "No primary storage mounted!"); } - public String[] getSecureContainerList() { - if (!ASEC_ENABLE) throw new UnsupportedOperationException(); - enforcePermission(android.Manifest.permission.ASEC_ACCESS); - waitForReady(); - warnOnNotMounted(); - - try { - return NativeDaemonEvent.filterMessageList( - mConnector.executeForList("asec", "list"), VoldResponseCode.AsecListResult); - } catch (NativeDaemonConnectorException e) { - return new String[0]; - } - } - - public int createSecureContainer(String id, int sizeMb, String fstype, String key, - int ownerUid, boolean external) { - if (!ASEC_ENABLE) throw new UnsupportedOperationException(); - enforcePermission(android.Manifest.permission.ASEC_CREATE); - waitForReady(); - warnOnNotMounted(); - - int rc = StorageResultCode.OperationSucceeded; - try { - mConnector.execute("asec", "create", id, sizeMb, fstype, new SensitiveArg(key), - ownerUid, external ? "1" : "0"); - } catch (NativeDaemonConnectorException e) { - rc = StorageResultCode.OperationFailedInternalError; - } - - if (rc == StorageResultCode.OperationSucceeded) { - synchronized (mAsecMountSet) { - mAsecMountSet.add(id); - } - } - return rc; - } - - @Override - public int resizeSecureContainer(String id, int sizeMb, String key) { - if (!ASEC_ENABLE) throw new UnsupportedOperationException(); - enforcePermission(android.Manifest.permission.ASEC_CREATE); - waitForReady(); - warnOnNotMounted(); - - int rc = StorageResultCode.OperationSucceeded; - try { - mConnector.execute("asec", "resize", id, sizeMb, new SensitiveArg(key)); - } catch (NativeDaemonConnectorException e) { - rc = StorageResultCode.OperationFailedInternalError; - } - return rc; - } - - public int finalizeSecureContainer(String id) { - if (!ASEC_ENABLE) throw new UnsupportedOperationException(); - enforcePermission(android.Manifest.permission.ASEC_CREATE); - warnOnNotMounted(); - - int rc = StorageResultCode.OperationSucceeded; - try { - mConnector.execute("asec", "finalize", id); - /* - * Finalization does a remount, so no need - * to update mAsecMountSet - */ - } catch (NativeDaemonConnectorException e) { - rc = StorageResultCode.OperationFailedInternalError; - } - return rc; - } - - public int fixPermissionsSecureContainer(String id, int gid, String filename) { - if (!ASEC_ENABLE) throw new UnsupportedOperationException(); - enforcePermission(android.Manifest.permission.ASEC_CREATE); - warnOnNotMounted(); - - int rc = StorageResultCode.OperationSucceeded; - try { - mConnector.execute("asec", "fixperms", id, gid, filename); - /* - * Fix permissions does a remount, so no need to update - * mAsecMountSet - */ - } catch (NativeDaemonConnectorException e) { - rc = StorageResultCode.OperationFailedInternalError; - } - return rc; - } - - public int destroySecureContainer(String id, boolean force) { - if (!ASEC_ENABLE) throw new UnsupportedOperationException(); - enforcePermission(android.Manifest.permission.ASEC_DESTROY); - waitForReady(); - warnOnNotMounted(); - - /* - * Force a GC to make sure AssetManagers in other threads of the - * system_server are cleaned up. We have to do this since AssetManager - * instances are kept as a WeakReference and it's possible we have files - * open on the external storage. - */ - Runtime.getRuntime().gc(); - - int rc = StorageResultCode.OperationSucceeded; - try { - final Command cmd = new Command("asec", "destroy", id); - if (force) { - cmd.appendArg("force"); - } - mConnector.execute(cmd); - } catch (NativeDaemonConnectorException e) { - int code = e.getCode(); - if (code == VoldResponseCode.OpFailedStorageBusy) { - rc = StorageResultCode.OperationFailedStorageBusy; - } else { - rc = StorageResultCode.OperationFailedInternalError; - } - } - - if (rc == StorageResultCode.OperationSucceeded) { - synchronized (mAsecMountSet) { - if (mAsecMountSet.contains(id)) { - mAsecMountSet.remove(id); - } - } - } - - return rc; - } - - public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) { - if (!ASEC_ENABLE) throw new UnsupportedOperationException(); - enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT); - waitForReady(); - warnOnNotMounted(); - - synchronized (mAsecMountSet) { - if (mAsecMountSet.contains(id)) { - return StorageResultCode.OperationFailedStorageMounted; - } - } - - int rc = StorageResultCode.OperationSucceeded; - try { - mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid, - readOnly ? "ro" : "rw"); - } catch (NativeDaemonConnectorException e) { - int code = e.getCode(); - if (code != VoldResponseCode.OpFailedStorageBusy) { - rc = StorageResultCode.OperationFailedInternalError; - } - } - - if (rc == StorageResultCode.OperationSucceeded) { - synchronized (mAsecMountSet) { - mAsecMountSet.add(id); - } - } - return rc; - } - - public int unmountSecureContainer(String id, boolean force) { - if (!ASEC_ENABLE) throw new UnsupportedOperationException(); - enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT); - waitForReady(); - warnOnNotMounted(); - - synchronized (mAsecMountSet) { - if (!mAsecMountSet.contains(id)) { - return StorageResultCode.OperationFailedStorageNotMounted; - } - } - - /* - * Force a GC to make sure AssetManagers in other threads of the - * system_server are cleaned up. We have to do this since AssetManager - * instances are kept as a WeakReference and it's possible we have files - * open on the external storage. - */ - Runtime.getRuntime().gc(); - - int rc = StorageResultCode.OperationSucceeded; - try { - final Command cmd = new Command("asec", "unmount", id); - if (force) { - cmd.appendArg("force"); - } - mConnector.execute(cmd); - } catch (NativeDaemonConnectorException e) { - int code = e.getCode(); - if (code == VoldResponseCode.OpFailedStorageBusy) { - rc = StorageResultCode.OperationFailedStorageBusy; - } else { - rc = StorageResultCode.OperationFailedInternalError; - } - } - - if (rc == StorageResultCode.OperationSucceeded) { - synchronized (mAsecMountSet) { - mAsecMountSet.remove(id); - } - } - return rc; - } - - public boolean isSecureContainerMounted(String id) { - if (!ASEC_ENABLE) throw new UnsupportedOperationException(); - enforcePermission(android.Manifest.permission.ASEC_ACCESS); - waitForReady(); - warnOnNotMounted(); - - synchronized (mAsecMountSet) { - return mAsecMountSet.contains(id); - } - } - - public int renameSecureContainer(String oldId, String newId) { - if (!ASEC_ENABLE) throw new UnsupportedOperationException(); - enforcePermission(android.Manifest.permission.ASEC_RENAME); - waitForReady(); - warnOnNotMounted(); - - synchronized (mAsecMountSet) { - /* - * Because a mounted container has active internal state which cannot be - * changed while active, we must ensure both ids are not currently mounted. - */ - if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) { - return StorageResultCode.OperationFailedStorageMounted; - } - } - - int rc = StorageResultCode.OperationSucceeded; - try { - mConnector.execute("asec", "rename", oldId, newId); - } catch (NativeDaemonConnectorException e) { - rc = StorageResultCode.OperationFailedInternalError; - } - - return rc; - } - - public String getSecureContainerPath(String id) { - if (!ASEC_ENABLE) throw new UnsupportedOperationException(); - enforcePermission(android.Manifest.permission.ASEC_ACCESS); - waitForReady(); - warnOnNotMounted(); - - final NativeDaemonEvent event; - try { - event = mConnector.execute("asec", "path", id); - event.checkCode(VoldResponseCode.AsecPathResult); - return event.getMessage(); - } catch (NativeDaemonConnectorException e) { - int code = e.getCode(); - if (code == VoldResponseCode.OpFailedStorageNotFound) { - Slog.i(TAG, String.format("Container '%s' not found", id)); - return null; - } else { - throw new IllegalStateException(String.format("Unexpected response code %d", code)); - } - } - } - - public String getSecureContainerFilesystemPath(String id) { - if (!ASEC_ENABLE) throw new UnsupportedOperationException(); - enforcePermission(android.Manifest.permission.ASEC_ACCESS); - waitForReady(); - warnOnNotMounted(); - - final NativeDaemonEvent event; - try { - event = mConnector.execute("asec", "fspath", id); - event.checkCode(VoldResponseCode.AsecPathResult); - return event.getMessage(); - } catch (NativeDaemonConnectorException e) { - int code = e.getCode(); - if (code == VoldResponseCode.OpFailedStorageNotFound) { - Slog.i(TAG, String.format("Container '%s' not found", id)); - return null; - } else { - throw new IllegalStateException(String.format("Unexpected response code %d", code)); - } - } - } - - @Override - public void finishMediaUpdate() { - if (Binder.getCallingUid() != Process.SYSTEM_UID) { - throw new SecurityException("no permission to call finishMediaUpdate()"); - } - if (mUnmountSignal != null) { - mUnmountSignal.countDown(); - } else { - Slog.w(TAG, "Odd, nobody asked to unmount?"); - } - } - private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) { if (callerUid == android.os.Process.SYSTEM_UID) { return true; @@ -2762,10 +1953,10 @@ class StorageManagerService extends IStorageManager.Stub return callerUid == packageUid; } + @Override public String getMountedObbPath(String rawPath) { Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); - waitForReady(); warnOnNotMounted(); final ObbState state; @@ -2777,23 +1968,7 @@ class StorageManagerService extends IStorageManager.Stub return null; } - if (ENABLE_BINDER) { - return findVolumeByIdOrThrow(state.volId).getPath().getAbsolutePath(); - } - - final NativeDaemonEvent event; - try { - event = mConnector.execute("obb", "path", state.canonicalPath); - event.checkCode(VoldResponseCode.AsecPathResult); - return event.getMessage(); - } catch (NativeDaemonConnectorException e) { - int code = e.getCode(); - if (code == VoldResponseCode.OpFailedStorageNotFound) { - return null; - } else { - throw new IllegalStateException(String.format("Unexpected response code %d", code)); - } - } + return findVolumeByIdOrThrow(state.volId).getPath().getAbsolutePath(); } @Override @@ -2850,28 +2025,10 @@ class StorageManagerService extends IStorageManager.Stub mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, "no permission to access the crypt keeper"); - waitForReady(); - - if (ENABLE_BINDER) { - try { - return mVold.fdeComplete(); - } catch (Exception e) { - Slog.wtf(TAG, e); - return StorageManager.ENCRYPTION_STATE_ERROR_UNKNOWN; - } - } - - final NativeDaemonEvent event; try { - event = mCryptConnector.execute("cryptfs", "cryptocomplete"); - return Integer.parseInt(event.getMessage()); - } catch (NumberFormatException e) { - // Bad result - unexpected. - Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete"); - return StorageManager.ENCRYPTION_STATE_ERROR_UNKNOWN; - } catch (NativeDaemonConnectorException e) { - // Something bad happened. - Slog.w(TAG, "Error in communicating with cryptfs in validating"); + return mVold.fdeComplete(); + } catch (Exception e) { + Slog.wtf(TAG, e); return StorageManager.ENCRYPTION_STATE_ERROR_UNKNOWN; } } @@ -2881,8 +2038,6 @@ class StorageManagerService extends IStorageManager.Stub mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, "no permission to access the crypt keeper"); - waitForReady(); - if (TextUtils.isEmpty(password)) { throw new IllegalArgumentException("password cannot be empty"); } @@ -2891,55 +2046,27 @@ class StorageManagerService extends IStorageManager.Stub Slog.i(TAG, "decrypting storage..."); } - if (ENABLE_BINDER) { - try { - mVold.fdeCheckPassword(password); - mHandler.postDelayed(() -> { - try { - mVold.fdeRestart(); - } catch (Exception e) { - Slog.wtf(TAG, e); - } - }, DateUtils.SECOND_IN_MILLIS); - return 0; - } catch (Exception e) { - Slog.wtf(TAG, e); - return StorageManager.ENCRYPTION_STATE_ERROR_UNKNOWN; - } - } - - final NativeDaemonEvent event; try { - event = mCryptConnector.execute("cryptfs", "checkpw", new SensitiveArg(password)); - - final int code = Integer.parseInt(event.getMessage()); - if (code == 0) { - // Decrypt was successful. Post a delayed message before restarting in order - // to let the UI to clear itself - mHandler.postDelayed(new Runnable() { - public void run() { - try { - mCryptConnector.execute("cryptfs", "restart"); - } catch (NativeDaemonConnectorException e) { - Slog.e(TAG, "problem executing in background", e); - } - } - }, 1000); // 1 second - } - - return code; - } catch (NativeDaemonConnectorException e) { - // Decryption failed - return e.getCode(); + mVold.fdeCheckPassword(password); + mHandler.postDelayed(() -> { + try { + mVold.fdeRestart(); + } catch (Exception e) { + Slog.wtf(TAG, e); + } + }, DateUtils.SECOND_IN_MILLIS); + return 0; + } catch (Exception e) { + Slog.wtf(TAG, e); + return StorageManager.ENCRYPTION_STATE_ERROR_UNKNOWN; } } + @Override public int encryptStorage(int type, String password) { mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, "no permission to access the crypt keeper"); - waitForReady(); - if (type == StorageManager.CRYPT_TYPE_DEFAULT) { password = ""; } else if (TextUtils.isEmpty(password)) { @@ -2951,17 +2078,7 @@ class StorageManagerService extends IStorageManager.Stub } try { - if (ENABLE_BINDER) { - mVold.fdeEnable(type, password, IVold.ENCRYPTION_FLAG_IN_PLACE); - } else { - if (type == StorageManager.CRYPT_TYPE_DEFAULT) { - mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", - CRYPTO_TYPES[type]); - } else { - mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", - CRYPTO_TYPES[type], new SensitiveArg(password)); - } - } + mVold.fdeEnable(type, password, IVold.ENCRYPTION_FLAG_IN_PLACE); } catch (Exception e) { Slog.wtf(TAG, e); return -1; @@ -2974,12 +2091,11 @@ class StorageManagerService extends IStorageManager.Stub * @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager. * @param password The password to set. */ + @Override public int changeEncryptionPassword(int type, String password) { mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, "no permission to access the crypt keeper"); - waitForReady(); - if (type == StorageManager.CRYPT_TYPE_DEFAULT) { password = ""; } else if (TextUtils.isEmpty(password)) { @@ -2990,23 +2106,12 @@ class StorageManagerService extends IStorageManager.Stub Slog.i(TAG, "changing encryption password..."); } - if (ENABLE_BINDER) { - try { - mVold.fdeChangePassword(type, password); - return 0; - } catch (Exception e) { - Slog.wtf(TAG, e); - return -1; - } - } - try { - NativeDaemonEvent event = mCryptConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type], - new SensitiveArg(password)); - return Integer.parseInt(event.getMessage()); - } catch (NativeDaemonConnectorException e) { - // Encryption failed - return e.getCode(); + mVold.fdeChangePassword(type, password); + return 0; + } catch (Exception e) { + Slog.wtf(TAG, e); + return -1; } } @@ -3027,30 +2132,16 @@ class StorageManagerService extends IStorageManager.Stub throw new IllegalArgumentException("password cannot be empty"); } - waitForReady(); - if (DEBUG_EVENTS) { Slog.i(TAG, "validating encryption password..."); } - if (ENABLE_BINDER) { - try { - mVold.fdeVerifyPassword(password); - return 0; - } catch (Exception e) { - Slog.wtf(TAG, e); - return -1; - } - } - - final NativeDaemonEvent event; try { - event = mCryptConnector.execute("cryptfs", "verifypw", new SensitiveArg(password)); - Slog.i(TAG, "cryptfs verifypw => " + event.getMessage()); - return Integer.parseInt(event.getMessage()); - } catch (NativeDaemonConnectorException e) { - // Encryption failed - return e.getCode(); + mVold.fdeVerifyPassword(password); + return 0; + } catch (Exception e) { + Slog.wtf(TAG, e); + return -1; } } @@ -3063,28 +2154,11 @@ class StorageManagerService extends IStorageManager.Stub mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, "no permission to access the crypt keeper"); - waitForReady(); - - if (ENABLE_BINDER) { - try { - return mVold.fdeGetPasswordType(); - } catch (Exception e) { - Slog.wtf(TAG, e); - return -1; - } - } - - final NativeDaemonEvent event; try { - event = mCryptConnector.execute("cryptfs", "getpwtype"); - for (int i = 0; i < CRYPTO_TYPES.length; ++i) { - if (CRYPTO_TYPES[i].equals(event.getMessage())) - return i; - } - - throw new IllegalStateException("unexpected return from cryptfs"); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + return mVold.fdeGetPasswordType(); + } catch (Exception e) { + Slog.wtf(TAG, e); + return -1; } } @@ -3098,23 +2172,12 @@ class StorageManagerService extends IStorageManager.Stub mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, "no permission to access the crypt keeper"); - waitForReady(); - - if (ENABLE_BINDER) { - try { - mVold.fdeSetField(field, contents); - return; - } catch (Exception e) { - Slog.wtf(TAG, e); - return; - } - } - - final NativeDaemonEvent event; try { - event = mCryptConnector.execute("cryptfs", "setfield", field, contents); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + mVold.fdeSetField(field, contents); + return; + } catch (Exception e) { + Slog.wtf(TAG, e); + return; } } @@ -3128,29 +2191,11 @@ class StorageManagerService extends IStorageManager.Stub mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, "no permission to access the crypt keeper"); - waitForReady(); - - if (ENABLE_BINDER) { - try { - return mVold.fdeGetField(field); - } catch (Exception e) { - Slog.wtf(TAG, e); - return null; - } - } - - final NativeDaemonEvent event; try { - final String[] contents = NativeDaemonEvent.filterMessageList( - mCryptConnector.executeForList("cryptfs", "getfield", field), - VoldResponseCode.CryptfsGetfieldResult); - String result = new String(); - for (String content : contents) { - result += content; - } - return result; - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + return mVold.fdeGetField(field); + } catch (Exception e) { + Slog.wtf(TAG, e); + return null; } } @@ -3163,23 +2208,11 @@ class StorageManagerService extends IStorageManager.Stub mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, "no permission to access the crypt keeper"); - waitForReady(); - - if (ENABLE_BINDER) { - try { - return mVold.isConvertibleToFbe(); - } catch (Exception e) { - Slog.wtf(TAG, e); - return false; - } - } - - final NativeDaemonEvent event; try { - event = mCryptConnector.execute("cryptfs", "isConvertibleToFBE"); - return Integer.parseInt(event.getMessage()) != 0; - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + return mVold.isConvertibleToFbe(); + } catch (Exception e) { + Slog.wtf(TAG, e); + return false; } } @@ -3188,31 +2221,10 @@ class StorageManagerService extends IStorageManager.Stub mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, "only keyguard can retrieve password"); - if (!isReady()) { - return new String(); - } - - if (ENABLE_BINDER) { - try { - return mVold.fdeGetPassword(); - } catch (Exception e) { - Slog.wtf(TAG, e); - return null; - } - } - - final NativeDaemonEvent event; try { - event = mCryptConnector.execute("cryptfs", "getpw"); - if ("-1".equals(event.getMessage())) { - // -1 equals no password - return null; - } - return event.getMessage(); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); - } catch (IllegalArgumentException e) { - Slog.e(TAG, "Invalid response to getPassword"); + return mVold.fdeGetPassword(); + } catch (Exception e) { + Slog.wtf(TAG, e); return null; } } @@ -3222,40 +2234,21 @@ class StorageManagerService extends IStorageManager.Stub mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, "only keyguard can clear password"); - if (!isReady()) { - return; - } - - if (ENABLE_BINDER) { - try { - mVold.fdeClearPassword(); - return; - } catch (Exception e) { - Slog.wtf(TAG, e); - return; - } - } - - final NativeDaemonEvent event; try { - event = mCryptConnector.execute("cryptfs", "clearpw"); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + mVold.fdeClearPassword(); + return; + } catch (Exception e) { + Slog.wtf(TAG, e); + return; } } @Override public void createUserKey(int userId, int serialNumber, boolean ephemeral) { enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); - waitForReady(); try { - if (ENABLE_BINDER) { - mVold.createUserKey(userId, serialNumber, ephemeral); - } else { - mCryptConnector.execute("cryptfs", "create_user_key", userId, serialNumber, - ephemeral ? 1 : 0); - } + mVold.createUserKey(userId, serialNumber, ephemeral); } catch (Exception e) { Slog.wtf(TAG, e); } @@ -3264,14 +2257,9 @@ class StorageManagerService extends IStorageManager.Stub @Override public void destroyUserKey(int userId) { enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); - waitForReady(); try { - if (ENABLE_BINDER) { - mVold.destroyUserKey(userId); - } else { - mCryptConnector.execute("cryptfs", "destroy_user_key", userId); - } + mVold.destroyUserKey(userId); } catch (Exception e) { Slog.wtf(TAG, e); } @@ -3295,16 +2283,9 @@ class StorageManagerService extends IStorageManager.Stub @Override public void addUserKeyAuth(int userId, int serialNumber, byte[] token, byte[] secret) { enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); - waitForReady(); try { - if (ENABLE_BINDER) { - mVold.addUserKeyAuth(userId, serialNumber, encodeBytes(token), encodeBytes(secret)); - } else { - mCryptConnector.execute("cryptfs", "add_user_key_auth", userId, serialNumber, - new SensitiveArg(encodeBytes(token)), - new SensitiveArg(encodeBytes(secret))); - } + mVold.addUserKeyAuth(userId, serialNumber, encodeBytes(token), encodeBytes(secret)); } catch (Exception e) { Slog.wtf(TAG, e); } @@ -3316,14 +2297,9 @@ class StorageManagerService extends IStorageManager.Stub @Override public void fixateNewestUserKeyAuth(int userId) { enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); - waitForReady(); try { - if (ENABLE_BINDER) { - mVold.fixateNewestUserKeyAuth(userId); - } else { - mCryptConnector.execute("cryptfs", "fixate_newest_user_key_auth", userId); - } + mVold.fixateNewestUserKeyAuth(userId); } catch (Exception e) { Slog.wtf(TAG, e); } @@ -3332,7 +2308,6 @@ class StorageManagerService extends IStorageManager.Stub @Override public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) { enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); - waitForReady(); if (StorageManager.isFileEncryptedNativeOrEmulated()) { // When a user has secure lock screen, require secret to actually unlock. @@ -3342,14 +2317,8 @@ class StorageManagerService extends IStorageManager.Stub } try { - if (ENABLE_BINDER) { - mVold.unlockUserKey(userId, serialNumber, encodeBytes(token), - encodeBytes(secret)); - } else { - mCryptConnector.execute("cryptfs", "unlock_user_key", userId, serialNumber, - new SensitiveArg(encodeBytes(token)), - new SensitiveArg(encodeBytes(secret))); - } + mVold.unlockUserKey(userId, serialNumber, encodeBytes(token), + encodeBytes(secret)); } catch (Exception e) { Slog.wtf(TAG, e); return; @@ -3369,14 +2338,9 @@ class StorageManagerService extends IStorageManager.Stub @Override public void lockUserKey(int userId) { enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); - waitForReady(); try { - if (ENABLE_BINDER) { - mVold.lockUserKey(userId); - } else { - mCryptConnector.execute("cryptfs", "lock_user_key", userId); - } + mVold.lockUserKey(userId); } catch (Exception e) { Slog.wtf(TAG, e); return; @@ -3397,15 +2361,9 @@ class StorageManagerService extends IStorageManager.Stub @Override public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) { enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); - waitForReady(); try { - if (ENABLE_BINDER) { - mVold.prepareUserStorage(volumeUuid, userId, serialNumber, flags); - } else { - mCryptConnector.execute("cryptfs", "prepare_user_storage", escapeNull(volumeUuid), - userId, serialNumber, flags); - } + mVold.prepareUserStorage(volumeUuid, userId, serialNumber, flags); } catch (Exception e) { Slog.wtf(TAG, e); } @@ -3414,15 +2372,9 @@ class StorageManagerService extends IStorageManager.Stub @Override public void destroyUserStorage(String volumeUuid, int userId, int flags) { enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); - waitForReady(); try { - if (ENABLE_BINDER) { - mVold.destroyUserStorage(volumeUuid, userId, flags); - } else { - mCryptConnector.execute("cryptfs", "destroy_user_storage", escapeNull(volumeUuid), - userId, flags); - } + mVold.destroyUserStorage(volumeUuid, userId, flags); } catch (Exception e) { Slog.wtf(TAG, e); } @@ -3431,14 +2383,9 @@ class StorageManagerService extends IStorageManager.Stub @Override public void secdiscard(String path) { enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); - waitForReady(); try { - if (ENABLE_BINDER) { - mVold.secdiscard(path); - } else { - mCryptConnector.execute("cryptfs", "secdiscard", escapeNull(path)); - } + mVold.secdiscard(path); } catch (Exception e) { Slog.wtf(TAG, e); } @@ -3453,33 +2400,18 @@ class StorageManagerService extends IStorageManager.Stub @Override public ParcelFileDescriptor open() throws NativeDaemonConnectorException { - if (ENABLE_BINDER) { - try { - return new ParcelFileDescriptor( - mVold.mountAppFuse(uid, Process.myPid(), mountId)); - } catch (Exception e) { - throw new NativeDaemonConnectorException("Failed to mount", e); - } - } else { - final NativeDaemonEvent event = mConnector.execute( - "appfuse", "mount", uid, Process.myPid(), mountId); - opened = true; - if (event.getFileDescriptors() == null || - event.getFileDescriptors().length == 0) { - throw new NativeDaemonConnectorException("Cannot obtain device FD"); - } - return new ParcelFileDescriptor(event.getFileDescriptors()[0]); + try { + return new ParcelFileDescriptor( + mVold.mountAppFuse(uid, Process.myPid(), mountId)); + } catch (Exception e) { + throw new NativeDaemonConnectorException("Failed to mount", e); } } @Override public void close() throws Exception { if (opened) { - if (ENABLE_BINDER) { - mVold.unmountAppFuse(uid, Process.myPid(), mountId); - } else { - mConnector.execute("appfuse", "unmount", uid, Process.myPid(), mountId); - } + mVold.unmountAppFuse(uid, Process.myPid(), mountId); opened = false; } } @@ -3568,11 +2500,7 @@ class StorageManagerService extends IStorageManager.Stub } try { - if (ENABLE_BINDER) { - mVold.mkdirs(appPath); - } else { - mConnector.execute("volume", "mkdirs", appPath); - } + mVold.mkdirs(appPath); return 0; } catch (Exception e) { Slog.wtf(TAG, e); @@ -4124,7 +3052,6 @@ class StorageManagerService extends IStorageManager.Stub @Override public void handleExecute() throws IOException, RemoteException { - waitForReady(); warnOnNotMounted(); final ObbInfo obbInfo = getObbInfo(); @@ -4174,19 +3101,9 @@ class StorageManagerService extends IStorageManager.Stub int rc = StorageResultCode.OperationSucceeded; try { - if (ENABLE_BINDER) { - mObbState.volId = mVold.createObb(mObbState.canonicalPath, binderKey, - mObbState.ownerGid); - mVold.mount(mObbState.volId, 0, -1); - } else { - mConnector.execute("obb", "mount", mObbState.canonicalPath, - new SensitiveArg(hashedKey), mObbState.ownerGid); - } - } catch (NativeDaemonConnectorException e) { - int code = e.getCode(); - if (code != VoldResponseCode.OpFailedStorageBusy) { - rc = StorageResultCode.OperationFailedInternalError; - } + mObbState.volId = mVold.createObb(mObbState.canonicalPath, binderKey, + mObbState.ownerGid); + mVold.mount(mObbState.volId, 0, -1); } catch (Exception e) { Slog.w(TAG, e); rc = StorageResultCode.OperationFailedInternalError; @@ -4233,7 +3150,6 @@ class StorageManagerService extends IStorageManager.Stub @Override public void handleExecute() throws IOException { - waitForReady(); warnOnNotMounted(); final ObbState existingState; @@ -4255,27 +3171,9 @@ class StorageManagerService extends IStorageManager.Stub int rc = StorageResultCode.OperationSucceeded; try { - if (ENABLE_BINDER) { - mVold.unmount(mObbState.volId); - mVold.destroyObb(mObbState.volId); - mObbState.volId = null; - } else { - final Command cmd = new Command("obb", "unmount", mObbState.canonicalPath); - if (mForceUnmount) { - cmd.appendArg("force"); - } - mConnector.execute(cmd); - } - } catch (NativeDaemonConnectorException e) { - int code = e.getCode(); - if (code == VoldResponseCode.OpFailedStorageBusy) { - rc = StorageResultCode.OperationFailedStorageBusy; - } else if (code == VoldResponseCode.OpFailedStorageNotFound) { - // If it's not mounted then we've already won. - rc = StorageResultCode.OperationSucceeded; - } else { - rc = StorageResultCode.OperationFailedInternalError; - } + mVold.unmount(mObbState.volId); + mVold.destroyObb(mObbState.volId); + mObbState.volId = null; } catch (Exception e) { Slog.w(TAG, e); rc = StorageResultCode.OperationFailedInternalError; @@ -4506,18 +3404,6 @@ class StorageManagerService extends IStorageManager.Stub } pw.println(); - pw.println("mConnector:"); - pw.increaseIndent(); - mConnector.dump(fd, pw, args); - pw.decreaseIndent(); - - pw.println(); - pw.println("mCryptConnector:"); - pw.increaseIndent(); - mCryptConnector.dump(fd, pw, args); - pw.decreaseIndent(); - - pw.println(); pw.print("Last maintenance: "); pw.println(TimeUtils.formatForLogging(mLastMaintenance)); } @@ -4525,11 +3411,10 @@ class StorageManagerService extends IStorageManager.Stub /** {@inheritDoc} */ @Override public void monitor() { - if (mConnector != null) { - mConnector.monitor(); - } - if (mCryptConnector != null) { - mCryptConnector.monitor(); + try { + mVold.monitor(); + } catch (Exception e) { + Slog.wtf(TAG, e); } } diff --git a/com/android/server/SystemServer.java b/com/android/server/SystemServer.java index 57271fa1..92cbd3d5 100644 --- a/com/android/server/SystemServer.java +++ b/com/android/server/SystemServer.java @@ -103,6 +103,7 @@ import com.android.server.restrictions.RestrictionsManagerService; import com.android.server.security.KeyAttestationApplicationIdProviderService; import com.android.server.security.KeyChainSystemService; import com.android.server.soundtrigger.SoundTriggerService; +import com.android.server.stats.StatsCompanionService; import com.android.server.statusbar.StatusBarManagerService; import com.android.server.storage.DeviceStorageMonitorService; import com.android.server.telecom.TelecomLoaderService; @@ -151,7 +152,7 @@ public final class SystemServer { * them from the build system somehow. */ private static final String BACKUP_MANAGER_SERVICE_CLASS = - "com.android.server.backup.BackupManagerService$Lifecycle"; + "com.android.server.backup.RefactoredBackupManagerService$Lifecycle"; private static final String APPWIDGET_SERVICE_CLASS = "com.android.server.appwidget.AppWidgetService"; private static final String VOICE_RECOGNITION_MANAGER_SERVICE_CLASS = @@ -667,9 +668,11 @@ public final class SystemServer { traceEnd(); // Tracks whether the updatable WebView is in a ready state and watches for update installs. - traceBeginAndSlog("StartWebViewUpdateService"); - mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class); - traceEnd(); + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) { + traceBeginAndSlog("StartWebViewUpdateService"); + mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class); + traceEnd(); + } } /** @@ -681,6 +684,7 @@ public final class SystemServer { VibratorService vibrator = null; IStorageManager storageManager = null; NetworkManagementService networkManagement = null; + IpSecService ipSecService = null; NetworkStatsService networkStats = null; NetworkPolicyManagerService networkPolicy = null; ConnectivityService connectivity = null; @@ -1030,6 +1034,15 @@ public final class SystemServer { reportWtf("starting NetworkManagement Service", e); } traceEnd(); + + traceBeginAndSlog("StartIpSecService"); + try { + ipSecService = IpSecService.create(context); + ServiceManager.addService(Context.IPSEC_SERVICE, ipSecService); + } catch (Throwable e) { + reportWtf("starting IpSec Service", e); + } + traceEnd(); } if (!disableNonCoreServices && !disableTextServices) { @@ -1080,6 +1093,14 @@ public final class SystemServer { traceBeginAndSlog("StartWifiRtt"); mSystemServiceManager.startService("com.android.server.wifi.RttService"); traceEnd(); + + if (context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WIFI_RTT)) { + traceBeginAndSlog("StartRttService"); + mSystemServiceManager.startService( + "com.android.server.wifi.rtt.RttService"); + traceEnd(); + } } if (context.getPackageManager().hasSystemFeature( @@ -1087,8 +1108,6 @@ public final class SystemServer { traceBeginAndSlog("StartWifiAware"); mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS); traceEnd(); - } else { - Slog.i(TAG, "No Wi-Fi Aware Service (Aware support Not Present)"); } if (context.getPackageManager().hasSystemFeature( @@ -1146,20 +1165,6 @@ public final class SystemServer { traceEnd(); } - /* - * StorageManagerService has a few dependencies: Notification Manager and - * AppWidget Provider. Make sure StorageManagerService is completely started - * first before continuing. - */ - if (storageManager != null && !mOnlyCore) { - traceBeginAndSlog("WaitForAsecScan"); - try { - storageManager.waitForAsecScan(); - } catch (RemoteException ignored) { - } - traceEnd(); - } - traceBeginAndSlog("StartNotificationManager"); mSystemServiceManager.startService(NotificationManagerService.class); SystemNotificationChannels.createAll(context); @@ -1209,18 +1214,6 @@ public final class SystemServer { traceEnd(); } - // timezone.RulesManagerService will prevent a device starting up if the chain of trust - // required for safe time zone updates might be broken. RuleManagerService cannot do - // this check when mOnlyCore == true, so we don't enable the service in this case. - final boolean startRulesManagerService = - !mOnlyCore && context.getResources().getBoolean( - R.bool.config_enableUpdateableTimeZoneRules); - if (startRulesManagerService) { - traceBeginAndSlog("StartTimeZoneRulesManagerService"); - mSystemServiceManager.startService(TIME_ZONE_RULES_MANAGER_SERVICE_CLASS); - traceEnd(); - } - traceBeginAndSlog("StartAudioService"); mSystemServiceManager.startService(AudioService.Lifecycle.class); traceEnd(); @@ -1361,6 +1354,19 @@ public final class SystemServer { } traceEnd(); + // timezone.RulesManagerService will prevent a device starting up if the chain of trust + // required for safe time zone updates might be broken. RuleManagerService cannot do + // this check when mOnlyCore == true, so we don't enable the service in this case. + // This service requires that JobSchedulerService is already started when it starts. + final boolean startRulesManagerService = + !mOnlyCore && context.getResources().getBoolean( + R.bool.config_enableUpdateableTimeZoneRules); + if (startRulesManagerService) { + traceBeginAndSlog("StartTimeZoneRulesManagerService"); + mSystemServiceManager.startService(TIME_ZONE_RULES_MANAGER_SERVICE_CLASS); + traceEnd(); + } + if (!disableNetwork && !disableNetworkTime) { traceBeginAndSlog("StartNetworkTimeUpdateService"); try { @@ -1536,6 +1542,11 @@ public final class SystemServer { traceEnd(); } + // Statsd helper + traceBeginAndSlog("StartStatsCompanionService"); + mSystemServiceManager.startService(StatsCompanionService.Lifecycle.class); + traceEnd(); + // Before things start rolling, be sure we have decided whether // we are in safe mode. final boolean safeMode = wm.detectSafeMode(); @@ -1661,6 +1672,7 @@ public final class SystemServer { final TelephonyRegistry telephonyRegistryF = telephonyRegistry; final MediaRouterService mediaRouterF = mediaRouter; final MmsServiceBroker mmsServiceF = mmsService; + final IpSecService ipSecServiceF = ipSecService; final WindowManagerService windowManagerF = wm; // We now tell the activity manager it is okay to run third party @@ -1683,10 +1695,10 @@ public final class SystemServer { traceEnd(); // No dependency on Webview preparation in system server. But this should - // be completed before allowring 3rd party + // be completed before allowing 3rd party final String WEBVIEW_PREPARATION = "WebViewFactoryPreparation"; Future<?> webviewPrep = null; - if (!mOnlyCore) { + if (!mOnlyCore && mWebViewUpdateService != null) { webviewPrep = SystemServerInitThreadPool.get().submit(() -> { Slog.i(TAG, WEBVIEW_PREPARATION); TimingsTraceLog traceLog = new TimingsTraceLog( @@ -1731,6 +1743,13 @@ public final class SystemServer { .networkScoreAndNetworkManagementServiceReady(); } traceEnd(); + traceBeginAndSlog("MakeIpSecServiceReady"); + try { + if (ipSecServiceF != null) ipSecServiceF.systemReady(); + } catch (Throwable e) { + reportWtf("making IpSec Service ready", e); + } + traceEnd(); traceBeginAndSlog("MakeNetworkStatsServiceReady"); try { if (networkStatsF != null) networkStatsF.systemReady(); diff --git a/com/android/server/accounts/AccountManagerService.java b/com/android/server/accounts/AccountManagerService.java index 8ae592f7..4e15e5d9 100644 --- a/com/android/server/accounts/AccountManagerService.java +++ b/com/android/server/accounts/AccountManagerService.java @@ -2969,9 +2969,13 @@ public class AccountManagerService * have users launching arbitrary activities by tricking users to * interact with malicious notifications. */ - checkKeyIntent( + if (!checkKeyIntent( Binder.getCallingUid(), - intent); + intent)) { + onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, + "invalid intent in bundle returned"); + return; + } doNotification( mAccounts, account, @@ -3366,9 +3370,13 @@ public class AccountManagerService Intent intent = null; if (result != null && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { - checkKeyIntent( + if (!checkKeyIntent( Binder.getCallingUid(), - intent); + intent)) { + onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, + "invalid intent in bundle returned"); + return; + } } IAccountManagerResponse response; if (mExpectActivityLaunch && result != null @@ -4716,9 +4724,7 @@ public class AccountManagerService * into launching arbitrary intents on the device via by tricking to click authenticator * supplied entries in the system Settings app. */ - protected void checkKeyIntent( - int authUid, - Intent intent) throws SecurityException { + protected boolean checkKeyIntent(int authUid, Intent intent) { intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION @@ -4727,6 +4733,9 @@ public class AccountManagerService try { PackageManager pm = mContext.getPackageManager(); ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId); + if (resolveInfo == null) { + return false; + } ActivityInfo targetActivityInfo = resolveInfo.activityInfo; int targetUid = targetActivityInfo.applicationInfo.uid; if (!isExportedSystemActivity(targetActivityInfo) @@ -4736,9 +4745,10 @@ public class AccountManagerService String activityName = targetActivityInfo.name; String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that " + "does not share a signature with the supplying authenticator (%s)."; - throw new SecurityException( - String.format(tmpl, activityName, pkgName, mAccountType)); + Log.e(TAG, String.format(tmpl, activityName, pkgName, mAccountType)); + return false; } + return true; } finally { Binder.restoreCallingIdentity(bid); } @@ -4888,9 +4898,13 @@ public class AccountManagerService } if (result != null && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { - checkKeyIntent( + if (!checkKeyIntent( Binder.getCallingUid(), - intent); + intent)) { + onError(AccountManager.ERROR_CODE_INVALID_RESPONSE, + "invalid intent in bundle returned"); + return; + } } if (result != null && !TextUtils.isEmpty(result.getString(AccountManager.KEY_AUTHTOKEN))) { @@ -5285,7 +5299,7 @@ public class AccountManagerService == PackageManager.PERMISSION_GRANTED) { // Checks runtime permission revocation. final int opCode = AppOpsManager.permissionToOpCode(perm); - if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp( + if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow( opCode, uid, packageName) == AppOpsManager.MODE_ALLOWED) { return true; } @@ -5306,7 +5320,7 @@ public class AccountManagerService Log.v(TAG, " caller uid " + callingUid + " has " + perm); } final int opCode = AppOpsManager.permissionToOpCode(perm); - if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOp( + if (opCode == AppOpsManager.OP_NONE || mAppOpsManager.noteOpNoThrow( opCode, callingUid, opPackageName) == AppOpsManager.MODE_ALLOWED) { return true; } diff --git a/com/android/server/am/ActiveServices.java b/com/android/server/am/ActiveServices.java index e0cde72f..2131731d 100644 --- a/com/android/server/am/ActiveServices.java +++ b/com/android/server/am/ActiveServices.java @@ -2162,6 +2162,15 @@ public final class ActiveServices { } } + if (r.fgRequired) { + if (DEBUG_FOREGROUND_SERVICE) { + Slog.v(TAG, "Whitelisting " + UserHandle.formatUid(r.appInfo.uid) + + " for fg-service launch"); + } + mAm.tempWhitelistUidLocked(r.appInfo.uid, + SERVICE_START_FOREGROUND_TIMEOUT, "fg-service-launch"); + } + if (!mPendingServices.contains(r)) { mPendingServices.add(r); } @@ -3141,7 +3150,7 @@ public final class ActiveServices { sr.userId, sr.crashCount, sr.shortName, app.pid); bringDownServiceLocked(sr); } else if (!allowRestart - || !mAm.mUserController.isUserRunningLocked(sr.userId, 0)) { + || !mAm.mUserController.isUserRunning(sr.userId, 0)) { bringDownServiceLocked(sr); } else { boolean canceled = scheduleServiceRestartLocked(sr, true); diff --git a/com/android/server/am/ActivityDisplay.java b/com/android/server/am/ActivityDisplay.java new file mode 100644 index 00000000..8bcbfbef --- /dev/null +++ b/com/android/server/am/ActivityDisplay.java @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2017 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.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; +import static android.app.ActivityManager.StackId.INVALID_STACK_ID; +import static android.app.ActivityManager.StackId.getStackIdForWindowingMode; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.FLAG_PRIVATE; +import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK; +import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.am.proto.ActivityDisplayProto.CONFIGURATION_CONTAINER; +import static com.android.server.am.proto.ActivityDisplayProto.STACKS; +import static com.android.server.am.proto.ActivityDisplayProto.ID; + +import android.app.ActivityManagerInternal; +import android.app.WindowConfiguration; +import android.util.IntArray; +import android.util.Slog; +import android.util.proto.ProtoOutputStream; +import android.view.Display; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.wm.ConfigurationContainer; + +import java.util.ArrayList; + +/** + * Exactly one of these classes per Display in the system. Capable of holding zero or more + * attached {@link ActivityStack}s. + */ +class ActivityDisplay extends ConfigurationContainer { + private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityDisplay" : TAG_AM; + private static final String TAG_STACK = TAG + POSTFIX_STACK; + + static final int POSITION_TOP = Integer.MAX_VALUE; + static final int POSITION_BOTTOM = Integer.MIN_VALUE; + + private ActivityStackSupervisor mSupervisor; + /** Actual Display this object tracks. */ + int mDisplayId; + Display mDisplay; + + /** All of the stacks on this display. Order matters, topmost stack is in front of all other + * stacks, bottommost behind. Accessed directly by ActivityManager package classes */ + final ArrayList<ActivityStack> mStacks = new ArrayList<>(); + + /** Array of all UIDs that are present on the display. */ + private IntArray mDisplayAccessUIDs = new IntArray(); + + /** All tokens used to put activities on this stack to sleep (including mOffToken) */ + final ArrayList<ActivityManagerInternal.SleepToken> mAllSleepTokens = new ArrayList<>(); + /** The token acquired by ActivityStackSupervisor to put stacks on the display to sleep */ + ActivityManagerInternal.SleepToken mOffToken; + + private boolean mSleeping; + + ActivityDisplay(ActivityStackSupervisor supervisor, int displayId) { + mSupervisor = supervisor; + mDisplayId = displayId; + final Display display = supervisor.mDisplayManager.getDisplay(displayId); + if (display == null) { + throw new IllegalStateException("Display does not exist displayId=" + displayId); + } + mDisplay = display; + } + + void addChild(ActivityStack stack, int position) { + if (position == POSITION_BOTTOM) { + position = 0; + } else if (position == POSITION_TOP) { + position = mStacks.size(); + } + if (DEBUG_STACK) Slog.v(TAG_STACK, "addChild: attaching " + stack + + " to displayId=" + mDisplayId + " position=" + position); + positionChildAt(stack, position); + mSupervisor.mService.updateSleepIfNeededLocked(); + } + + void removeChild(ActivityStack stack) { + if (DEBUG_STACK) Slog.v(TAG_STACK, "removeChild: detaching " + stack + + " from displayId=" + mDisplayId); + mStacks.remove(stack); + mSupervisor.mService.updateSleepIfNeededLocked(); + } + + void positionChildAtTop(ActivityStack stack) { + positionChildAt(stack, mStacks.size()); + } + + void positionChildAtBottom(ActivityStack stack) { + positionChildAt(stack, 0); + } + + private void positionChildAt(ActivityStack stack, int position) { + mStacks.remove(stack); + mStacks.add(getTopInsertPosition(stack, position), stack); + } + + private int getTopInsertPosition(ActivityStack stack, int candidatePosition) { + int position = mStacks.size(); + if (position > 0) { + final ActivityStack topStack = mStacks.get(position - 1); + if (topStack.getWindowConfiguration().isAlwaysOnTop() && topStack != stack) { + // If the top stack is always on top, we move this stack just below it. + position--; + } + } + return Math.min(position, candidatePosition); + } + + <T extends ActivityStack> T getStack(int stackId) { + for (int i = mStacks.size() - 1; i >= 0; --i) { + final ActivityStack stack = mStacks.get(i); + if (stack.mStackId == stackId) { + return (T) stack; + } + } + return null; + } + + /** + * @return the topmost stack on the display that is compatible with the input windowing mode and + * activity type. {@code null} means no compatible stack on the display. + * @see ConfigurationContainer#isCompatible(int, int) + */ + <T extends ActivityStack> T getStack(int windowingMode, int activityType) { + for (int i = mStacks.size() - 1; i >= 0; --i) { + final ActivityStack stack = mStacks.get(i); + // TODO: Should undefined windowing and activity type be compatible with standard type? + if (stack.isCompatible(windowingMode, activityType)) { + return (T) stack; + } + } + return null; + } + + /** + * @see #getStack(int, int) + * @see #createStack(int, int, boolean) + */ + <T extends ActivityStack> T getOrCreateStack(int windowingMode, int activityType, + boolean onTop) { + T stack = getStack(windowingMode, activityType); + if (stack != null) { + return stack; + } + return createStack(windowingMode, activityType, onTop); + } + + /** + * Creates a stack matching the input windowing mode and activity type on this display. + * @param windowingMode The windowing mode the stack should be created in. If + * {@link WindowConfiguration#WINDOWING_MODE_UNDEFINED} then the stack will + * be created in {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}. + * @param activityType The activityType the stack should be created in. If + * {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the stack will + * be created in {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}. + * @param onTop If true the stack will be created at the top of the display, else at the bottom. + * @return The newly created stack. + */ + <T extends ActivityStack> T createStack(int windowingMode, int activityType, boolean onTop) { + + if (activityType == ACTIVITY_TYPE_UNDEFINED) { + // Can't have an undefined stack type yet...so re-map to standard. Anyone that wants + // anything else should be passing it in anyways... + activityType = ACTIVITY_TYPE_STANDARD; + } + + if (activityType != ACTIVITY_TYPE_STANDARD) { + // For now there can be only one stack of a particular non-standard activity type on a + // display. So, get that ignoring whatever windowing mode it is currently in. + T stack = getStack(WINDOWING_MODE_UNDEFINED, activityType); + if (stack != null) { + throw new IllegalArgumentException("Stack=" + stack + " of activityType=" + + activityType + " already on display=" + this + ". Can't have multiple."); + } + } + + final ActivityManagerService service = mSupervisor.mService; + if (!mSupervisor.isWindowingModeSupported(windowingMode, service.mSupportsMultiWindow, + service.mSupportsSplitScreenMultiWindow, service.mSupportsFreeformWindowManagement, + service.mSupportsPictureInPicture, activityType)) { + throw new IllegalArgumentException("Can't create stack for unsupported windowingMode=" + + windowingMode); + } + + if (windowingMode == WINDOWING_MODE_UNDEFINED) { + // TODO: Should be okay to have stacks with with undefined windowing mode long term, but + // have to set them to something for now due to logic that depending on them. + windowingMode = WINDOWING_MODE_FULLSCREEN; + } + + final boolean inSplitScreenMode = hasSplitScreenStack(); + if (!inSplitScreenMode + && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) { + // Switch to fullscreen windowing mode if we are not in split-screen mode and we are + // trying to launch in split-screen secondary. + windowingMode = WINDOWING_MODE_FULLSCREEN; + } else if (inSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN + && WindowConfiguration.supportSplitScreenWindowingMode( + windowingMode, activityType)) { + windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; + } + + int stackId = INVALID_STACK_ID; + if (mDisplayId == DEFAULT_DISPLAY && (activityType == ACTIVITY_TYPE_STANDARD + || activityType == ACTIVITY_TYPE_UNDEFINED)) { + // TODO: Will be removed once we are no longer using static stack ids. + stackId = getStackIdForWindowingMode(windowingMode); + if (stackId == INVALID_STACK_ID) { + // Whatever...put in fullscreen stack for now. + stackId = FULLSCREEN_WORKSPACE_STACK_ID; + } + final T stack = getStack(stackId); + if (stack != null) { + return stack; + } + } + + if (stackId == INVALID_STACK_ID) { + stackId = mSupervisor.getNextStackId(); + } + + final T stack = createStackUnchecked(windowingMode, activityType, stackId, onTop); + + if (mDisplayId == DEFAULT_DISPLAY && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + // Make sure recents stack exist when creating a dock stack as it normally need to be on + // the other side of the docked stack and we make visibility decisions based on that. + // TODO: Not sure if this is needed after we change to calculate visibility based on + // stack z-order vs. id. + getOrCreateStack(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_RECENTS, onTop); + } + + return stack; + } + + @VisibleForTesting + <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType, + int stackId, boolean onTop) { + switch (windowingMode) { + case WINDOWING_MODE_PINNED: + return (T) new PinnedActivityStack(this, stackId, mSupervisor, onTop); + default: + return (T) new ActivityStack( + this, stackId, mSupervisor, windowingMode, activityType, onTop); + } + } + + /** + * Removes stacks in the input windowing modes from the system if they are of activity type + * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED + */ + void removeStacksInWindowingModes(int... windowingModes) { + if (windowingModes == null || windowingModes.length == 0) { + return; + } + + for (int j = windowingModes.length - 1 ; j >= 0; --j) { + final int windowingMode = windowingModes[j]; + for (int i = mStacks.size() - 1; i >= 0; --i) { + final ActivityStack stack = mStacks.get(i); + if (!stack.isActivityTypeStandardOrUndefined()) { + continue; + } + if (stack.getWindowingMode() != windowingMode) { + continue; + } + mSupervisor.removeStackLocked(stack.mStackId); + } + } + } + + void removeStacksWithActivityTypes(int... activityTypes) { + if (activityTypes == null || activityTypes.length == 0) { + return; + } + + for (int j = activityTypes.length - 1 ; j >= 0; --j) { + final int activityType = activityTypes[j]; + for (int i = mStacks.size() - 1; i >= 0; --i) { + final ActivityStack stack = mStacks.get(i); + if (stack.getActivityType() == activityType) { + mSupervisor.removeStackLocked(stack.mStackId); + } + } + } + } + + /** Returns the top visible stack activity type that isn't in the exclude windowing mode. */ + int getTopVisibleStackActivityType(int excludeWindowingMode) { + for (int i = mStacks.size() - 1; i >= 0; --i) { + final ActivityStack stack = mStacks.get(i); + if (stack.getWindowingMode() == excludeWindowingMode) { + continue; + } + if (stack.shouldBeVisible(null /* starting */)) { + return stack.getActivityType(); + } + } + return ACTIVITY_TYPE_UNDEFINED; + } + + ActivityStack getSplitScreenStack() { + return getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED); + } + + boolean hasSplitScreenStack() { + return getSplitScreenStack() != null; + } + + PinnedActivityStack getPinnedStack() { + return getStack(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); + } + + boolean hasPinnedStack() { + return getPinnedStack() != null; + } + + @Override + public String toString() { + return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}"; + } + + @Override + protected int getChildCount() { + return mStacks.size(); + } + + @Override + protected ConfigurationContainer getChildAt(int index) { + return mStacks.get(index); + } + + @Override + protected ConfigurationContainer getParent() { + return mSupervisor; + } + + boolean isPrivate() { + return (mDisplay.getFlags() & FLAG_PRIVATE) != 0; + } + + boolean isUidPresent(int uid) { + for (ActivityStack stack : mStacks) { + if (stack.isUidPresent(uid)) { + return true; + } + } + return false; + } + + /** Update and get all UIDs that are present on the display and have access to it. */ + IntArray getPresentUIDs() { + mDisplayAccessUIDs.clear(); + for (ActivityStack stack : mStacks) { + stack.getPresentUIDs(mDisplayAccessUIDs); + } + return mDisplayAccessUIDs; + } + + boolean shouldDestroyContentOnRemove() { + return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT; + } + + boolean shouldSleep() { + return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty()) + && (mSupervisor.mService.mRunningVoice == null); + } + + boolean isSleeping() { + return mSleeping; + } + + void setIsSleeping(boolean asleep) { + mSleeping = asleep; + } + + public void writeToProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + super.writeToProto(proto, CONFIGURATION_CONTAINER); + proto.write(ID, mDisplayId); + for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = mStacks.get(stackNdx); + stack.writeToProto(proto, STACKS); + } + proto.end(token); + } +} diff --git a/com/android/server/am/ActivityManagerService.java b/com/android/server/am/ActivityManagerService.java index 02eb3b43..e6fe6204 100644 --- a/com/android/server/am/ActivityManagerService.java +++ b/com/android/server/am/ActivityManagerService.java @@ -33,6 +33,14 @@ import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID; import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; +import static android.app.ActivityManager.StackId.getWindowingModeForStackId; +import static android.app.ActivityManager.StackId.isStaticStack; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS; import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT; import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY; @@ -145,7 +153,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IMMERSIVE; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKSCREEN; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LRU; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU; @@ -163,10 +170,8 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_URI_PERMISSION; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBLE_BEHIND; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED; import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME; import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY; import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS; @@ -177,8 +182,8 @@ import static com.android.server.am.TaskRecord.INVALID_TASK_ID; import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK; import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE; +import static com.android.server.am.proto.ActivityManagerServiceProto.ACTIVITIES; import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN; -import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_RELAUNCH; import static com.android.server.wm.AppTransition.TRANSIT_NONE; import static com.android.server.wm.AppTransition.TRANSIT_TASK_IN_PLACE; import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN; @@ -259,8 +264,8 @@ import android.content.pm.IPackageManager; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ParceledListSlice; import android.content.pm.PathPermission; import android.content.pm.PermissionInfo; @@ -349,6 +354,7 @@ import android.util.SparseArray; import android.util.SparseIntArray; import android.util.TimeUtils; import android.util.Xml; +import android.util.proto.ProtoOutputStream; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -402,6 +408,7 @@ import com.android.server.firewall.IntentFirewall; import com.android.server.job.JobSchedulerInternal; import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; +import com.android.server.utils.PriorityDump; import com.android.server.vr.VrManagerInternal; import com.android.server.wm.PinnedStackWindowController; import com.android.server.wm.WindowManagerService; @@ -682,11 +689,6 @@ public class ActivityManagerService extends IActivityManager.Stub ActivityInfo mLastAddedTaskActivity; /** - * List of packages whitelisted by DevicePolicyManager for locktask. Indexed by userId. - */ - SparseArray<String[]> mLockTaskPackages = new SparseArray<>(); - - /** * The package name of the DeviceOwner. This package is not permitted to have its data cleared. */ String mDeviceOwnerName; @@ -712,9 +714,45 @@ public class ActivityManagerService extends IActivityManager.Stub @VisibleForTesting long mWaitForNetworkTimeoutMs; + /** + * Helper class which parses out priority arguments and dumps sections according to their + * priority. If priority arguments are omitted, function calls the legacy dump command. + */ + private final PriorityDump.PriorityDumper mPriorityDumper = new PriorityDump.PriorityDumper() { + @Override + public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args) { + doDump(fd, pw, new String[] {"activities"}); + } + + @Override + public void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args) { + doDump(fd, pw, new String[] {"settings"}); + doDump(fd, pw, new String[] {"intents"}); + doDump(fd, pw, new String[] {"broadcasts"}); + doDump(fd, pw, new String[] {"providers"}); + doDump(fd, pw, new String[] {"permissions"}); + doDump(fd, pw, new String[] {"services"}); + doDump(fd, pw, new String[] {"recents"}); + doDump(fd, pw, new String[] {"lastanr"}); + doDump(fd, pw, new String[] {"starter"}); + if (mAssociations.size() > 0) { + doDump(fd, pw, new String[] {"associations"}); + } + doDump(fd, pw, new String[] {"processes"}); + doDump(fd, pw, new String[] {"-v", "all"}); + doDump(fd, pw, new String[] {"service", "all"}); + doDump(fd, pw, new String[] {"provider", "all"}); + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + doDump(fd, pw, args); + } + }; + public boolean canShowErrorDialogs() { return mShowDialogs && !mSleeping && !mShuttingDown - && !mKeyguardController.isKeyguardShowing() + && !mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY) && !(UserManager.isDeviceInDemoMode(mContext) && mUserController.getCurrentUser().isDemo()); } @@ -1653,45 +1691,34 @@ public class ActivityManagerService extends IActivityManager.Stub static final int DISPATCH_PROCESSES_CHANGED_UI_MSG = 31; static final int DISPATCH_PROCESS_DIED_UI_MSG = 32; static final int REPORT_MEM_USAGE_MSG = 33; - static final int REPORT_USER_SWITCH_MSG = 34; - static final int CONTINUE_USER_SWITCH_MSG = 35; - static final int USER_SWITCH_TIMEOUT_MSG = 36; static final int IMMERSIVE_MODE_LOCK_MSG = 37; static final int PERSIST_URI_GRANTS_MSG = 38; static final int REQUEST_ALL_PSS_MSG = 39; - static final int START_PROFILES_MSG = 40; static final int UPDATE_TIME_PREFERENCE_MSG = 41; - static final int SYSTEM_USER_START_MSG = 42; - static final int SYSTEM_USER_CURRENT_MSG = 43; static final int ENTER_ANIMATION_COMPLETE_MSG = 44; static final int FINISH_BOOTING_MSG = 45; - static final int START_USER_SWITCH_UI_MSG = 46; static final int SEND_LOCALE_TO_MOUNT_DAEMON_MSG = 47; static final int DISMISS_DIALOG_UI_MSG = 48; static final int NOTIFY_CLEARTEXT_NETWORK_MSG = 49; static final int POST_DUMP_HEAP_NOTIFICATION_MSG = 50; static final int DELETE_DUMPHEAP_MSG = 51; - static final int FOREGROUND_PROFILE_CHANGED_MSG = 52; static final int DISPATCH_UIDS_CHANGED_UI_MSG = 53; static final int REPORT_TIME_TRACKER_MSG = 54; - static final int REPORT_USER_SWITCH_COMPLETE_MSG = 55; static final int SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG = 56; static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG = 57; static final int IDLE_UIDS_MSG = 58; - static final int SYSTEM_USER_UNLOCK_MSG = 59; static final int LOG_STACK_STATE = 60; static final int VR_MODE_CHANGE_MSG = 61; static final int SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG_MSG = 62; static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 63; - static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 64; static final int NOTIFY_VR_SLEEPING_MSG = 65; static final int SERVICE_FOREGROUND_TIMEOUT_MSG = 66; static final int DISPATCH_PENDING_INTENT_CANCEL_MSG = 67; static final int PUSH_TEMP_WHITELIST_UI_MSG = 68; static final int SERVICE_FOREGROUND_CRASH_MSG = 69; static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70; - static final int USER_SWITCH_CALLBACKS_TIMEOUT_MSG = 71; - static final int START_USER_SWITCH_FG_MSG = 712; + static final int TOP_APP_KILLED_BY_LMK_MSG = 73; + static final int NOTIFY_VR_KEYGUARD_MSG = 74; static final int FIRST_ACTIVITY_STACK_MSG = 100; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -1904,10 +1931,6 @@ public class ActivityManagerService extends IActivityManager.Stub } break; } - case START_USER_SWITCH_UI_MSG: { - mUserController.showUserSwitchDialog((Pair<UserInfo, UserInfo>) msg.obj); - break; - } case DISMISS_DIALOG_UI_MSG: { final Dialog d = (Dialog) msg.obj; d.dismiss(); @@ -1923,6 +1946,17 @@ public class ActivityManagerService extends IActivityManager.Stub dispatchProcessDied(pid, uid); break; } + case TOP_APP_KILLED_BY_LMK_MSG: { + final String appName = (String) msg.obj; + final AlertDialog d = new BaseErrorDialog(mUiContext); + d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); + d.setTitle(mUiContext.getText(R.string.top_app_killed_title)); + d.setMessage(mUiContext.getString(R.string.top_app_killed_message, appName)); + d.setButton(DialogInterface.BUTTON_POSITIVE, mUiContext.getText(R.string.close), + obtainMessage(DISMISS_DIALOG_UI_MSG, d)); + d.show(); + break; + } case DISPATCH_UIDS_CHANGED_UI_MSG: { dispatchUidsChanged(); } break; @@ -2136,26 +2170,6 @@ public class ActivityManagerService extends IActivityManager.Stub thread.start(); break; } - case START_USER_SWITCH_FG_MSG: { - mUserController.startUserInForeground(msg.arg1); - break; - } - case REPORT_USER_SWITCH_MSG: { - mUserController.dispatchUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2); - break; - } - case CONTINUE_USER_SWITCH_MSG: { - mUserController.continueUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2); - break; - } - case USER_SWITCH_TIMEOUT_MSG: { - mUserController.timeoutUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2); - break; - } - case USER_SWITCH_CALLBACKS_TIMEOUT_MSG: { - mUserController.timeoutUserSwitchCallbacks(msg.arg1, msg.arg2); - break; - } case IMMERSIVE_MODE_LOCK_MSG: { final boolean nextState = (msg.arg1 != 0); if (mUpdateLock.isHeld() != nextState) { @@ -2180,12 +2194,6 @@ public class ActivityManagerService extends IActivityManager.Stub } break; } - case START_PROFILES_MSG: { - synchronized (ActivityManagerService.this) { - mUserController.startProfilesLocked(); - } - break; - } case UPDATE_TIME_PREFERENCE_MSG: { // The user's time format preference might have changed. // For convenience we re-use the Intent extra values. @@ -2204,35 +2212,6 @@ public class ActivityManagerService extends IActivityManager.Stub } break; } - case SYSTEM_USER_START_MSG: { - mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_RUNNING_START, - Integer.toString(msg.arg1), msg.arg1); - mSystemServiceManager.startUser(msg.arg1); - break; - } - case SYSTEM_USER_UNLOCK_MSG: { - final int userId = msg.arg1; - mSystemServiceManager.unlockUser(userId); - synchronized (ActivityManagerService.this) { - mRecentTasks.loadUserRecentsLocked(userId); - } - if (userId == UserHandle.USER_SYSTEM) { - startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_UNAWARE); - } - installEncryptionUnawareProviders(userId); - mUserController.finishUserUnlocked((UserState) msg.obj); - break; - } - case SYSTEM_USER_CURRENT_MSG: { - mBatteryStatsService.noteEvent( - BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_FINISH, - Integer.toString(msg.arg2), msg.arg2); - mBatteryStatsService.noteEvent( - BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START, - Integer.toString(msg.arg1), msg.arg1); - mSystemServiceManager.switchUser(msg.arg1); - break; - } case ENTER_ANIMATION_COMPLETE_MSG: { synchronized (ActivityManagerService.this) { ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj); @@ -2372,19 +2351,10 @@ public class ActivityManagerService extends IActivityManager.Stub mMemWatchDumpUid = -1; } } break; - case FOREGROUND_PROFILE_CHANGED_MSG: { - mUserController.dispatchForegroundProfileChanged(msg.arg1); - } break; case REPORT_TIME_TRACKER_MSG: { AppTimeTracker tracker = (AppTimeTracker)msg.obj; tracker.deliverResult(mContext); } break; - case REPORT_USER_SWITCH_COMPLETE_MSG: { - mUserController.dispatchUserSwitchComplete(msg.arg1); - } break; - case REPORT_LOCKED_BOOT_COMPLETE_MSG: { - mUserController.dispatchLockedBootComplete(msg.arg1); - } break; case SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG: { IUiAutomationConnection connection = (IUiAutomationConnection) msg.obj; try { @@ -2409,17 +2379,16 @@ public class ActivityManagerService extends IActivityManager.Stub if (disableNonVrUi) { // If we are in a VR mode where Picture-in-Picture mode is unsupported, // then remove the pinned stack. - final PinnedActivityStack pinnedStack = mStackSupervisor.getStack( - PINNED_STACK_ID); - if (pinnedStack != null) { - mStackSupervisor.removeStackLocked(PINNED_STACK_ID); - } + mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED); } } } break; case NOTIFY_VR_SLEEPING_MSG: { notifyVrManagerOfSleepState(msg.arg1 != 0); } break; + case NOTIFY_VR_KEYGUARD_MSG: { + notifyVrManagerOfKeyguardState(msg.arg1 != 0); + } break; case HANDLE_TRUST_STORAGE_UPDATE_MSG: { synchronized (ActivityManagerService.this) { for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { @@ -2568,10 +2537,12 @@ public class ActivityManagerService extends IActivityManager.Stub } public void setWindowManager(WindowManagerService wm) { - mWindowManager = wm; - mStackSupervisor.setWindowManager(wm); - mActivityStarter.setWindowManager(wm); - mLockTaskController.setWindowManager(wm); + synchronized (this) { + mWindowManager = wm; + mStackSupervisor.setWindowManager(wm); + mActivityStarter.setWindowManager(wm); + mLockTaskController.setWindowManager(wm); + } } public void setUsageStatsManager(UsageStatsManagerInternal usageStatsManager) { @@ -2589,6 +2560,14 @@ public class ActivityManagerService extends IActivityManager.Stub static class MemBinder extends Binder { ActivityManagerService mActivityManagerService; + private final PriorityDump.PriorityDumper mPriorityDumper = + new PriorityDump.PriorityDumper() { + @Override + public void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args) { + mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, false, null); + } + }; + MemBinder(ActivityManagerService activityManagerService) { mActivityManagerService = activityManagerService; } @@ -2597,7 +2576,7 @@ public class ActivityManagerService extends IActivityManager.Stub protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, "meminfo", pw)) return; - mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, false, null); + PriorityDump.dump(mPriorityDumper, fd, pw, args); } } @@ -2631,19 +2610,27 @@ public class ActivityManagerService extends IActivityManager.Stub static class CpuBinder extends Binder { ActivityManagerService mActivityManagerService; + private final PriorityDump.PriorityDumper mPriorityDumper = + new PriorityDump.PriorityDumper() { + @Override + public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, + "cpuinfo", pw)) return; + synchronized (mActivityManagerService.mProcessCpuTracker) { + pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentLoad()); + pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentState( + SystemClock.uptimeMillis())); + } + } + }; + CpuBinder(ActivityManagerService activityManagerService) { mActivityManagerService = activityManagerService; } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext, - "cpuinfo", pw)) return; - synchronized (mActivityManagerService.mProcessCpuTracker) { - pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentLoad()); - pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentState( - SystemClock.uptimeMillis())); - } + PriorityDump.dump(mPriorityDumper, fd, pw, args); } } @@ -3152,9 +3139,7 @@ public class ActivityManagerService extends IActivityManager.Stub } if (mLastResumedActivity != null && r.userId != mLastResumedActivity.userId) { - mHandler.removeMessages(FOREGROUND_PROFILE_CHANGED_MSG); - mHandler.obtainMessage( - FOREGROUND_PROFILE_CHANGED_MSG, r.userId, 0).sendToTarget(); + mUserController.sendForegroundProfileChanged(r.userId); } mLastResumedActivity = r; @@ -3259,7 +3244,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (r.requestedVrComponent != null && r.getStackId() >= FIRST_DYNAMIC_STACK_ID) { Slog.i(TAG, "Moving " + r.shortComponentName + " from stack " + r.getStackId() + " to main stack for VR"); - moveTaskToStack(r.getTask().taskId, FULLSCREEN_WORKSPACE_STACK_ID, true /* toTop */); + setTaskWindowingMode(r.getTask().taskId, + WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, true /* toTop */); } mHandler.sendMessage( mHandler.obtainMessage(VR_MODE_CHANGE_MSG, 0, 0, r)); @@ -3278,6 +3264,19 @@ public class ActivityManagerService extends IActivityManager.Stub vrService.onSleepStateChanged(isSleeping); } + private void sendNotifyVrManagerOfKeyguardState(boolean isShowing) { + mHandler.sendMessage( + mHandler.obtainMessage(NOTIFY_VR_KEYGUARD_MSG, isShowing ? 1 : 0, 0)); + } + + private void notifyVrManagerOfKeyguardState(boolean isShowing) { + final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class); + if (vrService == null) { + return; + } + vrService.onKeyguardStateChanged(isShowing); + } + final void showAskCompatModeDialogLocked(ActivityRecord r) { Message msg = Message.obtain(); msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG; @@ -3874,6 +3873,12 @@ public class ActivityManagerService extends IActivityManager.Stub mNativeDebuggingApp = null; } + if (app.info.isPrivilegedApp() && + !SystemProperties.getBoolean("pm.dexopt.priv-apps", true)) { + runtimeFlags |= Zygote.DISABLE_VERIFIER; + runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES; + } + String invokeWith = null; if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { // Debuggable apps may include a wrapper script with their library directory. @@ -4156,15 +4161,6 @@ public class ActivityManagerService extends IActivityManager.Stub } } - void enforceShellRestriction(String restriction, int userHandle) { - if (Binder.getCallingUid() == SHELL_UID) { - if (userHandle < 0 || mUserController.hasUserRestriction(restriction, userHandle)) { - throw new SecurityException("Shell does not have permission to access user " - + userHandle); - } - } - } - @Override public int getFrontActivityScreenCompatMode() { enforceNotIsolatedCaller("getFrontActivityScreenCompatMode"); @@ -5432,6 +5428,7 @@ public class ActivityManagerService extends IActivityManager.Stub boolean doLowMem = app.instr == null; boolean doOomAdj = doLowMem; if (!app.killedByAm) { + maybeNotifyTopAppKilled(app); Slog.i(TAG, "Process " + app.processName + " (pid " + pid + ") has died: " + ProcessList.makeOomAdjString(app.setAdj) + ProcessList.makeProcStateString(app.setProcState)); @@ -5465,6 +5462,23 @@ public class ActivityManagerService extends IActivityManager.Stub } } + /** Show system error dialog when a top app is killed by LMK */ + void maybeNotifyTopAppKilled(ProcessRecord app) { + if (!shouldNotifyTopAppKilled(app)) { + return; + } + + Message msg = mHandler.obtainMessage(TOP_APP_KILLED_BY_LMK_MSG); + msg.obj = mContext.getPackageManager().getApplicationLabel(app.info); + mUiHandler.sendMessage(msg); + } + + /** Only show notification when the top app is killed on low ram devices */ + private boolean shouldNotifyTopAppKilled(ProcessRecord app) { + return app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP && + ActivityManager.isLowRamDeviceStatic(); + } + /** * If a stack trace dump file is configured, dump process stack traces. * @param clearTraces causes the dump file to be erased prior to the new @@ -6188,7 +6202,7 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.w(TAG, "Failed trying to unstop package " + packageName + ": " + e); } - if (mUserController.isUserRunningLocked(user, 0)) { + if (mUserController.isUserRunning(user, 0)) { forceStopPackageLocked(packageName, pkgUid, "from pid " + callingPid); finishForceStopPackageLocked(packageName, pkgUid); } @@ -7346,33 +7360,32 @@ public class ActivityManagerService extends IActivityManager.Stub startProcessLocked(procs.get(ip), "on-hold", null); } } + if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) { + return; + } + // Start looking for apps that are abusing wake locks. + Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG); + mHandler.sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL); + // Tell anyone interested that we are done booting! + SystemProperties.set("sys.boot_completed", "1"); - if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) { - // Start looking for apps that are abusing wake locks. - Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG); - mHandler.sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL); - // Tell anyone interested that we are done booting! - SystemProperties.set("sys.boot_completed", "1"); - - // And trigger dev.bootcomplete if we are not showing encryption progress - if (!"trigger_restart_min_framework".equals(SystemProperties.get("vold.decrypt")) + // And trigger dev.bootcomplete if we are not showing encryption progress + if (!"trigger_restart_min_framework".equals(SystemProperties.get("vold.decrypt")) || "".equals(SystemProperties.get("vold.encrypt_progress"))) { - SystemProperties.set("dev.bootcomplete", "1"); - } - mUserController.sendBootCompletedLocked( - new IIntentReceiver.Stub() { - @Override - public void performReceive(Intent intent, int resultCode, - String data, Bundle extras, boolean ordered, - boolean sticky, int sendingUser) { - synchronized (ActivityManagerService.this) { - requestPssAllProcsLocked(SystemClock.uptimeMillis(), - true, false); - } - } - }); - scheduleStartProfilesLocked(); + SystemProperties.set("dev.bootcomplete", "1"); } + mUserController.sendBootCompleted( + new IIntentReceiver.Stub() { + @Override + public void performReceive(Intent intent, int resultCode, + String data, Bundle extras, boolean ordered, + boolean sticky, int sendingUser) { + synchronized (ActivityManagerService.this) { + requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false); + } + } + }); + mUserController.scheduleStartProfiles(); } } @@ -8112,7 +8125,7 @@ public class ActivityManagerService extends IActivityManager.Stub final Rect sourceBounds = new Rect(r.pictureInPictureArgs.getSourceRectHint()); mStackSupervisor.moveActivityToPinnedStackLocked(r, sourceBounds, aspectRatio, true /* moveHomeStackToFront */, "enterPictureInPictureMode"); - final PinnedActivityStack stack = mStackSupervisor.getStack(PINNED_STACK_ID); + final PinnedActivityStack stack = r.getStack(); stack.setPictureInPictureAspectRatio(aspectRatio); stack.setPictureInPictureActions(actions); @@ -8227,11 +8240,6 @@ public class ActivityManagerService extends IActivityManager.Stub + ": Current activity does not support picture-in-picture."); } - if (!StackId.isAllowedToEnterPictureInPicture(r.getStack().getStackId())) { - throw new IllegalStateException(caller - + ": Activities on the home, assistant, or recents stack not supported"); - } - if (params.hasSetAspectRatio() && !mWindowManager.isValidPictureInPictureAspectRatio(r.getStack().mDisplayId, params.getAspectRatio())) { @@ -9869,8 +9877,9 @@ public class ActivityManagerService extends IActivityManager.Stub if (tr.mBounds != null) { rti.bounds = new Rect(tr.mBounds); } - rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreen(); + rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreenWindowingMode(); rti.resizeMode = tr.mResizeMode; + rti.configuration.setTo(tr.getConfiguration()); ActivityRecord base = null; ActivityRecord top = null; @@ -10048,7 +10057,7 @@ public class ActivityManagerService extends IActivityManager.Stub enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS, "getTaskDescription()"); final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id, - MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, INVALID_STACK_ID); + MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (tr != null) { return tr.lastTaskDescription; } @@ -10161,7 +10170,7 @@ public class ActivityManagerService extends IActivityManager.Stub public void setTaskResizeable(int taskId, int resizeableMode) { synchronized (this) { final TaskRecord task = mStackSupervisor.anyTaskForIdLocked( - taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, INVALID_STACK_ID); + taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (task == null) { Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found"); return; @@ -10188,21 +10197,23 @@ public class ActivityManagerService extends IActivityManager.Stub // - a non-null bounds on a non-freeform (fullscreen OR docked) task moves // that task to freeform // - otherwise the task is not moved - int stackId = task.getStackId(); + ActivityStack stack = task.getStack(); if (!task.getWindowConfiguration().canResizeTask()) { throw new IllegalArgumentException("resizeTask not allowed on task=" + task); } - if (bounds == null && stackId == FREEFORM_WORKSPACE_STACK_ID) { - stackId = FULLSCREEN_WORKSPACE_STACK_ID; - } else if (bounds != null && stackId != FREEFORM_WORKSPACE_STACK_ID ) { - stackId = FREEFORM_WORKSPACE_STACK_ID; + if (bounds == null && stack.getWindowingMode() == WINDOWING_MODE_FREEFORM) { + stack = stack.getDisplay().getOrCreateStack( + WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), ON_TOP); + } else if (bounds != null && stack.getWindowingMode() != WINDOWING_MODE_FREEFORM) { + stack = stack.getDisplay().getOrCreateStack( + WINDOWING_MODE_FREEFORM, stack.getActivityType(), ON_TOP); } // Reparent the task to the right stack if necessary boolean preserveWindow = (resizeMode & RESIZE_MODE_PRESERVE_WINDOW) != 0; - if (stackId != task.getStackId()) { + if (stack != task.getStack()) { // Defer resume until the task is resized below - task.reparent(stackId, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, + task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, DEFER_RESUME, "resizeTask"); preserveWindow = false; } @@ -10224,7 +10235,7 @@ public class ActivityManagerService extends IActivityManager.Stub try { synchronized (this) { final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, - MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, INVALID_STACK_ID); + MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (task == null) { Slog.w(TAG, "getTaskBounds: taskId=" + taskId + " not found"); return rect; @@ -10256,7 +10267,7 @@ public class ActivityManagerService extends IActivityManager.Stub try { synchronized (this) { final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, - MATCH_TASK_IN_STACKS_ONLY, INVALID_STACK_ID); + MATCH_TASK_IN_STACKS_ONLY); if (task == null) { Slog.w(TAG, "cancelTaskWindowTransition: taskId=" + taskId + " not found"); return; @@ -10275,7 +10286,7 @@ public class ActivityManagerService extends IActivityManager.Stub try { synchronized (this) { final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, - MATCH_TASK_IN_STACKS_ONLY, INVALID_STACK_ID); + MATCH_TASK_IN_STACKS_ONLY); if (task == null) { Slog.w(TAG, "cancelTaskThumbnailTransition: taskId=" + taskId + " not found"); return; @@ -10295,7 +10306,7 @@ public class ActivityManagerService extends IActivityManager.Stub final TaskRecord task; synchronized (this) { task = mStackSupervisor.anyTaskForIdLocked(taskId, - MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, INVALID_STACK_ID); + MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (task == null) { Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found"); return null; @@ -10375,14 +10386,45 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void removeStack(int stackId) { enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS, "removeStack()"); - if (StackId.isHomeOrRecentsStack(stackId)) { - throw new IllegalArgumentException("Removing home or recents stack is not allowed."); + synchronized (this) { + final long ident = Binder.clearCallingIdentity(); + try { + final ActivityStack stack = mStackSupervisor.getStack(stackId); + if (stack != null && !stack.isActivityTypeStandardOrUndefined()) { + throw new IllegalArgumentException( + "Removing non-standard stack is not allowed."); + } + mStackSupervisor.removeStackLocked(stackId); + } finally { + Binder.restoreCallingIdentity(ident); + } } + } + /** + * Removes stacks in the input windowing modes from the system if they are of activity type + * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED + */ + @Override + public void removeStacksInWindowingModes(int[] windowingModes) { + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "removeStacksInWindowingModes()"); synchronized (this) { final long ident = Binder.clearCallingIdentity(); try { - mStackSupervisor.removeStackLocked(stackId); + mStackSupervisor.removeStacksInWindowingModes(windowingModes); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + @Override + public void removeStacksWithActivityTypes(int[] activityTypes) { + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "removeStacksWithActivityTypes()"); + synchronized (this) { + final long ident = Binder.clearCallingIdentity(); + try { + mStackSupervisor.removeStacksWithActivityTypes(activityTypes); } finally { Binder.restoreCallingIdentity(ident); } @@ -10532,13 +10574,15 @@ public class ActivityManagerService extends IActivityManager.Stub public int createStackOnDisplay(int displayId) throws RemoteException { enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "createStackOnDisplay()"); synchronized (this) { - final int stackId = mStackSupervisor.getNextStackId(); - final ActivityStack stack = - mStackSupervisor.createStackOnDisplay(stackId, displayId, true /*onTop*/); - if (stack == null) { + final ActivityDisplay display = + mStackSupervisor.getActivityDisplayOrCreateLocked(displayId); + if (display == null) { return INVALID_STACK_ID; } - return stack.mStackId; + // TODO(multi-display): Have the caller pass in the windowing mode and activity type. + final ActivityStack stack = display.createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /*onTop*/); + return (stack != null) ? stack.mStackId : INVALID_STACK_ID; } } @@ -10570,8 +10614,12 @@ public class ActivityManagerService extends IActivityManager.Stub "exitFreeformMode: You can only go fullscreen from freeform."); } + final ActivityStack fullscreenStack = stack.getDisplay().getOrCreateStack( + WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), ON_TOP); + if (DEBUG_STACK) Slog.d(TAG_STACK, "exitFreeformMode: " + r); - r.getTask().reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, + // TODO: Should just change windowing mode vs. re-parenting... + r.getTask().reparent(fullscreenStack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, "exitFreeformMode"); } finally { Binder.restoreCallingIdentity(ident); @@ -10580,29 +10628,35 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void moveTaskToStack(int taskId, int stackId, boolean toTop) { - enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()"); - if (StackId.isHomeOrRecentsStack(stackId)) { - throw new IllegalArgumentException( - "moveTaskToStack: Attempt to move task " + taskId + " to stack " + stackId); - } + public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) { + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()"); synchronized (this) { - long ident = Binder.clearCallingIdentity(); + final long ident = Binder.clearCallingIdentity(); try { final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); if (task == null) { - Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId); + Slog.w(TAG, "setTaskWindowingMode: No task for id=" + taskId); return; } - if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId - + " to stackId=" + stackId + " toTop=" + toTop); - if (stackId == DOCKED_STACK_ID) { + if (DEBUG_STACK) Slog.d(TAG_STACK, "setTaskWindowingMode: moving task=" + taskId + + " to windowingMode=" + windowingMode + " toTop=" + toTop); + if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { mWindowManager.setDockedStackCreateState(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */); } - task.reparent(stackId, toTop, - REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, "moveTaskToStack"); + + if (!task.isActivityTypeStandardOrUndefined()) { + throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move task " + + taskId + " to non-standard windowin mode=" + windowingMode); + } + final ActivityDisplay display = task.getStack().getDisplay(); + final ActivityStack stack = display.getOrCreateStack(windowingMode, + task.getStack().getActivityType(), toTop); + // TODO: We should just change the windowing mode for the task vs. creating and + // moving it to a stack. + task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, + "moveTaskToStack"); } finally { Binder.restoreCallingIdentity(ident); } @@ -10610,49 +10664,42 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void swapDockedAndFullscreenStack() throws RemoteException { - enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "swapDockedAndFullscreenStack()"); + public void moveTaskToStack(int taskId, int stackId, boolean toTop) { + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()"); synchronized (this) { long ident = Binder.clearCallingIdentity(); try { - final ActivityStack fullscreenStack = mStackSupervisor.getStack( - FULLSCREEN_WORKSPACE_STACK_ID); - final TaskRecord topTask = fullscreenStack != null ? fullscreenStack.topTask() - : null; - final ActivityStack dockedStack = mStackSupervisor.getStack(DOCKED_STACK_ID); - final ArrayList<TaskRecord> tasks = dockedStack != null ? dockedStack.getAllTasks() - : null; - if (topTask == null || tasks == null || tasks.size() == 0) { - Slog.w(TAG, - "Unable to swap tasks, either docked or fullscreen stack is empty."); + final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); + if (task == null) { + Slog.w(TAG, "moveTaskToStack: No task for id=" + taskId); return; } - // TODO: App transition - mWindowManager.prepareAppTransition(TRANSIT_ACTIVITY_RELAUNCH, false); - - // Defer the resume until we move all the docked tasks to the fullscreen stack below - topTask.reparent(DOCKED_STACK_ID, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, - DEFER_RESUME, "swapDockedAndFullscreenStack - DOCKED_STACK"); - final int size = tasks.size(); - for (int i = 0; i < size; i++) { - final int id = tasks.get(i).taskId; - if (id == topTask.taskId) { - continue; - } - - // Defer the resume until after all the tasks have been moved - tasks.get(i).reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, - REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, DEFER_RESUME, - "swapDockedAndFullscreenStack - FULLSCREEN_STACK"); + if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId + + " to stackId=" + stackId + " toTop=" + toTop); + if (stackId == DOCKED_STACK_ID) { + mWindowManager.setDockedStackCreateState(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, + null /* initialBounds */); } - // Because we deferred the resume to avoid conflicts with stack switches while - // resuming, we need to do it after all the tasks are moved. - mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); - mStackSupervisor.resumeFocusedStackTopActivityLocked(); - - mWindowManager.executeAppTransition(); + ActivityStack stack = mStackSupervisor.getStack(stackId); + if (stack == null) { + if (!isStaticStack(stackId)) { + throw new IllegalStateException( + "moveTaskToStack: No stack for stackId=" + stackId); + } + final ActivityDisplay display = task.getStack().getDisplay(); + final int windowingMode = + getWindowingModeForStackId(stackId, display.hasSplitScreenStack()); + stack = display.getOrCreateStack(windowingMode, + task.getStack().getActivityType(), toTop); + } + if (!stack.isActivityTypeStandardOrUndefined()) { + throw new IllegalArgumentException("moveTaskToStack: Attempt to move task " + + taskId + " to stack " + stackId); + } + task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME, + "moveTaskToStack"); } finally { Binder.restoreCallingIdentity(ident); } @@ -10685,13 +10732,18 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.w(TAG, "moveTaskToDockedStack: No task for id=" + taskId); return false; } - if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToDockedStack: moving task=" + taskId + " to createMode=" + createMode + " toTop=" + toTop); mWindowManager.setDockedStackCreateState(createMode, initialBounds); + final ActivityDisplay display = task.getStack().getDisplay(); + final ActivityStack stack = display.getOrCreateStack( + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, task.getStack().getActivityType(), + toTop); + // Defer resuming until we move the home stack to the front below - final boolean moved = task.reparent(DOCKED_STACK_ID, toTop, + // TODO: Should just change windowing mode vs. re-parenting... + final boolean moved = task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, animate, !DEFER_RESUME, "moveTaskToDockedStack"); if (moved) { @@ -10705,6 +10757,66 @@ public class ActivityManagerService extends IActivityManager.Stub } /** + * Dismisses split-screen multi-window mode. + * @param toTop If true the current primary split-screen stack will be placed or left on top. + */ + @Override + public void dismissSplitScreenMode(boolean toTop) { + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()"); + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (this) { + final ActivityStack stack = + mStackSupervisor.getDefaultDisplay().getSplitScreenStack(); + if (toTop) { + mStackSupervisor.resizeStackLocked(stack.mStackId, null /* destBounds */, + null /* tempTaskBounds */, null /* tempTaskInsetBounds */, + true /* preserveWindows */, true /* allowResizeInDockedMode */, + !DEFER_RESUME); + } else { + mStackSupervisor.moveTasksToFullscreenStackLocked(stack, false /* onTop */); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + /** + * Dismisses Pip + * @param animate True if the dismissal should be animated. + * @param animationDuration The duration of the resize animation in milliseconds or -1 if the + * default animation duration should be used. + */ + @Override + public void dismissPip(boolean animate, int animationDuration) { + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()"); + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (this) { + final PinnedActivityStack stack = + mStackSupervisor.getDefaultDisplay().getPinnedStack(); + + if (stack == null) { + return; + } + if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) { + throw new IllegalArgumentException("Stack: " + stack + + " doesn't support animated resize."); + } + if (animate) { + stack.animateResizePinnedStack(null /* sourceHintBounds */, + null /* destBounds */, animationDuration, false /* fromFullscreen */); + } else { + mStackSupervisor.moveTasksToFullscreenStackLocked(stack, true /* onTop */); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + /** * Moves the top activity in the input stackId to the pinned stack. * * @param stackId Id of stack to move the top activity to pinned stack. @@ -10739,17 +10851,16 @@ public class ActivityManagerService extends IActivityManager.Stub try { synchronized (this) { if (animate) { - if (stackId == PINNED_STACK_ID) { - final PinnedActivityStack pinnedStack = - mStackSupervisor.getStack(PINNED_STACK_ID); - if (pinnedStack != null) { - pinnedStack.animateResizePinnedStack(null /* sourceHintBounds */, - destBounds, animationDuration, false /* fromFullscreen */); - } - } else { + final PinnedActivityStack stack = mStackSupervisor.getStack(stackId); + if (stack == null) { + return; + } + if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) { throw new IllegalArgumentException("Stack: " + stackId + " doesn't support animated resize."); } + stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds, + animationDuration, false /* fromFullscreen */); } else { mStackSupervisor.resizeStackLocked(stackId, destBounds, null /* tempTaskBounds */, null /* tempTaskInsetBounds */, preserveWindows, @@ -10801,11 +10912,6 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void positionTaskInStack(int taskId, int stackId, int position) { enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "positionTaskInStack()"); - if (StackId.isHomeOrRecentsStack(stackId)) { - throw new IllegalArgumentException( - "positionTaskInStack: Attempt to change the position of task " - + taskId + " in/to home/recents stack"); - } synchronized (this) { long ident = Binder.clearCallingIdentity(); try { @@ -10817,8 +10923,16 @@ public class ActivityManagerService extends IActivityManager.Stub + taskId); } - final ActivityStack stack = mStackSupervisor.getStack(stackId, CREATE_IF_NEEDED, - !ON_TOP); + final ActivityStack stack = mStackSupervisor.getStack(stackId); + + if (stack == null) { + throw new IllegalArgumentException("positionTaskInStack: no stack for id=" + + stackId); + } + if (!stack.isActivityTypeStandardOrUndefined()) { + throw new IllegalArgumentException("positionTaskInStack: Attempt to change" + + " the position of task " + taskId + " in/to non-standard stack"); + } // TODO: Have the callers of this API call a separate reparent method if that is // what they intended to do vs. having this method also do reparenting. @@ -10827,8 +10941,8 @@ public class ActivityManagerService extends IActivityManager.Stub stack.positionChildAt(task, position); } else { // Reparent to new stack. - task.reparent(stackId, position, REPARENT_LEAVE_STACK_IN_PLACE, - !ANIMATE, !DEFER_RESUME, "positionTaskInStack"); + task.reparent(stack, position, REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, + !DEFER_RESUME, "positionTaskInStack"); } } finally { Binder.restoreCallingIdentity(ident); @@ -10850,12 +10964,12 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public StackInfo getStackInfo(int stackId) { + public StackInfo getStackInfo(int windowingMode, int activityType) { enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()"); long ident = Binder.clearCallingIdentity(); try { synchronized (this) { - return mStackSupervisor.getStackInfoLocked(stackId); + return mStackSupervisor.getStackInfo(windowingMode, activityType); } } finally { Binder.restoreCallingIdentity(ident); @@ -10882,7 +10996,6 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void updateLockTaskPackages(int userId, String[] packages) { - // TODO: move this into LockTaskController final int callingUid = Binder.getCallingUid(); if (callingUid != 0 && callingUid != SYSTEM_UID) { enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES, @@ -10891,8 +11004,7 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized (this) { if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Whitelisting " + userId + ":" + Arrays.toString(packages)); - mLockTaskPackages.put(userId, packages); - mLockTaskController.onLockTaskPackagesUpdated(); + mLockTaskController.updateLockTaskPackages(userId, packages); } } @@ -10908,11 +11020,7 @@ public class ActivityManagerService extends IActivityManager.Stub } // When a task is locked, dismiss the pinned stack if it exists - final PinnedActivityStack pinnedStack = mStackSupervisor.getStack( - PINNED_STACK_ID); - if (pinnedStack != null) { - mStackSupervisor.removeStackLocked(PINNED_STACK_ID); - } + mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED); // isAppPinning is used to distinguish between locked and pinned mode, as pinned mode // is initiated by system after the pinning request was shown and locked mode is initiated @@ -11139,7 +11247,7 @@ public class ActivityManagerService extends IActivityManager.Stub boolean checkedGrants = false; if (checkUser) { // Looking for cross-user grants before enforcing the typical cross-users permissions - int tmpTargetUserId = mUserController.unsafeConvertIncomingUserLocked(userId); + int tmpTargetUserId = mUserController.unsafeConvertIncomingUser(userId); if (tmpTargetUserId != UserHandle.getUserId(callingUid)) { if (checkAuthorityGrants(callingUid, cpi, tmpTargetUserId, checkUser)) { return null; @@ -11527,7 +11635,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Make sure that the user who owns this provider is running. If not, // we don't want to allow it to run. - if (!mUserController.isUserRunningLocked(userId, 0)) { + if (!mUserController.isUserRunning(userId, 0)) { Slog.w(TAG, "Unable to launch app " + cpi.applicationInfo.packageName + "/" + cpi.applicationInfo.uid + " for provider " @@ -12073,7 +12181,7 @@ public class ActivityManagerService extends IActivityManager.Stub //mUsageStatsService.monitorPackages(); } - private void startPersistentApps(int matchFlags) { + void startPersistentApps(int matchFlags) { if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) return; synchronized (this) { @@ -12094,7 +12202,7 @@ public class ActivityManagerService extends IActivityManager.Stub * When a user is unlocked, we need to install encryption-unaware providers * belonging to any running apps. */ - private void installEncryptionUnawareProviders(int userId) { + void installEncryptionUnawareProviders(int userId) { // We're only interested in providers that are encryption unaware, and // we don't care about uninstalled apps, since there's no way they're // running at this point. @@ -12157,9 +12265,7 @@ public class ActivityManagerService extends IActivityManager.Stub int callingPid = Binder.getCallingPid(); long ident = 0; boolean clearedIdentity = false; - synchronized (this) { - userId = mUserController.unsafeConvertIncomingUserLocked(userId); - } + userId = mUserController.unsafeConvertIncomingUser(userId); if (canClearIdentity(callingPid, callingUid, userId)) { clearedIdentity = true; ident = Binder.clearCallingIdentity(); @@ -12399,19 +12505,14 @@ public class ActivityManagerService extends IActivityManager.Stub void onWakefulnessChanged(int wakefulness) { synchronized(this) { + boolean wasAwake = mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE; + boolean isAwake = wakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE; mWakefulness = wakefulness; - // Also update state in a special way for running foreground services UI. - switch (mWakefulness) { - case PowerManagerInternal.WAKEFULNESS_ASLEEP: - case PowerManagerInternal.WAKEFULNESS_DREAMING: - case PowerManagerInternal.WAKEFULNESS_DOZING: - mServices.updateScreenStateLocked(false /* screenOn */); - break; - case PowerManagerInternal.WAKEFULNESS_AWAKE: - default: - mServices.updateScreenStateLocked(true /* screenOn */); - break; + if (wasAwake != isAwake) { + // Also update state in a special way for running foreground services UI. + mServices.updateScreenStateLocked(isAwake); + sendNotifyVrManagerOfSleepState(!isAwake); } } } @@ -12447,7 +12548,6 @@ public class ActivityManagerService extends IActivityManager.Stub } mStackSupervisor.applySleepTokensLocked(true /* applyToStacks */); if (wasSleeping) { - sendNotifyVrManagerOfSleepState(false); updateOomAdjLocked(); } } else if (!mSleeping && shouldSleep) { @@ -12457,7 +12557,6 @@ public class ActivityManagerService extends IActivityManager.Stub } mTopProcessState = ActivityManager.PROCESS_STATE_TOP_SLEEPING; mStackSupervisor.goingToSleepLocked(); - sendNotifyVrManagerOfSleepState(true); updateOomAdjLocked(); } } @@ -12551,7 +12650,7 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void setLockScreenShown(boolean showing) { + public void setLockScreenShown(boolean showing, int secondaryDisplayShowing) { if (checkCallingPermission(android.Manifest.permission.DEVICE_POWER) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires permission " @@ -12561,11 +12660,12 @@ public class ActivityManagerService extends IActivityManager.Stub synchronized(this) { long ident = Binder.clearCallingIdentity(); try { - mKeyguardController.setKeyguardShown(showing); + mKeyguardController.setKeyguardShown(showing, secondaryDisplayShowing); } finally { Binder.restoreCallingIdentity(ident); } } + sendNotifyVrManagerOfKeyguardState(showing); } @Override @@ -12584,7 +12684,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (mUserController.shouldConfirmCredentials(userId)) { if (mKeyguardController.isKeyguardLocked()) { // Showing launcher to avoid user entering credential twice. - final int currentUserId = mUserController.getCurrentUserIdLocked(); + final int currentUserId = mUserController.getCurrentUserId(); startHomeActivityLocked(currentUserId, "notifyLockedProfile"); } mStackSupervisor.lockAllProfileTasks(userId); @@ -13982,10 +14082,10 @@ public class ActivityManagerService extends IActivityManager.Stub mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT) || Settings.Global.getInt( resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0; - final boolean supportsPictureInPicture = - mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE); final boolean supportsMultiWindow = ActivityManager.supportsMultiWindow(mContext); + final boolean supportsPictureInPicture = supportsMultiWindow && + mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE); final boolean supportsSplitScreenMultiWindow = ActivityManager.supportsSplitScreenMultiWindow(mContext); final boolean supportsMultiDisplay = mContext.getPackageManager() @@ -14164,9 +14264,8 @@ public class ActivityManagerService extends IActivityManager.Stub } retrieveSettings(); - final int currentUserId; + final int currentUserId = mUserController.getCurrentUserId(); synchronized (this) { - currentUserId = mUserController.getCurrentUserIdLocked(); readGrantedUriPermissionsLocked(); } @@ -14245,7 +14344,7 @@ public class ActivityManagerService extends IActivityManager.Stub Binder.restoreCallingIdentity(ident); } mStackSupervisor.resumeFocusedStackTopActivityLocked(); - mUserController.sendUserSwitchBroadcastsLocked(-1, currentUserId); + mUserController.sendUserSwitchBroadcasts(-1, currentUserId); traceLog.traceEnd(); // ActivityManagerStartApps traceLog.traceEnd(); // PhaseActivityManagerReady } @@ -14597,6 +14696,9 @@ public class ActivityManagerService extends IActivityManager.Stub } sb.append("\n"); } + if (process.info.isInstantApp()) { + sb.append("Instant-App: true\n"); + } } } @@ -14943,6 +15045,13 @@ public class ActivityManagerService extends IActivityManager.Stub @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + PriorityDump.dump(mPriorityDumper, fd, pw, args); + } + + /** + * Wrapper function to print out debug data filtered by specified arguments. + */ + private void doDump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return; boolean dumpAll = false; @@ -14951,6 +15060,7 @@ public class ActivityManagerService extends IActivityManager.Stub boolean dumpCheckinFormat = false; boolean dumpVisibleStacksOnly = false; boolean dumpFocusedStackOnly = false; + boolean useProto = false; String dumpPackage = null; int opti = 0; @@ -14984,12 +15094,26 @@ public class ActivityManagerService extends IActivityManager.Stub } else if ("-h".equals(opt)) { ActivityManagerShellCommand.dumpHelp(pw, true); return; + } else if ("--proto".equals(opt)) { + useProto = true; } else { pw.println("Unknown argument: " + opt + "; use -h for help"); } } long origId = Binder.clearCallingIdentity(); + + if (useProto) { + //TODO: Options when dumping proto + final ProtoOutputStream proto = new ProtoOutputStream(fd); + synchronized (this) { + writeActivitiesToProtoLocked(proto); + } + proto.flush(); + Binder.restoreCallingIdentity(origId); + return; + } + boolean more = false; // Is the caller requesting to dump a particular piece of data? if (opti < args.length) { @@ -15333,6 +15457,10 @@ public class ActivityManagerService extends IActivityManager.Stub Binder.restoreCallingIdentity(origId); } + private void writeActivitiesToProtoLocked(ProtoOutputStream proto) { + mStackSupervisor.writeToProto(proto, ACTIVITIES); + } + private void dumpLastANRLocked(PrintWriter pw) { pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)"); if (mLastANRState == null) { @@ -19047,7 +19175,7 @@ public class ActivityManagerService extends IActivityManager.Stub // If not, we will just skip it. Make an exception for shutdown broadcasts // and upgrade steps. - if (userId != UserHandle.USER_ALL && !mUserController.isUserRunningLocked(userId, 0)) { + if (userId != UserHandle.USER_ALL && !mUserController.isUserRunning(userId, 0)) { if ((callingUid != SYSTEM_UID || (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) && !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) { @@ -19466,7 +19594,7 @@ public class ActivityManagerService extends IActivityManager.Stub int[] users; if (userId == UserHandle.USER_ALL) { // Caller wants broadcast to go to all started users. - users = mUserController.getStartedUserArrayLocked(); + users = mUserController.getStartedUserArray(); } else { // Caller wants broadcast to go to one specific user. users = new int[] {userId}; @@ -20092,12 +20220,20 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public int getFocusedStackId() throws RemoteException { - ActivityStack focusedStack = getFocusedStack(); - if (focusedStack != null) { - return focusedStack.getStackId(); + public StackInfo getFocusedStackInfo() throws RemoteException { + enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()"); + long ident = Binder.clearCallingIdentity(); + try { + synchronized (this) { + ActivityStack focusedStack = getFocusedStack(); + if (focusedStack != null) { + return mStackSupervisor.getStackInfo(focusedStack.mStackId); + } + return null; + } + } finally { + Binder.restoreCallingIdentity(ident); } - return -1; } public Configuration getConfiguration() { @@ -20123,15 +20259,20 @@ public class ActivityManagerService extends IActivityManager.Stub * activity and clearing the task at the same time. */ @Override + // TODO: API should just be about changing windowing modes... public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) { enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTasksToFullscreenStack()"); - if (StackId.isHomeOrRecentsStack(fromStackId)) { - throw new IllegalArgumentException("You can't move tasks from the home/recents stack."); - } synchronized (this) { final long origId = Binder.clearCallingIdentity(); try { - mStackSupervisor.moveTasksToFullscreenStackLocked(fromStackId, onTop); + final ActivityStack stack = mStackSupervisor.getStack(fromStackId); + if (stack != null){ + if (!stack.isActivityTypeStandardOrUndefined()) { + throw new IllegalArgumentException( + "You can't move tasks from non-standard stacks."); + } + mStackSupervisor.moveTasksToFullscreenStackLocked(stack, onTop); + } } finally { Binder.restoreCallingIdentity(origId); } @@ -20229,7 +20370,7 @@ public class ActivityManagerService extends IActivityManager.Stub void updateUserConfigurationLocked() { final Configuration configuration = new Configuration(getGlobalConfiguration()); - final int currentUserId = mUserController.getCurrentUserIdLocked(); + final int currentUserId = mUserController.getCurrentUserId(); Settings.System.adjustConfigurationForUser(mContext.getContentResolver(), configuration, currentUserId, Settings.System.canWrite(mContext)); updateConfigurationLocked(configuration, null /* starting */, false /* initLocale */, @@ -20340,7 +20481,7 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig); // TODO(multi-display): Update UsageEvents#Event to include displayId. mUsageStatsService.reportConfigurationChange(mTempConfig, - mUserController.getCurrentUserIdLocked()); + mUserController.getCurrentUserId()); // TODO: If our config changes, should we auto dismiss any currently showing dialogs? mShowDialogs = shouldShowDialogs(mTempConfig); @@ -22271,7 +22412,7 @@ public class ActivityManagerService extends IActivityManager.Stub String authority) { if (app == null) return; if (app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { - UserState userState = mUserController.getStartedUserStateLocked(app.userId); + UserState userState = mUserController.getStartedUserState(app.userId); if (userState == null) return; final long now = SystemClock.elapsedRealtime(); Long lastReported = userState.mProviderLastReportedFg.get(authority); @@ -23566,54 +23707,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public boolean switchUser(final int targetUserId) { - enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, targetUserId); - int currentUserId; - UserInfo targetUserInfo; - synchronized (this) { - currentUserId = mUserController.getCurrentUserIdLocked(); - targetUserInfo = mUserController.getUserInfo(targetUserId); - if (targetUserId == currentUserId) { - Slog.i(TAG, "user #" + targetUserId + " is already the current user"); - return true; - } - if (targetUserInfo == null) { - Slog.w(TAG, "No user info for user #" + targetUserId); - return false; - } - if (!targetUserInfo.isDemo() && UserManager.isDeviceInDemoMode(mContext)) { - Slog.w(TAG, "Cannot switch to non-demo user #" + targetUserId - + " when device is in demo mode"); - return false; - } - if (!targetUserInfo.supportsSwitchTo()) { - Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported"); - return false; - } - if (targetUserInfo.isManagedProfile()) { - Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user"); - return false; - } - mUserController.setTargetUserIdLocked(targetUserId); - } - if (mUserController.mUserSwitchUiEnabled) { - UserInfo currentUserInfo = mUserController.getUserInfo(currentUserId); - Pair<UserInfo, UserInfo> userNames = new Pair<>(currentUserInfo, targetUserInfo); - mUiHandler.removeMessages(START_USER_SWITCH_UI_MSG); - mUiHandler.sendMessage(mHandler.obtainMessage( - START_USER_SWITCH_UI_MSG, userNames)); - } else { - mHandler.removeMessages(START_USER_SWITCH_FG_MSG); - mHandler.sendMessage(mHandler.obtainMessage( - START_USER_SWITCH_FG_MSG, targetUserId, 0)); - } - return true; - } - - void scheduleStartProfilesLocked() { - if (!mHandler.hasMessages(START_PROFILES_MSG)) { - mHandler.sendMessageDelayed(mHandler.obtainMessage(START_PROFILES_MSG), - DateUtils.SECOND_IN_MILLIS); - } + return mUserController.switchUser(targetUserId); } @Override @@ -23627,10 +23721,8 @@ public class ActivityManagerService extends IActivityManager.Stub } String getStartedUserState(int userId) { - synchronized (this) { - final UserState userState = mUserController.getStartedUserStateLocked(userId); - return UserState.stateToString(userState.state); - } + final UserState userState = mUserController.getStartedUserState(userId); + return UserState.stateToString(userState.state); } @Override @@ -23645,9 +23737,7 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.w(TAG, msg); throw new SecurityException(msg); } - synchronized (this) { - return mUserController.isUserRunningLocked(userId, flags); - } + return mUserController.isUserRunning(userId, flags); } @Override @@ -23661,9 +23751,7 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.w(TAG, msg); throw new SecurityException(msg); } - synchronized (this) { - return mUserController.getStartedUserArrayLocked(); - } + return mUserController.getStartedUserArray(); } @Override @@ -23684,9 +23772,7 @@ public class ActivityManagerService extends IActivityManager.Stub } public boolean isUserStopped(int userId) { - synchronized (this) { - return mUserController.getStartedUserStateLocked(userId) == null; - } + return mUserController.getStartedUserState(userId) == null; } ActivityInfo getActivityInfoForUser(ActivityInfo aInfo, int userId) { @@ -24025,7 +24111,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void notifyKeyguardTrustedChanged() { synchronized (ActivityManagerService.this) { - if (mKeyguardController.isKeyguardShowing()) { + if (mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)) { mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); } } @@ -24274,7 +24360,7 @@ public class ActivityManagerService extends IActivityManager.Stub permission.INTERACT_ACROSS_USERS_FULL, "getLastResumedActivityUserId()"); synchronized (this) { if (mLastResumedActivity == null) { - return mUserController.getCurrentUserIdLocked(); + return mUserController.getCurrentUserId(); } return mLastResumedActivity.userId; } @@ -24481,7 +24567,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (updateFrameworkRes || packagesToUpdate.contains(packageName)) { try { final ApplicationInfo ai = AppGlobals.getPackageManager() - .getApplicationInfo(packageName, 0 /*flags*/, app.userId); + .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId); if (ai != null) { app.thread.scheduleApplicationInfoChanged(ai); } diff --git a/com/android/server/am/ActivityManagerShellCommand.java b/com/android/server/am/ActivityManagerShellCommand.java index 6901d2de..4c934232 100644 --- a/com/android/server/am/ActivityManagerShellCommand.java +++ b/com/android/server/am/ActivityManagerShellCommand.java @@ -75,6 +75,9 @@ import static android.app.ActivityManager.RESIZE_MODE_SYSTEM; import static android.app.ActivityManager.RESIZE_MODE_USER; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.Display.INVALID_DISPLAY; import static com.android.server.am.TaskRecord.INVALID_TASK_ID; @@ -115,7 +118,8 @@ final class ActivityManagerShellCommand extends ShellCommand { private boolean mStreaming; // Streaming the profiling output to a file. private String mAgent; // Agent to attach on startup. private int mDisplayId; - private int mStackId; + private int mWindowingMode; + private int mActivityType; private int mTaskId; private boolean mIsTaskOverlay; @@ -271,7 +275,8 @@ final class ActivityManagerShellCommand extends ShellCommand { mStreaming = false; mUserId = defUser; mDisplayId = INVALID_DISPLAY; - mStackId = INVALID_STACK_ID; + mWindowingMode = WINDOWING_MODE_UNDEFINED; + mActivityType = ACTIVITY_TYPE_UNDEFINED; mTaskId = INVALID_TASK_ID; mIsTaskOverlay = false; @@ -308,8 +313,10 @@ final class ActivityManagerShellCommand extends ShellCommand { mReceiverPermission = getNextArgRequired(); } else if (opt.equals("--display")) { mDisplayId = Integer.parseInt(getNextArgRequired()); - } else if (opt.equals("--stack")) { - mStackId = Integer.parseInt(getNextArgRequired()); + } else if (opt.equals("--windowingMode")) { + mWindowingMode = Integer.parseInt(getNextArgRequired()); + } else if (opt.equals("--activityType")) { + mActivityType = Integer.parseInt(getNextArgRequired()); } else if (opt.equals("--task")) { mTaskId = Integer.parseInt(getNextArgRequired()); } else if (opt.equals("--task-overlay")) { @@ -396,9 +403,17 @@ final class ActivityManagerShellCommand extends ShellCommand { options = ActivityOptions.makeBasic(); options.setLaunchDisplayId(mDisplayId); } - if (mStackId != INVALID_STACK_ID) { - options = ActivityOptions.makeBasic(); - options.setLaunchStackId(mStackId); + if (mWindowingMode != WINDOWING_MODE_UNDEFINED) { + if (options == null) { + options = ActivityOptions.makeBasic(); + } + options.setLaunchWindowingMode(mWindowingMode); + } + if (mActivityType != ACTIVITY_TYPE_UNDEFINED) { + if (options == null) { + options = ActivityOptions.makeBasic(); + } + options.setLaunchActivityType(mActivityType); } if (mTaskId != INVALID_TASK_ID) { options = ActivityOptions.makeBasic(); @@ -2099,9 +2114,9 @@ final class ActivityManagerShellCommand extends ShellCommand { } int runStackInfo(PrintWriter pw) throws RemoteException { - String stackIdStr = getNextArgRequired(); - int stackId = Integer.parseInt(stackIdStr); - ActivityManager.StackInfo info = mInterface.getStackInfo(stackId); + int windowingMode = Integer.parseInt(getNextArgRequired()); + int activityType = Integer.parseInt(getNextArgRequired()); + ActivityManager.StackInfo info = mInterface.getStackInfo(windowingMode, activityType); pw.println(info); return 0; } @@ -2135,7 +2150,8 @@ final class ActivityManagerShellCommand extends ShellCommand { final String delayStr = getNextArg(); final int delayMs = (delayStr != null) ? Integer.parseInt(delayStr) : 0; - ActivityManager.StackInfo info = mInterface.getStackInfo(DOCKED_STACK_ID); + ActivityManager.StackInfo info = mInterface.getStackInfo( + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED); if (info == null) { err.println("Docked stack doesn't exist"); return -1; @@ -2238,10 +2254,6 @@ final class ActivityManagerShellCommand extends ShellCommand { return runTaskResizeable(pw); } else if (op.equals("resize")) { return runTaskResize(pw); - } else if (op.equals("drag-task-test")) { - return runTaskDragTaskTest(pw); - } else if (op.equals("size-task-test")) { - return runTaskSizeTaskTest(pw); } else if (op.equals("focus")) { return runTaskFocus(pw); } else { @@ -2294,58 +2306,6 @@ final class ActivityManagerShellCommand extends ShellCommand { } } - int runTaskDragTaskTest(PrintWriter pw) throws RemoteException { - final int taskId = Integer.parseInt(getNextArgRequired()); - final int stepSize = Integer.parseInt(getNextArgRequired()); - final String delayStr = getNextArg(); - final int delay_ms = (delayStr != null) ? Integer.parseInt(delayStr) : 0; - final ActivityManager.StackInfo stackInfo; - Rect taskBounds; - stackInfo = mInterface.getStackInfo(mInterface.getFocusedStackId()); - taskBounds = mInterface.getTaskBounds(taskId); - final Rect stackBounds = stackInfo.bounds; - int travelRight = stackBounds.width() - taskBounds.width(); - int travelLeft = -travelRight; - int travelDown = stackBounds.height() - taskBounds.height(); - int travelUp = -travelDown; - int passes = 0; - - // We do 2 passes to get back to the original location of the task. - while (passes < 2) { - // Move right - pw.println("Moving right..."); - pw.flush(); - travelRight = moveTask(taskId, taskBounds, stackBounds, stepSize, - travelRight, MOVING_FORWARD, MOVING_HORIZONTALLY, delay_ms); - pw.println("Still need to travel right by " + travelRight); - - // Move down - pw.println("Moving down..."); - pw.flush(); - travelDown = moveTask(taskId, taskBounds, stackBounds, stepSize, - travelDown, MOVING_FORWARD, !MOVING_HORIZONTALLY, delay_ms); - pw.println("Still need to travel down by " + travelDown); - - // Move left - pw.println("Moving left..."); - pw.flush(); - travelLeft = moveTask(taskId, taskBounds, stackBounds, stepSize, - travelLeft, !MOVING_FORWARD, MOVING_HORIZONTALLY, delay_ms); - pw.println("Still need to travel left by " + travelLeft); - - // Move up - pw.println("Moving up..."); - pw.flush(); - travelUp = moveTask(taskId, taskBounds, stackBounds, stepSize, - travelUp, !MOVING_FORWARD, !MOVING_HORIZONTALLY, delay_ms); - pw.println("Still need to travel up by " + travelUp); - - taskBounds = mInterface.getTaskBounds(taskId); - passes++; - } - return 0; - } - int moveTask(int taskId, Rect taskRect, Rect stackRect, int stepSize, int maxToTravel, boolean movingForward, boolean horizontal, int delay_ms) throws RemoteException { @@ -2408,133 +2368,6 @@ final class ActivityManagerShellCommand extends ShellCommand { return stepSize; } - int runTaskSizeTaskTest(PrintWriter pw) throws RemoteException { - final int taskId = Integer.parseInt(getNextArgRequired()); - final int stepSize = Integer.parseInt(getNextArgRequired()); - final String delayStr = getNextArg(); - final int delay_ms = (delayStr != null) ? Integer.parseInt(delayStr) : 0; - final ActivityManager.StackInfo stackInfo; - final Rect initialTaskBounds; - stackInfo = mInterface.getStackInfo(mInterface.getFocusedStackId()); - initialTaskBounds = mInterface.getTaskBounds(taskId); - final Rect stackBounds = stackInfo.bounds; - stackBounds.inset(STACK_BOUNDS_INSET, STACK_BOUNDS_INSET); - final Rect currentTaskBounds = new Rect(initialTaskBounds); - - // Size by top-left - pw.println("Growing top-left"); - pw.flush(); - do { - currentTaskBounds.top -= getStepSize( - currentTaskBounds.top, stackBounds.top, stepSize, GREATER_THAN_TARGET); - - currentTaskBounds.left -= getStepSize( - currentTaskBounds.left, stackBounds.left, stepSize, GREATER_THAN_TARGET); - - taskResize(taskId, currentTaskBounds, delay_ms, true); - } while (stackBounds.top < currentTaskBounds.top - || stackBounds.left < currentTaskBounds.left); - - // Back to original size - pw.println("Shrinking top-left"); - pw.flush(); - do { - currentTaskBounds.top += getStepSize( - currentTaskBounds.top, initialTaskBounds.top, stepSize, !GREATER_THAN_TARGET); - - currentTaskBounds.left += getStepSize( - currentTaskBounds.left, initialTaskBounds.left, stepSize, !GREATER_THAN_TARGET); - - taskResize(taskId, currentTaskBounds, delay_ms, true); - } while (initialTaskBounds.top > currentTaskBounds.top - || initialTaskBounds.left > currentTaskBounds.left); - - // Size by top-right - pw.println("Growing top-right"); - pw.flush(); - do { - currentTaskBounds.top -= getStepSize( - currentTaskBounds.top, stackBounds.top, stepSize, GREATER_THAN_TARGET); - - currentTaskBounds.right += getStepSize( - currentTaskBounds.right, stackBounds.right, stepSize, !GREATER_THAN_TARGET); - - taskResize(taskId, currentTaskBounds, delay_ms, true); - } while (stackBounds.top < currentTaskBounds.top - || stackBounds.right > currentTaskBounds.right); - - // Back to original size - pw.println("Shrinking top-right"); - pw.flush(); - do { - currentTaskBounds.top += getStepSize( - currentTaskBounds.top, initialTaskBounds.top, stepSize, !GREATER_THAN_TARGET); - - currentTaskBounds.right -= getStepSize(currentTaskBounds.right, initialTaskBounds.right, - stepSize, GREATER_THAN_TARGET); - - taskResize(taskId, currentTaskBounds, delay_ms, true); - } while (initialTaskBounds.top > currentTaskBounds.top - || initialTaskBounds.right < currentTaskBounds.right); - - // Size by bottom-left - pw.println("Growing bottom-left"); - pw.flush(); - do { - currentTaskBounds.bottom += getStepSize( - currentTaskBounds.bottom, stackBounds.bottom, stepSize, !GREATER_THAN_TARGET); - - currentTaskBounds.left -= getStepSize( - currentTaskBounds.left, stackBounds.left, stepSize, GREATER_THAN_TARGET); - - taskResize(taskId, currentTaskBounds, delay_ms, true); - } while (stackBounds.bottom > currentTaskBounds.bottom - || stackBounds.left < currentTaskBounds.left); - - // Back to original size - pw.println("Shrinking bottom-left"); - pw.flush(); - do { - currentTaskBounds.bottom -= getStepSize(currentTaskBounds.bottom, - initialTaskBounds.bottom, stepSize, GREATER_THAN_TARGET); - - currentTaskBounds.left += getStepSize( - currentTaskBounds.left, initialTaskBounds.left, stepSize, !GREATER_THAN_TARGET); - - taskResize(taskId, currentTaskBounds, delay_ms, true); - } while (initialTaskBounds.bottom < currentTaskBounds.bottom - || initialTaskBounds.left > currentTaskBounds.left); - - // Size by bottom-right - pw.println("Growing bottom-right"); - pw.flush(); - do { - currentTaskBounds.bottom += getStepSize( - currentTaskBounds.bottom, stackBounds.bottom, stepSize, !GREATER_THAN_TARGET); - - currentTaskBounds.right += getStepSize( - currentTaskBounds.right, stackBounds.right, stepSize, !GREATER_THAN_TARGET); - - taskResize(taskId, currentTaskBounds, delay_ms, true); - } while (stackBounds.bottom > currentTaskBounds.bottom - || stackBounds.right > currentTaskBounds.right); - - // Back to original size - pw.println("Shrinking bottom-right"); - pw.flush(); - do { - currentTaskBounds.bottom -= getStepSize(currentTaskBounds.bottom, - initialTaskBounds.bottom, stepSize, GREATER_THAN_TARGET); - - currentTaskBounds.right -= getStepSize(currentTaskBounds.right, initialTaskBounds.right, - stepSize, GREATER_THAN_TARGET); - - taskResize(taskId, currentTaskBounds, delay_ms, true); - } while (initialTaskBounds.bottom < currentTaskBounds.bottom - || initialTaskBounds.right < currentTaskBounds.right); - return 0; - } - int runTaskFocus(PrintWriter pw) throws RemoteException { final int taskId = Integer.parseInt(getNextArgRequired()); pw.println("Setting focus to task " + taskId); @@ -2661,6 +2494,7 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" -p: limit output to given package."); pw.println(" --checkin: output checkin format, resetting data."); pw.println(" --C: output checkin format, not resetting data."); + pw.println(" --proto: output dump in protocol buffer format."); } else { pw.println("Activity manager (activity) commands:"); pw.println(" help"); @@ -2685,7 +2519,8 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" --track-allocation: enable tracking of object allocations"); pw.println(" --user <USER_ID> | current: Specify which user to run as; if not"); pw.println(" specified then run as the current user."); - pw.println(" --stack <STACK_ID>: Specify into which stack should the activity be put."); + pw.println(" --windowingMode <WINDOWING_MODE>: The windowing mode to launch the activity into."); + pw.println(" --activityType <ACTIVITY_TYPE>: The activity type to launch the activity as."); pw.println(" start-service [--user <USER_ID> | current] <INTENT>"); pw.println(" Start a Service. Options are:"); pw.println(" --user <USER_ID> | current: Specify which user to run as; if not"); @@ -2864,8 +2699,8 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" Place <TASK_ID> in <STACK_ID> at <POSITION>"); pw.println(" list"); pw.println(" List all of the activity stacks and their sizes."); - pw.println(" info <STACK_ID>"); - pw.println(" Display the information about activity stack <STACK_ID>."); + pw.println(" info <WINDOWING_MODE> <ACTIVITY_TYPE>"); + pw.println(" Display the information about activity stack in <WINDOWING_MODE> and <ACTIVITY_TYPE>."); pw.println(" remove <STACK_ID>"); pw.println(" Remove stack <STACK_ID>."); pw.println(" task [COMMAND] [...]: sub-commands for operating on activity tasks."); @@ -2883,14 +2718,6 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" Makes sure <TASK_ID> is in a stack with the specified bounds."); pw.println(" Forces the task to be resizeable and creates a stack if no existing stack"); pw.println(" has the specified bounds."); - pw.println(" drag-task-test <TASK_ID> <STEP_SIZE> [DELAY_MS]"); - pw.println(" Test command for dragging/moving <TASK_ID> by"); - pw.println(" <STEP_SIZE> increments around the screen applying the optional [DELAY_MS]"); - pw.println(" between each step."); - pw.println(" size-task-test <TASK_ID> <STEP_SIZE> [DELAY_MS]"); - pw.println(" Test command for sizing <TASK_ID> by <STEP_SIZE>"); - pw.println(" increments within the screen applying the optional [DELAY_MS] between"); - pw.println(" each step."); pw.println(" update-appinfo <USER_ID> <PACKAGE_NAME> [<PACKAGE_NAME>...]"); pw.println(" Update the ApplicationInfo objects of the listed packages for <USER_ID>"); pw.println(" without restarting any processes."); diff --git a/com/android/server/am/ActivityMetricsLogger.java b/com/android/server/am/ActivityMetricsLogger.java index 0c8321d5..fdcb8c69 100644 --- a/com/android/server/am/ActivityMetricsLogger.java +++ b/com/android/server/am/ActivityMetricsLogger.java @@ -2,12 +2,9 @@ package com.android.server.am; import static android.app.ActivityManager.START_SUCCESS; import static android.app.ActivityManager.START_TASK_TO_FRONT; -import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID; -import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.ActivityManagerInternal.APP_TRANSITION_TIMEOUT; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; @@ -32,13 +29,10 @@ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_T import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.server.am.ActivityStack.STACK_INVISIBLE; -import android.app.ActivityManager.StackId; import android.content.Context; import android.metrics.LogMaker; import android.os.SystemClock; -import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; diff --git a/com/android/server/am/ActivityRecord.java b/com/android/server/am/ActivityRecord.java index 0ccb45f7..7b0b942a 100644 --- a/com/android/server/am/ActivityRecord.java +++ b/com/android/server/am/ActivityRecord.java @@ -17,8 +17,6 @@ package com.android.server.am; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; -import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID; -import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; @@ -36,7 +34,6 @@ import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.activityTypeToString; import static android.content.Intent.ACTION_MAIN; @@ -89,10 +86,8 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SAVED_STATE; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SCREENSHOTS; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STATES; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH; -import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_THUMBNAILS; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -107,7 +102,6 @@ import static com.android.server.am.ActivityStack.ActivityState.STOPPING; import static com.android.server.am.ActivityStack.LAUNCH_TICK; import static com.android.server.am.ActivityStack.LAUNCH_TICK_MSG; import static com.android.server.am.ActivityStack.PAUSE_TIMEOUT_MSG; -import static com.android.server.am.ActivityStack.STACK_INVISIBLE; import static com.android.server.am.ActivityStack.STOP_TIMEOUT_MSG; import static com.android.server.am.EventLogTags.AM_ACTIVITY_FULLY_DRAWN_TIME; import static com.android.server.am.EventLogTags.AM_ACTIVITY_LAUNCH_TIME; @@ -116,6 +110,16 @@ import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY; import static com.android.server.am.TaskPersister.DEBUG; import static com.android.server.am.TaskPersister.IMAGE_EXTENSION; import static com.android.server.am.TaskRecord.INVALID_TASK_ID; +import static com.android.server.am.proto.ActivityRecordProto.CONFIGURATION_CONTAINER; +import static com.android.server.am.proto.ActivityRecordProto.FRONT_OF_TASK; +import static com.android.server.am.proto.ActivityRecordProto.IDENTIFIER; +import static com.android.server.am.proto.ActivityRecordProto.PROC_ID; +import static com.android.server.am.proto.ActivityRecordProto.STATE; +import static com.android.server.am.proto.ActivityRecordProto.VISIBLE; +import static com.android.server.wm.proto.IdentifierProto.HASH_CODE; +import static com.android.server.wm.proto.IdentifierProto.TITLE; +import static com.android.server.wm.proto.IdentifierProto.USER_ID; + import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.END_TAG; import static org.xmlpull.v1.XmlPullParser.START_TAG; @@ -153,6 +157,7 @@ import android.util.Log; import android.util.MergedConfiguration; import android.util.Slog; import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; import android.view.AppTransitionAnimationSpec; import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IApplicationToken; @@ -1038,7 +1043,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } } else if (realActivity.getClassName().contains(RECENTS_PACKAGE_NAME)) { activityType = ACTIVITY_TYPE_RECENTS; - } else if (options != null && options.getLaunchStackId() == ASSISTANT_STACK_ID + } else if (options != null && options.getLaunchActivityType() == ACTIVITY_TYPE_ASSISTANT && canLaunchAssistActivity(launchedFromPackage)) { activityType = ACTIVITY_TYPE_ASSISTANT; } @@ -1062,6 +1067,11 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo return getStack() != null ? getStack().mStackId : INVALID_STACK_ID; } + ActivityDisplay getDisplay() { + final ActivityStack stack = getStack(); + return stack != null ? stack.getDisplay() : null; + } + boolean changeWindowTranslucency(boolean toOpaque) { if (fullscreen == toOpaque) { return false; @@ -1127,10 +1137,12 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo * @return whether this activity supports split-screen multi-window and can be put in the docked * stack. */ - boolean supportsSplitScreen() { + @Override + public boolean supportsSplitScreenWindowingMode() { // An activity can not be docked even if it is considered resizeable because it only // supports picture-in-picture mode but has a non-resizeable resizeMode - return service.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow(); + return super.supportsSplitScreenWindowingMode() + && service.mSupportsSplitScreenMultiWindow && supportsResizeableMultiWindow(); } /** @@ -1157,8 +1169,15 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo * can be put a secondary screen. */ boolean canBeLaunchedOnDisplay(int displayId) { + final TaskRecord task = getTask(); + + // The resizeability of an Activity's parent task takes precendence over the ActivityInfo. + // This allows for a non resizable activity to be launched into a resizeable task. + final boolean resizeable = + task != null ? task.isResizeable() : supportsResizeableMultiWindow(); + return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId, - supportsResizeableMultiWindow(), launchedFromPid, launchedFromUid, info); + resizeable, launchedFromPid, launchedFromUid, info); } /** @@ -1184,7 +1203,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo boolean isKeyguardLocked = service.isKeyguardLocked(); boolean isCurrentAppLocked = service.getLockTaskModeState() != LOCK_TASK_MODE_NONE; - boolean hasPinnedStack = mStackSupervisor.getStack(PINNED_STACK_ID) != null; + final ActivityDisplay display = getDisplay(); + boolean hasPinnedStack = display != null && display.hasPinnedStack(); // Don't return early if !isNotLocked, since we want to throw an exception if the activity // is in an incorrect state boolean isNotLockedOrOnKeyguard = !isKeyguardLocked && !isCurrentAppLocked; @@ -1530,8 +1550,9 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo if (service.mSupportsLeanbackOnly && isVisible && isActivityTypeRecents()) { // On devices that support leanback only (Android TV), Recents activity can only be // visible if the home stack is the focused stack or we are in split-screen mode. - isVisible = mStackSupervisor.getStack(DOCKED_STACK_ID) != null - || mStackSupervisor.isFocusedStack(getStack()); + final ActivityDisplay display = getDisplay(); + boolean hasSplitScreenStack = display != null && display.hasSplitScreenStack(); + isVisible = hasSplitScreenStack || mStackSupervisor.isFocusedStack(getStack()); } return isVisible; @@ -1934,7 +1955,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo return (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0 || (mStackSupervisor.isCurrentProfileLocked(userId) - && service.mUserController.isUserRunningLocked(userId, 0 /* flags */)); + && service.mUserController.isUserRunning(userId, 0 /* flags */)); } /** @@ -2298,7 +2319,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // be visible based on the stack, task, and lockscreen state and use that here instead. The // method should be based on the logic in ActivityStack.ensureActivitiesVisibleLocked(). // Skip updating configuration for activity is a stack that shouldn't be visible. - if (stack.shouldBeVisible(null /* starting */) == STACK_INVISIBLE) { + if (!stack.shouldBeVisible(null /* starting */)) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Skipping config check invisible stack: " + this); return true; @@ -2770,4 +2791,25 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo stringName = sb.toString(); return toString(); } + + void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + proto.write(HASH_CODE, System.identityHashCode(this)); + proto.write(USER_ID, userId); + proto.write(TITLE, intent.getComponent().flattenToShortString()); + proto.end(token); + } + + public void writeToProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + super.writeToProto(proto, CONFIGURATION_CONTAINER); + writeIdentifierToProto(proto, IDENTIFIER); + proto.write(STATE, state.toString()); + proto.write(VISIBLE, visible); + proto.write(FRONT_OF_TASK, frontOfTask); + if (app != null) { + proto.write(PROC_ID, app.pid); + } + proto.end(token); + } } diff --git a/com/android/server/am/ActivityStack.java b/com/android/server/am/ActivityStack.java index a6a702fb..1940ca2b 100644 --- a/com/android/server/am/ActivityStack.java +++ b/com/android/server/am/ActivityStack.java @@ -16,19 +16,19 @@ package com.android.server.am; -import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; -import static android.app.ActivityManager.StackId.getActivityTypeForStackId; -import static android.app.ActivityManager.StackId.getWindowingModeForStackId; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT; import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING; @@ -37,6 +37,8 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; import static android.view.Display.INVALID_DISPLAY; +import static com.android.server.am.ActivityDisplay.POSITION_BOTTOM; +import static com.android.server.am.ActivityDisplay.POSITION_TOP; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_APP; @@ -74,10 +76,16 @@ import static com.android.server.am.ActivityStack.ActivityState.PAUSED; import static com.android.server.am.ActivityStack.ActivityState.STOPPED; import static com.android.server.am.ActivityStack.ActivityState.STOPPING; import static com.android.server.am.ActivityStackSupervisor.FindTaskResult; -import static com.android.server.am.ActivityStackSupervisor.ON_TOP; import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY; import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS; +import static com.android.server.am.proto.ActivityStackProto.BOUNDS; +import static com.android.server.am.proto.ActivityStackProto.CONFIGURATION_CONTAINER; +import static com.android.server.am.proto.ActivityStackProto.DISPLAY_ID; +import static com.android.server.am.proto.ActivityStackProto.FULLSCREEN; +import static com.android.server.am.proto.ActivityStackProto.ID; +import static com.android.server.am.proto.ActivityStackProto.RESUMED_ACTIVITY; +import static com.android.server.am.proto.ActivityStackProto.TASKS; import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_CLOSE; import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN; import static com.android.server.wm.AppTransition.TRANSIT_NONE; @@ -86,6 +94,7 @@ import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN; import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN_BEHIND; import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_BACK; import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT; + import static java.lang.Integer.MAX_VALUE; import android.app.Activity; @@ -101,7 +110,6 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.res.Configuration; -import android.graphics.Point; import android.graphics.Rect; import android.net.Uri; import android.os.Binder; @@ -122,6 +130,7 @@ import android.util.IntArray; import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import android.util.proto.ProtoOutputStream; import android.view.Display; import com.android.internal.annotations.VisibleForTesting; @@ -231,11 +240,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai DESTROYED } - // Stack is not considered visible. - static final int STACK_INVISIBLE = 0; - // Stack is considered visible - static final int STACK_VISIBLE = 1; - @VisibleForTesting /* The various modes for the method {@link #removeTask}. */ // Task is being completely removed from all stacks in the system. @@ -258,7 +262,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final ActivityManagerService mService; private final WindowManagerService mWindowManager; T mWindowContainerController; - private final RecentTasks mRecentTasks; /** * The back history of all previous (and possibly still @@ -341,9 +344,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai int mCurrentUser; final int mStackId; - /** The other stacks, in order, on the attached display. Updated at attach/detach time. */ - // TODO: This list doesn't belong here... - ArrayList<ActivityStack> mStacks; /** The attached Display's unique identifier, or -1 if detached */ int mDisplayId; @@ -452,49 +452,35 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return count; } - ActivityStack(ActivityStackSupervisor.ActivityDisplay display, int stackId, - ActivityStackSupervisor supervisor, RecentTasks recentTasks, boolean onTop) { + ActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor, + int windowingMode, int activityType, boolean onTop) { mStackSupervisor = supervisor; mService = supervisor.mService; mHandler = new ActivityStackHandler(mService.mHandler.getLooper()); mWindowManager = mService.mWindowManager; mStackId = stackId; - mCurrentUser = mService.mUserController.getCurrentUserIdLocked(); - mRecentTasks = recentTasks; + mCurrentUser = mService.mUserController.getCurrentUserId(); mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID ? new LaunchingTaskPositioner() : null; mTmpRect2.setEmpty(); - updateOverrideConfiguration(); + setWindowingMode(windowingMode); + setActivityType(activityType); mWindowContainerController = createStackWindowController(display.mDisplayId, onTop, mTmpRect2); - mStackSupervisor.mStacks.put(mStackId, this); postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop); } T createStackWindowController(int displayId, boolean onTop, Rect outBounds) { - return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds); + return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds, + mStackSupervisor.mWindowManager); } T getWindowContainerController() { return mWindowContainerController; } - // TODO: Not needed once we are no longer using stack ids as the override config. can be passed - // in. - private void updateOverrideConfiguration() { - final int windowingMode = getWindowingModeForStackId( - mStackId, mStackSupervisor.getStack(DOCKED_STACK_ID) != null); - if (windowingMode != WINDOWING_MODE_UNDEFINED) { - setWindowingMode(windowingMode); - } - final int activityType = getActivityTypeForStackId(mStackId); - if (activityType != ACTIVITY_TYPE_UNDEFINED) { - setActivityType(activityType); - } - } - /** Adds the stack to specified display and calls WindowManager to do the same. */ - void reparent(ActivityStackSupervisor.ActivityDisplay activityDisplay, boolean onTop) { + void reparent(ActivityDisplay activityDisplay, boolean onTop) { removeFromDisplay(); mTmpRect2.setEmpty(); postAddToDisplay(activityDisplay, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop); @@ -512,10 +498,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai * @param activityDisplay New display to which this stack was attached. * @param bounds Updated bounds. */ - private void postAddToDisplay(ActivityStackSupervisor.ActivityDisplay activityDisplay, - Rect bounds, boolean onTop) { + private void postAddToDisplay(ActivityDisplay activityDisplay, Rect bounds, boolean onTop) { mDisplayId = activityDisplay.mDisplayId; - mStacks = activityDisplay.mStacks; mBounds = bounds != null ? new Rect(bounds) : null; mFullscreen = mBounds == null; if (mTaskPositioner != null) { @@ -524,7 +508,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } onParentChanged(); - activityDisplay.attachStack(this, findStackInsertIndex(onTop)); + activityDisplay.addChild(this, onTop ? POSITION_TOP : POSITION_BOTTOM); if (mStackId == DOCKED_STACK_ID) { // If we created a docked stack we want to resize it so it resizes all other stacks // in the system. @@ -538,42 +522,36 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai * either destroyed completely or re-parented. */ private void removeFromDisplay() { - final ActivityStackSupervisor.ActivityDisplay display = getDisplay(); + if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + // If we removed a docked stack we want to resize it so it resizes all other stacks + // in the system to fullscreen. + mStackSupervisor.resizeDockedStackLocked( + null, null, null, null, null, PRESERVE_WINDOWS); + } + final ActivityDisplay display = getDisplay(); if (display != null) { - display.detachStack(this); + display.removeChild(this); } mDisplayId = INVALID_DISPLAY; - mStacks = null; if (mTaskPositioner != null) { mTaskPositioner.reset(); } - if (mStackId == DOCKED_STACK_ID) { - // If we removed a docked stack we want to resize it so it resizes all other stacks - // in the system to fullscreen. - mStackSupervisor.resizeDockedStackLocked( - null, null, null, null, null, PRESERVE_WINDOWS); - } } /** Removes the stack completely. Also calls WindowManager to do the same on its side. */ void remove() { removeFromDisplay(); - mStackSupervisor.mStacks.remove(mStackId); mWindowContainerController.removeContainer(); mWindowContainerController = null; onParentChanged(); } - ActivityStackSupervisor.ActivityDisplay getDisplay() { + ActivityDisplay getDisplay() { return mStackSupervisor.getActivityDisplay(mDisplayId); } - void getDisplaySize(Point out) { - getDisplay().mDisplay.getSize(out); - } - /** - * @see ActivityStack.getStackDockedModeBounds(Rect, Rect, Rect, boolean) + * @see #getStackDockedModeBounds(Rect, Rect, Rect, boolean) */ void getStackDockedModeBounds(Rect currentTempTaskBounds, Rect outStackBounds, Rect outTempTaskBounds, boolean ignoreVisibility) { @@ -837,7 +815,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } final boolean isHomeOrRecentsStack() { - return StackId.isHomeOrRecentsStack(mStackId); + return isActivityTypeHome() || isActivityTypeRecents(); } final boolean isDockedStack() { @@ -849,7 +827,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } final boolean isOnHomeDisplay() { - return isAttached() && mDisplayId == DEFAULT_DISPLAY; + return mDisplayId == DEFAULT_DISPLAY; } void moveToFront(String reason) { @@ -865,8 +843,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return; } - mStacks.remove(this); - mStacks.add(findStackInsertIndex(ON_TOP), this); + getDisplay().positionChildAtTop(this); mStackSupervisor.setFocusStackUnchecked(reason, this); if (task != null) { insertTaskAtTop(task, null); @@ -880,45 +857,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } } - /** - * @param task If non-null, the task will be moved to the back of the stack. - * */ - private void moveToBack(TaskRecord task) { - if (!isAttached()) { - return; - } - - mStacks.remove(this); - mStacks.add(0, this); - - if (task != null) { - mTaskHistory.remove(task); - mTaskHistory.add(0, task); - updateTaskMovement(task, false); - mWindowContainerController.positionChildAtBottom(task.getWindowContainerController()); - } - } - - /** - * @return the index to insert a new stack into, taking the always-on-top stacks into account. - */ - private int findStackInsertIndex(boolean onTop) { - if (onTop) { - int addIndex = mStacks.size(); - if (addIndex > 0) { - final ActivityStack topStack = mStacks.get(addIndex - 1); - if (topStack.getWindowConfiguration().isAlwaysOnTop() - && topStack != this) { - // If the top stack is always on top, we move this stack just below it. - addIndex--; - } - } - return addIndex; - } else { - return 0; - } - } - boolean isFocusable() { if (getWindowConfiguration().canReceiveKeys()) { return true; @@ -930,7 +868,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } final boolean isAttached() { - return mStacks != null; + return getParent() != null; } /** @@ -1105,14 +1043,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai "Launch completed; removing icicle of " + r.icicle); } - void addRecentActivityLocked(ActivityRecord r) { - if (r != null) { - final TaskRecord task = r.getTask(); - mRecentTasks.addLocked(task); - task.touchActiveTime(); - } - } - private void startLaunchTraces(String packageName) { if (mFullyDrawnStartTime != 0) { Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0); @@ -1527,7 +1457,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // focus). Also if there is an active pinned stack - we always want to notify it about // task stack changes, because its positioning may depend on it. if (mStackSupervisor.mAppVisibilitiesChangedSinceLastPause - || mService.mStackSupervisor.getStack(PINNED_STACK_ID) != null) { + || getDisplay().hasPinnedStack()) { mService.mTaskChangeNotificationController.notifyTaskStackChanged(); mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = false; } @@ -1559,54 +1489,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } } - // Find the first visible activity above the passed activity and if it is translucent return it - // otherwise return null; - ActivityRecord findNextTranslucentActivity(ActivityRecord r) { - TaskRecord task = r.getTask(); - if (task == null) { - return null; - } - - final ActivityStack stack = task.getStack(); - if (stack == null) { - return null; - } - - int stackNdx = mStacks.indexOf(stack); - - ArrayList<TaskRecord> tasks = stack.mTaskHistory; - int taskNdx = tasks.indexOf(task); - - ArrayList<ActivityRecord> activities = task.mActivities; - int activityNdx = activities.indexOf(r) + 1; - - final int numStacks = mStacks.size(); - while (stackNdx < numStacks) { - final ActivityStack historyStack = mStacks.get(stackNdx); - tasks = historyStack.mTaskHistory; - final int numTasks = tasks.size(); - while (taskNdx < numTasks) { - final TaskRecord currentTask = tasks.get(taskNdx); - activities = currentTask.mActivities; - final int numActivities = activities.size(); - while (activityNdx < numActivities) { - final ActivityRecord activity = activities.get(activityNdx); - if (!activity.finishing) { - return historyStack.mFullscreen - && currentTask.mFullscreen && activity.fullscreen ? null : activity; - } - ++activityNdx; - } - activityNdx = 0; - ++taskNdx; - } - taskNdx = 0; - ++stackNdx; - } - - return null; - } - /** Returns true if the stack contains a fullscreen task. */ private boolean hasFullscreenTask() { for (int i = mTaskHistory.size() - 1; i >= 0; --i) { @@ -1650,9 +1532,11 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return false; } + final ActivityStack stackBehind = mStackSupervisor.getStack(stackBehindId); + final boolean stackBehindHomeOrRecent = stackBehind != null + && stackBehind.isHomeOrRecentsStack(); if (!isHomeOrRecentsStack() && r.frontOfTask && task.isOverHomeStack() - && !StackId.isHomeOrRecentsStack(stackBehindId) - && !isActivityTypeAssistant()) { + && !stackBehindHomeOrRecent && !isActivityTypeAssistant()) { // Stack isn't translucent if it's top activity should have the home stack // behind it and the stack currently behind it isn't the home or recents stack // or the assistant stack. @@ -1670,119 +1554,146 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } /** - * Returns what the stack visibility should be: {@link #STACK_INVISIBLE} or - * {@link #STACK_VISIBLE}. + * Returns true if the stack should be visible. * * @param starting The currently starting activity or null if there is none. */ - int shouldBeVisible(ActivityRecord starting) { + boolean shouldBeVisible(ActivityRecord starting) { if (!isAttached() || mForceHidden) { - return STACK_INVISIBLE; + return false; } if (mStackSupervisor.isFrontStackOnDisplay(this) || mStackSupervisor.isFocusedStack(this)) { - return STACK_VISIBLE; + return true; } - final int stackIndex = mStacks.indexOf(this); + final ActivityDisplay display = getDisplay(); + final ArrayList<ActivityStack> displayStacks = display.mStacks; + final int stackIndex = displayStacks.indexOf(this); - if (stackIndex == mStacks.size() - 1) { + if (stackIndex == displayStacks.size() - 1) { Slog.wtf(TAG, "Stack=" + this + " isn't front stack but is at the top of the stack list"); - return STACK_INVISIBLE; + return false; } // Check position and visibility of this stack relative to the front stack on its display. final ActivityStack topStack = getTopStackOnDisplay(); final int topStackId = topStack.mStackId; + final int windowingMode = getWindowingMode(); + final int activityType = getActivityType(); - if (mStackId == DOCKED_STACK_ID) { + if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { // If the assistant stack is focused and translucent, then the docked stack is always // visible if (topStack.isActivityTypeAssistant()) { - return (topStack.isStackTranslucent(starting, DOCKED_STACK_ID)) ? STACK_VISIBLE - : STACK_INVISIBLE; + return topStack.isStackTranslucent(starting, DOCKED_STACK_ID); } - return STACK_VISIBLE; + return true; } // Set home stack to invisible when it is below but not immediately below the docked stack // A case would be if recents stack exists but has no tasks and is below the docked stack // and home stack is below recents - if (mStackId == HOME_STACK_ID) { - int dockedStackIndex = mStacks.indexOf(mStackSupervisor.getStack(DOCKED_STACK_ID)); + if (activityType == ACTIVITY_TYPE_HOME) { + final ActivityStack splitScreenStack = display.getStack( + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED); + int dockedStackIndex = displayStacks.indexOf(splitScreenStack); if (dockedStackIndex > stackIndex && stackIndex != dockedStackIndex - 1) { - return STACK_INVISIBLE; + return false; } } // Find the first stack behind front stack that actually got something visible. - int stackBehindTopIndex = mStacks.indexOf(topStack) - 1; + int stackBehindTopIndex = displayStacks.indexOf(topStack) - 1; while (stackBehindTopIndex >= 0 && - mStacks.get(stackBehindTopIndex).topRunningActivityLocked() == null) { + displayStacks.get(stackBehindTopIndex).topRunningActivityLocked() == null) { stackBehindTopIndex--; } - final int stackBehindTopId = (stackBehindTopIndex >= 0) - ? mStacks.get(stackBehindTopIndex).mStackId : INVALID_STACK_ID; + final ActivityStack stackBehindTop = (stackBehindTopIndex >= 0) + ? displayStacks.get(stackBehindTopIndex) : null; + int stackBehindTopId = INVALID_STACK_ID; + int stackBehindTopWindowingMode = WINDOWING_MODE_UNDEFINED; + int stackBehindTopActivityType = ACTIVITY_TYPE_UNDEFINED; + if (stackBehindTop != null) { + stackBehindTopId = stackBehindTop.mStackId; + stackBehindTopWindowingMode = stackBehindTop.getWindowingMode(); + stackBehindTopActivityType = stackBehindTop.getActivityType(); + } + final boolean alwaysOnTop = topStack.getWindowConfiguration().isAlwaysOnTop(); - if (topStackId == DOCKED_STACK_ID || alwaysOnTop) { + if (topStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY || alwaysOnTop) { if (stackIndex == stackBehindTopIndex) { // Stacks directly behind the docked or pinned stack are always visible. - return STACK_VISIBLE; + return true; } else if (alwaysOnTop && stackIndex == stackBehindTopIndex - 1) { // Otherwise, this stack can also be visible if it is directly behind a docked stack // or translucent assistant stack behind an always-on-top top-most stack - if (stackBehindTopId == DOCKED_STACK_ID) { - return STACK_VISIBLE; - } else if (stackBehindTopId == ASSISTANT_STACK_ID) { - return mStacks.get(stackBehindTopIndex).isStackTranslucent(starting, mStackId) - ? STACK_VISIBLE : STACK_INVISIBLE; + if (stackBehindTopWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + return true; + } else if (stackBehindTopActivityType == ACTIVITY_TYPE_ASSISTANT) { + return displayStacks.get(stackBehindTopIndex).isStackTranslucent( + starting, mStackId); } } } - if (StackId.isBackdropToTranslucentActivity(topStackId) + if (topStack.isBackdropToTranslucentActivity() && topStack.isStackTranslucent(starting, stackBehindTopId)) { // Stacks behind the fullscreen or assistant stack with a translucent activity are // always visible so they can act as a backdrop to the translucent activity. // For example, dialog activities if (stackIndex == stackBehindTopIndex) { - return STACK_VISIBLE; + return true; } if (stackBehindTopIndex >= 0) { - if ((stackBehindTopId == DOCKED_STACK_ID - || stackBehindTopId == PINNED_STACK_ID) + if ((stackBehindTopWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY + || stackBehindTopWindowingMode == WINDOWING_MODE_PINNED) && stackIndex == (stackBehindTopIndex - 1)) { // The stack behind the docked or pinned stack is also visible so we can have a // complete backdrop to the translucent activity when the docked stack is up. - return STACK_VISIBLE; + return true; } } } - if (StackId.isStaticStack(mStackId)) { + if (StackId.isStaticStack(mStackId) + || isHomeOrRecentsStack() || isActivityTypeAssistant()) { // Visibility of any static stack should have been determined by the conditions above. - return STACK_INVISIBLE; + return false; } - for (int i = stackIndex + 1; i < mStacks.size(); i++) { - final ActivityStack stack = mStacks.get(i); + for (int i = stackIndex + 1; i < displayStacks.size(); i++) { + final ActivityStack stack = displayStacks.get(i); if (!stack.mFullscreen && !stack.hasFullscreenTask()) { continue; } - if (!StackId.isDynamicStacksVisibleBehindAllowed(stack.mStackId)) { + if (!stack.isDynamicStacksVisibleBehindAllowed()) { // These stacks can't have any dynamic stacks visible behind them. - return STACK_INVISIBLE; + return false; } if (!stack.isStackTranslucent(starting, INVALID_STACK_ID)) { - return STACK_INVISIBLE; + return false; } } - return STACK_VISIBLE; + return true; + } + + private boolean isBackdropToTranslucentActivity() { + if (isActivityTypeAssistant()) { + return true; + } + final int windowingMode = getWindowingMode(); + return windowingMode == WINDOWING_MODE_FULLSCREEN + || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; + } + + private boolean isDynamicStacksVisibleBehindAllowed() { + return isActivityTypeAssistant() || getWindowingMode() == WINDOWING_MODE_PINNED; } final int rankTaskLayers(int baseLayer) { @@ -1819,12 +1730,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // If the top activity is not fullscreen, then we need to // make sure any activities under it are now visible. boolean aboveTop = top != null; - final int stackVisibility = shouldBeVisible(starting); - final boolean stackInvisible = stackVisibility != STACK_VISIBLE; - boolean behindFullscreenActivity = stackInvisible; + final boolean stackShouldBeVisible = shouldBeVisible(starting); + boolean behindFullscreenActivity = !stackShouldBeVisible; boolean resumeNextActivity = mStackSupervisor.isFocusedStack(this) && (isInStackLocked(starting) == null); - boolean behindTranslucentActivity = false; for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { final TaskRecord task = mTaskHistory.get(taskNdx); final ArrayList<ActivityRecord> activities = task.mActivities; @@ -1848,11 +1757,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final boolean reallyVisible = checkKeyguardVisibility(r, visibleIgnoringKeyguard, isTop); if (visibleIgnoringKeyguard) { - behindFullscreenActivity = updateBehindFullscreen(stackInvisible, + behindFullscreenActivity = updateBehindFullscreen(!stackShouldBeVisible, behindFullscreenActivity, task, r); - if (behindFullscreenActivity && !r.fullscreen) { - behindTranslucentActivity = true; - } } if (reallyVisible) { if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make visible? " + r @@ -1888,10 +1794,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai configChanges |= r.configChangeFlags; } else { if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make invisible? " + r - + " finishing=" + r.finishing + " state=" + r.state + " stackInvisible=" - + stackInvisible + " behindFullscreenActivity=" - + behindFullscreenActivity + " mLaunchTaskBehind=" - + r.mLaunchTaskBehind); + + " finishing=" + r.finishing + " state=" + r.state + + " stackShouldBeVisible=" + stackShouldBeVisible + + " behindFullscreenActivity=" + behindFullscreenActivity + + " mLaunchTaskBehind=" + r.mLaunchTaskBehind); makeInvisible(r); } } @@ -1899,10 +1805,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // The visibility of tasks and the activities they contain in freeform stack are // determined individually unlike other stacks where the visibility or fullscreen // status of an activity in a previous task affects other. - behindFullscreenActivity = stackVisibility == STACK_INVISIBLE; - } else if (mStackId == HOME_STACK_ID) { + behindFullscreenActivity = !stackShouldBeVisible; + } else if (isActivityTypeHome()) { if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + task - + " stackInvisible=" + stackInvisible + + " stackShouldBeVisible=" + stackShouldBeVisible + " behindFullscreenActivity=" + behindFullscreenActivity); // No other task in the home stack should be visible behind the home activity. // Home activities is usually a translucent activity with the wallpaper behind @@ -1962,7 +1868,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible, boolean isTop) { final boolean isInPinnedStack = r.getStack().getStackId() == PINNED_STACK_ID; - final boolean keyguardShowing = mStackSupervisor.mKeyguardController.isKeyguardShowing(); + final boolean keyguardShowing = mStackSupervisor.mKeyguardController.isKeyguardShowing( + mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY); final boolean keyguardLocked = mStackSupervisor.mKeyguardController.isKeyguardLocked(); final boolean showWhenLocked = r.canShowWhenLocked() && !isInPinnedStack; final boolean dismissKeyguard = r.hasDismissKeyguardWindows(); @@ -2002,7 +1909,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai * {@link Display#FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD} applied. */ private boolean canShowWithInsecureKeyguard() { - final ActivityStackSupervisor.ActivityDisplay activityDisplay = getDisplay(); + final ActivityDisplay activityDisplay = getDisplay(); if (activityDisplay == null) { throw new IllegalStateException("Stack is not attached to any display, stackId=" + mStackId); @@ -2182,7 +2089,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // activities as we need to display their starting window until they are done initializing. boolean behindFullscreenActivity = false; - if (shouldBeVisible(null) == STACK_INVISIBLE) { + if (!shouldBeVisible(null)) { // The stack is not visible, so no activity in it should be displaying a starting // window. Mark all activities below top and behind fullscreen. aboveTop = false; @@ -2258,9 +2165,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mResumedActivity = r; r.state = ActivityState.RESUMED; mService.setResumedActivityUncheckLocked(r, reason); - final TaskRecord task = r.getTask(); - task.touchActiveTime(); - mRecentTasks.addLocked(task); + mStackSupervisor.addRecentActivity(r); } private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { @@ -2543,128 +2448,139 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai || (lastStack.mLastPausedActivity != null && !lastStack.mLastPausedActivity.fullscreen)); - // This activity is now becoming visible. - if (!next.visible || next.stopped || lastActivityTranslucent) { - next.setVisibility(true); - } + // The contained logic must be synchronized, since we are both changing the visibility + // and updating the {@link Configuration}. {@link ActivityRecord#setVisibility} will + // ultimately cause the client code to schedule a layout. Since layouts retrieve the + // current {@link Configuration}, we must ensure that the below code updates it before + // the layout can occur. + synchronized(mWindowManager.getWindowManagerLock()) { + // This activity is now becoming visible. + if (!next.visible || next.stopped || lastActivityTranslucent) { + next.setVisibility(true); + } - // schedule launch ticks to collect information about slow apps. - next.startLaunchTickingLocked(); + // schedule launch ticks to collect information about slow apps. + next.startLaunchTickingLocked(); - ActivityRecord lastResumedActivity = - lastStack == null ? null :lastStack.mResumedActivity; - ActivityState lastState = next.state; + ActivityRecord lastResumedActivity = + lastStack == null ? null :lastStack.mResumedActivity; + ActivityState lastState = next.state; - mService.updateCpuStats(); + mService.updateCpuStats(); - if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + next + " (in existing)"); + if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to RESUMED: " + next + + " (in existing)"); - setResumedActivityLocked(next, "resumeTopActivityInnerLocked"); + setResumedActivityLocked(next, "resumeTopActivityInnerLocked"); - mService.updateLruProcessLocked(next.app, true, null); - updateLRUListLocked(next); - mService.updateOomAdjLocked(); + mService.updateLruProcessLocked(next.app, true, null); + updateLRUListLocked(next); + mService.updateOomAdjLocked(); - // Have the window manager re-evaluate the orientation of - // the screen based on the new activity order. - boolean notUpdated = true; - if (mStackSupervisor.isFocusedStack(this)) { - - // We have special rotation behavior when Keyguard is locked. Make sure all activity - // visibilities are set correctly as well as the transition is updated if needed to - // get the correct rotation behavior. - // TODO: Remove this once visibilities are set correctly immediately when starting - // an activity. - if (mStackSupervisor.mKeyguardController.isKeyguardLocked()) { - mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */, - 0 /* configChanges */, false /* preserveWindows */); - } - final Configuration config = mWindowManager.updateOrientationFromAppTokens( - mStackSupervisor.getDisplayOverrideConfiguration(mDisplayId), - next.mayFreezeScreenLocked(next.app) ? next.appToken : null, mDisplayId); - if (config != null) { - next.frozenBeforeDestroy = true; - } - notUpdated = !mService.updateDisplayOverrideConfigurationLocked(config, next, - false /* deferResume */, mDisplayId); - } - - if (notUpdated) { - // The configuration update wasn't able to keep the existing - // instance of the activity, and instead started a new one. - // We should be all done, but let's just make sure our activity - // is still at the top and schedule another run if something - // weird happened. - ActivityRecord nextNext = topRunningActivityLocked(); - if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_STATES, - "Activity config changed during resume: " + next - + ", new next: " + nextNext); - if (nextNext != next) { - // Do over! - mStackSupervisor.scheduleResumeTopActivities(); - } - if (!next.visible || next.stopped) { - next.setVisibility(true); - } - next.completeResumeLocked(); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); - return true; - } + // Have the window manager re-evaluate the orientation of + // the screen based on the new activity order. + boolean notUpdated = true; - try { - // Deliver all pending results. - ArrayList<ResultInfo> a = next.results; - if (a != null) { - final int N = a.size(); - if (!next.finishing && N > 0) { - if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, - "Delivering results to " + next + ": " + a); - next.app.thread.scheduleSendResult(next.appToken, a); + if (mStackSupervisor.isFocusedStack(this)) { + + // We have special rotation behavior when Keyguard is locked. Make sure all + // activity visibilities are set correctly as well as the transition is updated + // if needed to get the correct rotation behavior. + // TODO: Remove this once visibilities are set correctly immediately when + // starting an activity. + if (mStackSupervisor.mKeyguardController.isKeyguardLocked()) { + mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */, + 0 /* configChanges */, false /* preserveWindows */); } + final Configuration config = mWindowManager.updateOrientationFromAppTokens( + mStackSupervisor.getDisplayOverrideConfiguration(mDisplayId), + next.mayFreezeScreenLocked(next.app) ? next.appToken : null, + mDisplayId); + if (config != null) { + next.frozenBeforeDestroy = true; + } + notUpdated = !mService.updateDisplayOverrideConfigurationLocked(config, next, + false /* deferResume */, mDisplayId); } - if (next.newIntents != null) { - next.app.thread.scheduleNewIntent( - next.newIntents, next.appToken, false /* andPause */); + if (notUpdated) { + // The configuration update wasn't able to keep the existing + // instance of the activity, and instead started a new one. + // We should be all done, but let's just make sure our activity + // is still at the top and schedule another run if something + // weird happened. + ActivityRecord nextNext = topRunningActivityLocked(); + if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_STATES, + "Activity config changed during resume: " + next + + ", new next: " + nextNext); + if (nextNext != next) { + // Do over! + mStackSupervisor.scheduleResumeTopActivities(); + } + if (!next.visible || next.stopped) { + next.setVisibility(true); + } + next.completeResumeLocked(); + if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); + return true; } - // Well the app will no longer be stopped. - // Clear app token stopped state in window manager if needed. - next.notifyAppResumed(next.stopped); - - EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId, - System.identityHashCode(next), next.getTask().taskId, - next.shortComponentName); + try { + // Deliver all pending results. + ArrayList<ResultInfo> a = next.results; + if (a != null) { + final int N = a.size(); + if (!next.finishing && N > 0) { + if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, + "Delivering results to " + next + ": " + a); + next.app.thread.scheduleSendResult(next.appToken, a); + } + } - next.sleeping = false; - mService.showUnsupportedZoomDialogIfNeededLocked(next); - mService.showAskCompatModeDialogLocked(next); - next.app.pendingUiClean = true; - next.app.forceProcessStateUpTo(mService.mTopProcessState); - next.clearOptionsLocked(); - next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState, - mService.isNextTransitionForward(), resumeAnimOptions); + if (next.newIntents != null) { + next.app.thread.scheduleNewIntent( + next.newIntents, next.appToken, false /* andPause */); + } - if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed " + next); - } catch (Exception e) { - // Whoops, need to restart this activity! - if (DEBUG_STATES) Slog.v(TAG_STATES, "Resume failed; resetting state to " - + lastState + ": " + next); - next.state = lastState; - if (lastStack != null) { - lastStack.mResumedActivity = lastResumedActivity; - } - Slog.i(TAG, "Restarting because process died: " + next); - if (!next.hasBeenLaunched) { - next.hasBeenLaunched = true; - } else if (SHOW_APP_STARTING_PREVIEW && lastStack != null && - mStackSupervisor.isFrontStackOnDisplay(lastStack)) { - next.showStartingWindow(null /* prev */, false /* newTask */, - false /* taskSwitch */); + // Well the app will no longer be stopped. + // Clear app token stopped state in window manager if needed. + next.notifyAppResumed(next.stopped); + + EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.userId, + System.identityHashCode(next), next.getTask().taskId, + next.shortComponentName); + + next.sleeping = false; + mService.showUnsupportedZoomDialogIfNeededLocked(next); + mService.showAskCompatModeDialogLocked(next); + next.app.pendingUiClean = true; + next.app.forceProcessStateUpTo(mService.mTopProcessState); + next.clearOptionsLocked(); + next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState, + mService.isNextTransitionForward(), resumeAnimOptions); + + if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed " + + next); + } catch (Exception e) { + // Whoops, need to restart this activity! + if (DEBUG_STATES) Slog.v(TAG_STATES, "Resume failed; resetting state to " + + lastState + ": " + next); + next.state = lastState; + if (lastStack != null) { + lastStack.mResumedActivity = lastResumedActivity; + } + Slog.i(TAG, "Restarting because process died: " + next); + if (!next.hasBeenLaunched) { + next.hasBeenLaunched = true; + } else if (SHOW_APP_STARTING_PREVIEW && lastStack != null && + mStackSupervisor.isFrontStackOnDisplay(lastStack)) { + next.showStartingWindow(null /* prev */, false /* newTask */, + false /* taskSwitch */); + } + mStackSupervisor.startSpecificActivityLocked(next, true, false); + if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); + return true; } - mStackSupervisor.startSpecificActivityLocked(next, true, false); - if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); - return true; } // From this point on, if something goes wrong there is no way @@ -2989,9 +2905,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Ensure that we do not trigger entering PiP an activity on the pinned stack return false; } - final int targetStackId = toFrontTask != null ? toFrontTask.getStackId() - : toFrontActivity.getStackId(); - if (targetStackId == ASSISTANT_STACK_ID) { + final ActivityStack targetStack = toFrontTask != null + ? toFrontTask.getStack() : toFrontActivity.getStack(); + if (targetStack != null && targetStack.isActivityTypeAssistant()) { // Ensure the task/activity being brought forward is not the assistant return false; } @@ -4548,7 +4464,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // Don't refocus if invisible to current user final ActivityRecord top = tr.getTopActivity(); if (top == null || !top.okToShowLocked()) { - addRecentActivityLocked(top); + mStackSupervisor.addRecentActivity(top); ActivityOptions.abort(options); return; } @@ -4601,7 +4517,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai Slog.i(TAG, "moveTaskToBack: " + tr); // If the task is locked, then show the lock task toast - if (!mService.mLockTaskController.checkLockedTask(tr)) { + if (mService.mLockTaskController.checkLockedTask(tr)) { return false; } @@ -4982,8 +4898,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } ci.numActivities = numActivities; ci.numRunning = numRunning; - ci.supportsSplitScreenMultiWindow = task.supportsSplitScreen(); + ci.supportsSplitScreenMultiWindow = task.supportsSplitScreenWindowingMode(); ci.resizeMode = task.mResizeMode; + ci.configuration.setTo(task.getConfiguration()); list.add(ci); } } @@ -5152,8 +5069,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (task.autoRemoveFromRecents() || isVoiceSession) { // Task creator asked to remove this when done, or this task was a voice // interaction, so it should not remain on the recent tasks list. - mRecentTasks.remove(task); - task.removedFromRecents(); + mStackSupervisor.removeTaskFromRecents(task); } task.removeWindowContainer(); @@ -5170,11 +5086,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mStackSupervisor.moveHomeStackToFront(myReason); } } - if (mStacks != null) { - mStacks.remove(this); - mStacks.add(0, this); + if (isAttached()) { + getDisplay().positionChildAtBottom(this); } - if (!isHomeOrRecentsStack()) { + if (!isActivityTypeHome()) { remove(); } } @@ -5194,8 +5109,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai voiceInteractor); // add the task to stack first, mTaskPositioner might need the stack association addTask(task, toTop, "createTaskRecord"); - final boolean isLockscreenShown = - mService.mStackSupervisor.mKeyguardController.isKeyguardShowing(); + final boolean isLockscreenShown = mService.mStackSupervisor.mKeyguardController + .isKeyguardShowing(mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY); if (!layoutTaskInStack(task, info.windowLayout) && mBounds != null && task.isResizeable() && !isLockscreenShown) { task.updateOverrideConfiguration(mBounds); @@ -5349,11 +5264,30 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } boolean shouldSleepActivities() { - final ActivityStackSupervisor.ActivityDisplay display = getDisplay(); + final ActivityDisplay display = getDisplay(); return display != null ? display.isSleeping() : mService.isSleepingLocked(); } boolean shouldSleepOrShutDownActivities() { return shouldSleepActivities() || mService.isShuttingDownLocked(); } + + public void writeToProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + super.writeToProto(proto, CONFIGURATION_CONTAINER); + proto.write(ID, mStackId); + for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { + final TaskRecord task = mTaskHistory.get(taskNdx); + task.writeToProto(proto, TASKS); + } + if (mResumedActivity != null) { + mResumedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY); + } + proto.write(DISPLAY_ID, mDisplayId); + if (mBounds != null) { + mBounds.writeToProto(proto, BOUNDS); + } + proto.write(FULLSCREEN, mFullscreen); + proto.end(token); + } } diff --git a/com/android/server/am/ActivityStackSupervisor.java b/com/android/server/am/ActivityStackSupervisor.java index fe28956d..da2827a6 100644 --- a/com/android/server/am/ActivityStackSupervisor.java +++ b/com/android/server/am/ActivityStackSupervisor.java @@ -23,30 +23,29 @@ import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID; -import static android.app.ActivityManager.StackId.FIRST_STATIC_STACK_ID; -import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; -import static android.app.ActivityManager.StackId.LAST_STATIC_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; -import static android.app.ActivityManager.StackId.RECENTS_STACK_ID; import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY; import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.PowerManager.PARTIAL_WAKE_LOCK; import static android.os.Process.SYSTEM_UID; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.Display.FLAG_PRIVATE; import static android.view.Display.INVALID_DISPLAY; -import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT; import static android.view.Display.TYPE_VIRTUAL; import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; @@ -81,19 +80,24 @@ import static com.android.server.am.ActivityStack.ActivityState.RESUMED; import static com.android.server.am.ActivityStack.ActivityState.STOPPED; import static com.android.server.am.ActivityStack.ActivityState.STOPPING; import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING; -import static com.android.server.am.ActivityStack.STACK_INVISIBLE; -import static com.android.server.am.ActivityStack.STACK_VISIBLE; +import static com.android.server.am.TaskRecord.INVALID_TASK_ID; import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE; import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE; import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT; +import static com.android.server.am.proto.ActivityStackSupervisorProto.DISPLAYS; +import static com.android.server.am.proto.ActivityStackSupervisorProto.FOCUSED_STACK_ID; +import static com.android.server.am.proto.ActivityStackSupervisorProto.KEYGUARD_CONTROLLER; +import static com.android.server.am.proto.ActivityStackSupervisorProto.RESUMED_ACTIVITY; +import static com.android.server.am.proto.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER; import static com.android.server.wm.AppTransition.TRANSIT_DOCK_TASK_FROM_RECENTS; import static java.lang.Integer.MAX_VALUE; import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; @@ -106,6 +110,7 @@ import android.app.AppOpsManager; import android.app.ProfilerInfo; import android.app.ResultInfo; import android.app.WaitResult; +import android.app.WindowConfiguration; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -147,6 +152,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; import android.view.Display; import com.android.internal.annotations.VisibleForTesting; @@ -166,7 +172,6 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Set; @@ -375,15 +380,11 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D * They are used by components that may hide and block interaction with underlying * activities. */ - final ArrayList<SleepToken> mSleepTokens = new ArrayList<SleepToken>(); + final ArrayList<SleepToken> mSleepTokens = new ArrayList<>(); /** Stack id of the front stack when user switched, indexed by userId. */ SparseIntArray mUserStackInFront = new SparseIntArray(2); - // TODO: Add listener for removal of references. - /** Mapping from (ActivityStack/TaskStack).mStackId to their current state */ - SparseArray<ActivityStack> mStacks = new SparseArray<>(); - // TODO: There should be an ActivityDisplayController coordinating am/wm interaction. /** Mapping from displayId to display current state */ private final SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<>(); @@ -602,16 +603,13 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D Display[] displays = mDisplayManager.getDisplays(); for (int displayNdx = displays.length - 1; displayNdx >= 0; --displayNdx) { final int displayId = displays[displayNdx].getDisplayId(); - ActivityDisplay activityDisplay = new ActivityDisplay(displayId); - if (activityDisplay.mDisplay == null) { - throw new IllegalStateException("Default Display does not exist"); - } + ActivityDisplay activityDisplay = new ActivityDisplay(this, displayId); mActivityDisplays.put(displayId, activityDisplay); calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay); } - mHomeStack = mFocusedStack = mLastFocusedStack = - getStack(HOME_STACK_ID, CREATE_IF_NEEDED, ON_TOP); + mHomeStack = mFocusedStack = mLastFocusedStack = getDefaultDisplay().getOrCreateStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); } @@ -667,7 +665,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } void moveRecentsStackToFront(String reason) { - final ActivityStack recentsStack = getStack(RECENTS_STACK_ID); + final ActivityStack recentsStack = getDefaultDisplay().getStack( + WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS); if (recentsStack != null) { recentsStack.moveToFront(reason); } @@ -708,24 +707,26 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } TaskRecord anyTaskForIdLocked(int id) { - return anyTaskForIdLocked(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, - INVALID_STACK_ID); + return anyTaskForIdLocked(id, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE); + } + + TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode) { + return anyTaskForIdLocked(id, matchMode, null); } /** * Returns a {@link TaskRecord} for the input id if available. {@code null} otherwise. * @param id Id of the task we would like returned. * @param matchMode The mode to match the given task id in. - * @param stackId The stack to restore the task to (default launch stack will be used if - * stackId is {@link android.app.ActivityManager.StackId#INVALID_STACK_ID}). Only - * valid if the matchMode is - * {@link #MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE}. + * @param aOptions The activity options to use for restoration. Can be null. */ - TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode, int stackId) { + TaskRecord anyTaskForIdLocked(int id, @AnyTaskForIdMatchTaskMode int matchMode, + @Nullable ActivityOptions aOptions) { // If there is a stack id set, ensure that we are attempting to actually restore a task - if (matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE && - stackId != INVALID_STACK_ID) { - throw new IllegalArgumentException("Should not specify stackId for non-restore lookup"); + // TODO: Don't really know if this is needed... + if (matchMode != MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE && aOptions != null) { + throw new IllegalArgumentException("Should not specify activity options for non-restore" + + " lookup"); } int numDisplays = mActivityDisplays.size(); @@ -763,7 +764,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } // Implicitly, this case is MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE - if (!restoreRecentTaskLocked(task, stackId)) { + if (!restoreRecentTaskLocked(task, aOptions)) { if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, "Couldn't restore task id=" + id + " found in recents"); return null; @@ -858,8 +859,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // was 10, user 0 could only have taskIds 0 to 9, user 1: 10 to 19, user 2: 20 to 29, so on. int candidateTaskId = nextTaskIdForUser(currentTaskId, userId); while (mRecentTasks.taskIdTakenForUserLocked(candidateTaskId, userId) - || anyTaskForIdLocked(candidateTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, - INVALID_STACK_ID) != null) { + || anyTaskForIdLocked( + candidateTaskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) { candidateTaskId = nextTaskIdForUser(candidateTaskId, userId); if (candidateTaskId == currentTaskId) { // Something wrong! @@ -1152,8 +1153,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D void getTasksLocked(int maxNum, List<RunningTaskInfo> list, int callingUid, boolean allowed) { // Gather all of the running tasks for each stack into runningTaskLists. - ArrayList<ArrayList<RunningTaskInfo>> runningTaskLists = - new ArrayList<ArrayList<RunningTaskInfo>>(); + ArrayList<ArrayList<RunningTaskInfo>> runningTaskLists = new ArrayList<>(); final int numDisplays = mActivityDisplays.size(); for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks; @@ -1235,7 +1235,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D synchronized (mService) { return mService.getPackageManagerInternalLocked().resolveIntent(intent, resolvedType, PackageManager.MATCH_INSTANT | PackageManager.MATCH_DEFAULT_ONLY | flags - | ActivityManagerService.STOCK_PM_FLAGS, userId); + | ActivityManagerService.STOCK_PM_FLAGS, userId, true); } } @@ -1639,6 +1639,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return true; } + // Check if caller is already present on display + final boolean uidPresentOnDisplay = activityDisplay.isUidPresent(callingUid); + final int displayOwnerUid = activityDisplay.mDisplay.getOwnerUid(); if (activityDisplay.mDisplay.getType() == TYPE_VIRTUAL && displayOwnerUid != SYSTEM_UID && displayOwnerUid != aInfo.applicationInfo.uid) { @@ -1651,7 +1654,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } // Check if the caller is allowed to embed activities from other apps. if (mService.checkPermission(ACTIVITY_EMBEDDING, callingPid, callingUid) - == PERMISSION_DENIED) { + == PERMISSION_DENIED && !uidPresentOnDisplay) { if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:" + " disallow activity embedding without permission."); return false; @@ -1672,8 +1675,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return true; } - // Check if caller is present on display - if (activityDisplay.isUidPresent(callingUid)) { + if (uidPresentOnDisplay) { if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:" + " allow launch for caller present on the display"); return true; @@ -1953,7 +1955,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D */ void updateUserStackLocked(int userId, ActivityStack stack) { if (userId != mCurrentUser) { - mUserStackInFront.put(userId, stack != null ? stack.getStackId() : HOME_STACK_ID); + mUserStackInFront.put(userId, stack != null ? stack.getStackId() : mHomeStack.mStackId); } } @@ -2083,38 +2085,35 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // we'll just indicate that this task returns to the home task. task.setTaskToReturnTo(ACTIVITY_TYPE_HOME); } - ActivityStack currentStack = task.getStack(); + final ActivityStack currentStack = task.getStack(); if (currentStack == null) { Slog.e(TAG, "findTaskToMoveToFrontLocked: can't move task=" + task + " to front. Stack is null"); return; } - if (task.isResizeable() && options != null) { - int stackId = options.getLaunchStackId(); - if (canUseActivityOptionsLaunchBounds(options, stackId)) { - final Rect bounds = TaskRecord.validateBounds(options.getLaunchBounds()); - task.updateOverrideConfiguration(bounds); - if (stackId == INVALID_STACK_ID) { - stackId = task.getLaunchStackId(); - } - if (stackId != currentStack.mStackId) { - task.reparent(stackId, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE, - DEFER_RESUME, "findTaskToMoveToFrontLocked"); - stackId = currentStack.mStackId; - // moveTaskToStackUncheckedLocked() should already placed the task on top, - // still need moveTaskToFrontLocked() below for any transition settings. - } - if (StackId.resizeStackWithLaunchBounds(stackId)) { - resizeStackLocked(stackId, bounds, - null /* tempTaskBounds */, null /* tempTaskInsetBounds */, - !PRESERVE_WINDOWS, true /* allowResizeInDockedMode */, !DEFER_RESUME); - } else { - // WM resizeTask must be done after the task is moved to the correct stack, - // because Task's setBounds() also updates dim layer's bounds, but that has - // dependency on the stack. - task.resizeWindowContainer(); - } + if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) { + final Rect bounds = TaskRecord.validateBounds(options.getLaunchBounds()); + task.updateOverrideConfiguration(bounds); + + ActivityStack stack = getLaunchStack(null, options, task, ON_TOP); + + if (stack != currentStack) { + task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE, DEFER_RESUME, + "findTaskToMoveToFrontLocked"); + stack = currentStack; + // moveTaskToStackUncheckedLocked() should already placed the task on top, + // still need moveTaskToFrontLocked() below for any transition settings. + } + if (StackId.resizeStackWithLaunchBounds(stack.mStackId)) { + resizeStackLocked(stack.mStackId, bounds, null /* tempTaskBounds */, + null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS, + true /* allowResizeInDockedMode */, !DEFER_RESUME); + } else { + // WM resizeTask must be done after the task is moved to the correct stack, + // because Task's setBounds() also updates dim layer's bounds, but that has + // dependency on the stack. + task.resizeWindowContainer(); } } @@ -2125,39 +2124,246 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D if (DEBUG_STACK) Slog.d(TAG_STACK, "findTaskToMoveToFront: moved to front of stack=" + currentStack); - handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, DEFAULT_DISPLAY, + handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY, currentStack.mStackId, forceNonResizeable); } - boolean canUseActivityOptionsLaunchBounds(ActivityOptions options, int launchStackId) { + boolean canUseActivityOptionsLaunchBounds(ActivityOptions options) { // We use the launch bounds in the activity options is the device supports freeform // window management or is launching into the pinned stack. - if (options.getLaunchBounds() == null) { + if (options == null || options.getLaunchBounds() == null) { return false; } - return (mService.mSupportsPictureInPicture && launchStackId == PINNED_STACK_ID) + return (mService.mSupportsPictureInPicture + && options.getLaunchWindowingMode() == WINDOWING_MODE_PINNED) || mService.mSupportsFreeformWindowManagement; } protected <T extends ActivityStack> T getStack(int stackId) { - return getStack(stackId, !CREATE_IF_NEEDED, !ON_TOP); + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final T stack = mActivityDisplays.valueAt(i).getStack(stackId); + if (stack != null) { + return stack; + } + } + return null; + } + + /** @see ActivityDisplay#getStack(int, int) */ + private <T extends ActivityStack> T getStack(int windowingMode, int activityType) { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final T stack = mActivityDisplays.valueAt(i).getStack(windowingMode, activityType); + if (stack != null) { + return stack; + } + } + return null; + } + + /** + * Returns true if the {@param windowingMode} is supported based on other parameters passed in. + * @param windowingMode The windowing mode we are checking support for. + * @param supportsMultiWindow If we should consider support for multi-window mode in general. + * @param supportsSplitScreen If we should consider support for split-screen multi-window. + * @param supportsFreeform If we should consider support for freeform multi-window. + * @param supportsPip If we should consider support for picture-in-picture mutli-window. + * @param activityType The activity type under consideration. + * @return true if the windowing mode is supported. + */ + boolean isWindowingModeSupported(int windowingMode, boolean supportsMultiWindow, + boolean supportsSplitScreen, boolean supportsFreeform, boolean supportsPip, + int activityType) { + + if (windowingMode == WINDOWING_MODE_UNDEFINED + || windowingMode == WINDOWING_MODE_FULLSCREEN) { + return true; + } + if (!supportsMultiWindow) { + return false; + } + + if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY + || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) { + return supportsSplitScreen && WindowConfiguration.supportSplitScreenWindowingMode( + windowingMode, activityType); + } + + if (!supportsFreeform && windowingMode == WINDOWING_MODE_FREEFORM) { + return false; + } + + if (!supportsPip && windowingMode == WINDOWING_MODE_PINNED) { + return false; + } + return true; + } + + private int resolveWindowingMode(@Nullable ActivityRecord r, @Nullable ActivityOptions options, + @Nullable TaskRecord task, int activityType) { + + // First preference if the windowing mode in the activity options if set. + int windowingMode = (options != null) + ? options.getLaunchWindowingMode() : WINDOWING_MODE_UNDEFINED; + + // If windowing mode is unset, then next preference is the candidate task, then the + // activity record. + if (windowingMode == WINDOWING_MODE_UNDEFINED) { + if (task != null) { + windowingMode = task.getWindowingMode(); + } + if (windowingMode == WINDOWING_MODE_UNDEFINED && r != null) { + windowingMode = r.getWindowingMode(); + } + } + + // Make sure the windowing mode we are trying to use makes sense for what is supported. + boolean supportsMultiWindow = mService.mSupportsMultiWindow; + boolean supportsSplitScreen = mService.mSupportsSplitScreenMultiWindow; + boolean supportsFreeform = mService.mSupportsFreeformWindowManagement; + boolean supportsPip = mService.mSupportsPictureInPicture; + if (supportsMultiWindow) { + if (task != null) { + supportsMultiWindow = task.isResizeable(); + supportsSplitScreen = task.supportsSplitScreenWindowingMode(); + // TODO: Do we need to check for freeform and Pip support here? + } else if (r != null) { + supportsMultiWindow = r.isResizeable(); + supportsSplitScreen = r.supportsSplitScreenWindowingMode(); + supportsFreeform = r.supportsFreeform(); + supportsPip = r.supportsPictureInPicture(); + } + } + + if (isWindowingModeSupported(windowingMode, supportsMultiWindow, supportsSplitScreen, + supportsFreeform, supportsPip, activityType)) { + return windowingMode; + } + return WINDOWING_MODE_FULLSCREEN; + } + + int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options, + @Nullable TaskRecord task) { + // Preference is given to the activity type for the activity then the task since the type + // once set shouldn't change. + int activityType = r != null ? r.getActivityType() : ACTIVITY_TYPE_UNDEFINED; + if (activityType == ACTIVITY_TYPE_UNDEFINED && task != null) { + activityType = task.getActivityType(); + } + if (activityType != ACTIVITY_TYPE_UNDEFINED) { + return activityType; + } + return options != null ? options.getLaunchActivityType() : ACTIVITY_TYPE_UNDEFINED; + } + + <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r, + @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop) { + return getLaunchStack(r, options, candidateTask, onTop, INVALID_DISPLAY); } - protected <T extends ActivityStack> T getStack(int stackId, boolean createStaticStackIfNeeded, - boolean createOnTop) { - final ActivityStack stack = mStacks.get(stackId); + /** + * Returns the right stack to use for launching factoring in all the input parameters. + * + * @param r The activity we are trying to launch. Can be null. + * @param options The activity options used to the launch. Can be null. + * @param candidateTask The possible task the activity might be launched in. Can be null. + * + * @return The stack to use for the launch or INVALID_STACK_ID. + */ + <T extends ActivityStack> T getLaunchStack(@Nullable ActivityRecord r, + @Nullable ActivityOptions options, @Nullable TaskRecord candidateTask, boolean onTop, + int candidateDisplayId) { + int taskId = INVALID_TASK_ID; + int displayId = INVALID_DISPLAY; + //Rect bounds = null; + + // We give preference to the launch preference in activity options. + if (options != null) { + taskId = options.getLaunchTaskId(); + displayId = options.getLaunchDisplayId(); + // TODO: Need to work this into the equation... + //bounds = options.getLaunchBounds(); + } + + // First preference for stack goes to the task Id set in the activity options. Use the stack + // associated with that if possible. + if (taskId != INVALID_TASK_ID) { + // Temporarily set the task id to invalid in case in re-entry. + options.setLaunchTaskId(INVALID_TASK_ID); + final TaskRecord task = anyTaskForIdLocked(taskId, + MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, options); + options.setLaunchTaskId(taskId); + if (task != null) { + return task.getStack(); + } + } + + final int activityType = resolveActivityType(r, options, candidateTask); + int windowingMode = resolveWindowingMode(r, options, candidateTask, activityType); + T stack = null; + + // Next preference for stack goes to the display Id set in the activity options or the + // candidate display. + if (displayId == INVALID_DISPLAY) { + displayId = candidateDisplayId; + } + if (displayId != INVALID_DISPLAY) { + if (r != null) { + // TODO: This should also take in the windowing mode and activity type into account. + stack = (T) getValidLaunchStackOnDisplay(displayId, r); + if (stack != null) { + return stack; + } + } + final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId); + if (display != null) { + for (int i = display.mStacks.size() - 1; i >= 0; --i) { + stack = (T) display.mStacks.get(i); + if (stack.isCompatible(windowingMode, activityType)) { + return stack; + } + } + // TODO: We should create the stack we want on the display at this point. + } + } + + // Give preference to the stack and display of the input task and activity if they match the + // mode we want to launch into. + stack = null; + ActivityDisplay display = null; + if (candidateTask != null) { + stack = candidateTask.getStack(); + } + if (stack == null && r != null) { + stack = r.getStack(); + } if (stack != null) { - return (T) stack; + if (stack.isCompatible(windowingMode, activityType)) { + return stack; + } + display = stack.getDisplay(); } - if (!createStaticStackIfNeeded || !StackId.isStaticStack(stackId)) { - return null; + + if (display == null + // TODO: Can be removed once we figure-out how non-standard types should launch + // outside the default display. + || (activityType != ACTIVITY_TYPE_STANDARD + && activityType != ACTIVITY_TYPE_UNDEFINED)) { + display = getDefaultDisplay(); } - if (stackId == DOCKED_STACK_ID) { - // Make sure recents stack exist when creating a dock stack as it normally need to be on - // the other side of the docked stack and we make visibility decisions based on that. - getStack(RECENTS_STACK_ID, CREATE_IF_NEEDED, createOnTop); + + stack = display.getOrCreateStack(windowingMode, activityType, onTop); + if (stack != null) { + return stack; + } + + // Whatever...return some default for now. + if (candidateTask != null && candidateTask.mBounds != null + && mService.mSupportsFreeformWindowManagement) { + windowingMode = WINDOWING_MODE_FREEFORM; + } else { + windowingMode = WINDOWING_MODE_FULLSCREEN; } - return (T) createStackOnDisplay(stackId, DEFAULT_DISPLAY, createOnTop); + return display.getOrCreateStack(windowingMode, activityType, onTop); } /** @@ -2174,26 +2380,50 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D "Display with displayId=" + displayId + " not found."); } + if (!r.canBeLaunchedOnDisplay(displayId)) { + return null; + } + // Return the topmost valid stack on the display. for (int i = activityDisplay.mStacks.size() - 1; i >= 0; --i) { final ActivityStack stack = activityDisplay.mStacks.get(i); - if (mService.mActivityStarter.isValidLaunchStackId(stack.mStackId, displayId, r)) { + if (isValidLaunchStack(stack, displayId, r)) { return stack; } } // If there is no valid stack on the external display - check if new dynamic stack will do. - if (displayId != Display.DEFAULT_DISPLAY) { - final int newDynamicStackId = getNextStackId(); - if (mService.mActivityStarter.isValidLaunchStackId(newDynamicStackId, displayId, r)) { - return createStackOnDisplay(newDynamicStackId, displayId, true /*onTop*/); - } + if (displayId != DEFAULT_DISPLAY) { + return activityDisplay.createStack( + r.getWindowingMode(), r.getActivityType(), true /*onTop*/); } Slog.w(TAG, "getValidLaunchStackOnDisplay: can't launch on displayId " + displayId); return null; } + // TODO: Can probably be consolidated into getLaunchStack()... + private boolean isValidLaunchStack(ActivityStack stack, int displayId, ActivityRecord r) { + switch (stack.getActivityType()) { + case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome(); + case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents(); + case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant(); + } + switch (stack.getWindowingMode()) { + case WINDOWING_MODE_FULLSCREEN: return true; + case WINDOWING_MODE_FREEFORM: return r.supportsFreeform(); + case WINDOWING_MODE_PINNED: return r.supportsPictureInPicture(); + case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: return r.supportsSplitScreenWindowingMode(); + case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return r.supportsSplitScreenWindowingMode(); + } + + if (StackId.isDynamicStack(stack.mStackId)) { + return r.canBeLaunchedOnDisplay(displayId); + } + Slog.e(TAG, "isValidLaunchStack: Unexpected stack=" + stack); + return false; + } + ArrayList<ActivityStack> getStacks() { ArrayList<ActivityStack> allStacks = new ArrayList<>(); for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { @@ -2225,7 +2455,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D for (int j = stacks.size() - 1; j >= 0; --j) { final ActivityStack stack = stacks.get(j); if (stack != currentFocus && stack.isFocusable() - && stack.shouldBeVisible(null) != STACK_INVISIBLE) { + && stack.shouldBeVisible(null)) { return stack; } } @@ -2294,7 +2524,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return; } - final boolean splitScreenActive = getStack(DOCKED_STACK_ID) != null; + final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenStack(); if (!allowResizeInDockedMode && !stack.getWindowConfiguration().tasksAreFloating() && splitScreenActive) { // If the docked stack exists, don't resize non-floating stacks independently of the @@ -2305,7 +2535,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId); mWindowManager.deferSurfaceLayout(); try { - if (stack.supportSplitScreenWindowingMode()) { + if (stack.supportsSplitScreenWindowingMode()) { if (bounds == null && stack.inSplitScreenWindowingMode()) { // null bounds = fullscreen windowing mode...at least for now. stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); @@ -2326,26 +2556,25 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - private void deferUpdateBounds(int stackId) { - final ActivityStack stack = getStack(stackId); + private void deferUpdateBounds(int activityType) { + final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType); if (stack != null) { stack.deferUpdateBounds(); } } - private void continueUpdateBounds(int stackId) { - final ActivityStack stack = getStack(stackId); + private void continueUpdateBounds(int activityType) { + final ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType); if (stack != null) { stack.continueUpdateBounds(); } } void notifyAppTransitionDone() { - continueUpdateBounds(RECENTS_STACK_ID); + continueUpdateBounds(ACTIVITY_TYPE_RECENTS); for (int i = mResizingTasksDuringAnimation.size() - 1; i >= 0; i--) { final int taskId = mResizingTasksDuringAnimation.valueAt(i); - final TaskRecord task = - anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_ONLY, INVALID_STACK_ID); + final TaskRecord task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_ONLY); if (task != null) { task.setTaskDockedResizing(false); } @@ -2353,29 +2582,31 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mResizingTasksDuringAnimation.clear(); } - private void moveTasksToFullscreenStackInSurfaceTransaction(int fromStackId, - boolean onTop) { - - final ActivityStack stack = getStack(fromStackId); - if (stack == null) { - return; - } + /** + * TODO: This should just change the windowing mode and resize vs. actually moving task around. + * Can do that once we are no longer using static stack ids. Specially when + * {@link ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} is removed. + */ + private void moveTasksToFullscreenStackInSurfaceTransaction(ActivityStack fromStack, + int toDisplayId, boolean onTop) { mWindowManager.deferSurfaceLayout(); try { - if (fromStackId == DOCKED_STACK_ID) { + final int windowingMode = fromStack.getWindowingMode(); + final boolean inPinnedWindowingMode = windowingMode == WINDOWING_MODE_PINNED; + final ActivityDisplay toDisplay = getActivityDisplay(toDisplayId); + + if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { // We are moving all tasks from the docked stack to the fullscreen stack, // which is dismissing the docked stack, so resize all other stacks to // fullscreen here already so we don't end up with resize trashing. - for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) { - final ActivityStack otherStack = getStack(i); - if (otherStack == null) { - continue; - } + final ArrayList<ActivityStack> displayStacks = toDisplay.mStacks; + for (int i = displayStacks.size() - 1; i >= 0; --i) { + final ActivityStack otherStack = displayStacks.get(i); if (!otherStack.inSplitScreenSecondaryWindowingMode()) { continue; } - resizeStackLocked(i, null, null, null, PRESERVE_WINDOWS, + resizeStackLocked(otherStack.mStackId, null, null, null, PRESERVE_WINDOWS, true /* allowResizeInDockedMode */, DEFER_RESUME); } @@ -2384,48 +2615,51 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // resize when we remove task from it below and it is detached from the // display because it no longer contains any tasks. mAllowDockedStackResize = false; - } else if (fromStackId == PINNED_STACK_ID) { - if (onTop) { - // Log if we are expanding the PiP to fullscreen - MetricsLogger.action(mService.mContext, - ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN); - } + } else if (inPinnedWindowingMode && onTop) { + // Log if we are expanding the PiP to fullscreen + MetricsLogger.action(mService.mContext, + ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN); } - ActivityStack fullscreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID); - final boolean isFullscreenStackVisible = fullscreenStack != null && - fullscreenStack.shouldBeVisible(null) == STACK_VISIBLE; + // If we are moving from the pinned stack, then the animation takes care of updating // the picture-in-picture mode. - final boolean schedulePictureInPictureModeChange = (fromStackId == PINNED_STACK_ID); - final ArrayList<TaskRecord> tasks = stack.getAllTasks(); - final int size = tasks.size(); - if (onTop) { - for (int i = 0; i < size; i++) { - final TaskRecord task = tasks.get(i); - final boolean isTopTask = i == (size - 1); - if (fromStackId == PINNED_STACK_ID) { - // Update the return-to to reflect where the pinned stack task was moved - // from so that we retain the stack that was previously visible if the - // pinned stack is recreated. See moveActivityToPinnedStackLocked(). - task.setTaskToReturnTo(isFullscreenStackVisible ? - ACTIVITY_TYPE_STANDARD : ACTIVITY_TYPE_HOME); + final boolean schedulePictureInPictureModeChange = inPinnedWindowingMode; + final ArrayList<TaskRecord> tasks = fromStack.getAllTasks(); + + if (!tasks.isEmpty()) { + final int size = tasks.size(); + final ActivityStack fullscreenStack = toDisplay.getOrCreateStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, onTop); + + if (onTop) { + final int returnToType = + toDisplay.getTopVisibleStackActivityType(WINDOWING_MODE_PINNED); + for (int i = 0; i < size; i++) { + final TaskRecord task = tasks.get(i); + final boolean isTopTask = i == (size - 1); + if (inPinnedWindowingMode) { + // Update the return-to to reflect where the pinned stack task was moved + // from so that we retain the stack that was previously visible if the + // pinned stack is recreated. See moveActivityToPinnedStackLocked(). + task.setTaskToReturnTo(returnToType); + } + // Defer resume until all the tasks have been moved to the fullscreen stack + task.reparent(fullscreenStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, + isTopTask /* animate */, DEFER_RESUME, + schedulePictureInPictureModeChange, + "moveTasksToFullscreenStack - onTop"); + } + } else { + for (int i = 0; i < size; i++) { + final TaskRecord task = tasks.get(i); + // Position the tasks in the fullscreen stack in order at the bottom of the + // stack. Also defer resume until all the tasks have been moved to the + // fullscreen stack. + task.reparent(fullscreenStack, i /* position */, + REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME, + schedulePictureInPictureModeChange, + "moveTasksToFullscreenStack - NOT_onTop"); } - // Defer resume until all the tasks have been moved to the fullscreen stack - task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, ON_TOP, - REPARENT_MOVE_STACK_TO_FRONT, isTopTask /* animate */, DEFER_RESUME, - schedulePictureInPictureModeChange, - "moveTasksToFullscreenStack - onTop"); - } - } else { - for (int i = 0; i < size; i++) { - final TaskRecord task = tasks.get(i); - // Position the tasks in the fullscreen stack in order at the bottom of the - // stack. Also defer resume until all the tasks have been moved to the - // fullscreen stack. - task.reparent(FULLSCREEN_WORKSPACE_STACK_ID, i /* position */, - REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME, - schedulePictureInPictureModeChange, - "moveTasksToFullscreenStack - NOT_onTop"); } } @@ -2437,9 +2671,13 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - void moveTasksToFullscreenStackLocked(int fromStackId, boolean onTop) { - mWindowManager.inSurfaceTransaction( - () -> moveTasksToFullscreenStackInSurfaceTransaction(fromStackId, onTop)); + void moveTasksToFullscreenStackLocked(ActivityStack fromStack, boolean onTop) { + moveTasksToFullscreenStackLocked(fromStack, DEFAULT_DISPLAY, onTop); + } + + void moveTasksToFullscreenStackLocked(ActivityStack fromStack, int toDisplayId, boolean onTop) { + mWindowManager.inSurfaceTransaction(() -> + moveTasksToFullscreenStackInSurfaceTransaction(fromStack, toDisplayId, onTop)); } void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds, @@ -2450,7 +2688,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D false /* deferResume */); } - void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds, + private void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds, Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds, boolean preserveWindows, boolean deferResume) { @@ -2459,7 +2697,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return; } - final ActivityStack stack = getStack(DOCKED_STACK_ID); + final ActivityStack stack = getDefaultDisplay().getStack( + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED); if (stack == null) { Slog.w(TAG, "resizeDockedStackLocked: docked stack not found"); return; @@ -2479,7 +2718,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // The dock stack either was dismissed or went fullscreen, which is kinda the same. // In this case we make all other static stacks fullscreen and move all // docked stack tasks to the fullscreen stack. - moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, ON_TOP); + moveTasksToFullscreenStackLocked(stack, ON_TOP); // stack shouldn't contain anymore activities, so nothing to resume. r = null; @@ -2488,13 +2727,14 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // static stacks need to be adjusted so they don't overlap with the docked stack. // We get the bounds to use from window manager which has been adjusted for any // screen controls and is also the same for all stacks. + final ArrayList<ActivityStack> stacks = getStacksOnDefaultDisplay(); final Rect otherTaskRect = new Rect(); - for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) { - if (i == DOCKED_STACK_ID) { + for (int i = stacks.size() - 1; i >= 0; --i) { + final ActivityStack current = stacks.get(i); + if (current.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { continue; } - final ActivityStack current = getStack(i); - if (current == null || !current.supportSplitScreenWindowingMode()) { + if (!current.supportsSplitScreenWindowingMode()) { continue; } // Need to set windowing mode here before we try to get the dock bounds. @@ -2504,7 +2744,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D tempRect /* outStackBounds */, otherTaskRect /* outTempTaskBounds */, true /* ignoreVisibility */); - resizeStackLocked(i, !tempRect.isEmpty() ? tempRect : null, + resizeStackLocked(current.mStackId, !tempRect.isEmpty() ? tempRect : null, !otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds, tempOtherTaskInsetBounds, preserveWindows, true /* allowResizeInDockedMode */, deferResume); @@ -2521,7 +2761,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } void resizePinnedStackLocked(Rect pinnedBounds, Rect tempPinnedTaskBounds) { - final PinnedActivityStack stack = getStack(PINNED_STACK_ID); + // TODO(multi-display): Pinned stack display should be passed in. + final PinnedActivityStack stack = getDefaultDisplay().getStack( + WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); if (stack == null) { Slog.w(TAG, "resizePinnedStackLocked: pinned stack not found"); return; @@ -2559,32 +2801,14 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - ActivityStack createStackOnDisplay(int stackId, int displayId, boolean onTop) { - final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(displayId); - if (activityDisplay == null) { - return null; - } - return createStack(stackId, activityDisplay, onTop); - - } - - ActivityStack createStack(int stackId, ActivityDisplay display, boolean onTop) { - switch (stackId) { - case PINNED_STACK_ID: - return new PinnedActivityStack(display, stackId, this, mRecentTasks, onTop); - default: - return new ActivityStack(display, stackId, this, mRecentTasks, onTop); - } - } - - void removeStackInSurfaceTransaction(int stackId) { + private void removeStackInSurfaceTransaction(int stackId) { final ActivityStack stack = getStack(stackId); if (stack == null) { return; } final ArrayList<TaskRecord> tasks = stack.getAllTasks(); - if (stack.getStackId() == PINNED_STACK_ID) { + if (stack.getWindowingMode() == WINDOWING_MODE_PINNED) { /** * Workaround: Force-stop all the activities in the pinned stack before we reparent them * to the fullscreen stack. This is to guarantee that when we are removing a stack, @@ -2602,7 +2826,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D true /* processPausingActivites */, null /* configuration */); // Move all the tasks to the bottom of the fullscreen stack - moveTasksToFullscreenStackLocked(PINNED_STACK_ID, !ON_TOP); + moveTasksToFullscreenStackLocked(pinnedStack, !ON_TOP); } else { for (int i = tasks.size() - 1; i >= 0; i--) { removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */, @@ -2617,8 +2841,23 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D * instead moved back onto the fullscreen stack. */ void removeStackLocked(int stackId) { - mWindowManager.inSurfaceTransaction( - () -> removeStackInSurfaceTransaction(stackId)); + mWindowManager.inSurfaceTransaction(() -> removeStackInSurfaceTransaction(stackId)); + } + + /** + * Removes stacks in the input windowing modes from the system if they are of activity type + * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED + */ + void removeStacksInWindowingModes(int... windowingModes) { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + mActivityDisplays.valueAt(i).removeStacksInWindowingModes(windowingModes); + } + } + + void removeStacksWithActivityTypes(int... activityTypes) { + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + mActivityDisplays.valueAt(i).removeStacksWithActivityTypes(activityTypes); + } } /** @@ -2640,8 +2879,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D */ boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents, boolean pauseImmediately) { - final TaskRecord tr = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, - INVALID_STACK_ID); + final TaskRecord tr = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS); if (tr != null) { tr.removeTaskActivitiesLocked(pauseImmediately); cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents); @@ -2654,10 +2892,23 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return false; } + void addRecentActivity(ActivityRecord r) { + if (r == null) { + return; + } + final TaskRecord task = r.getTask(); + mRecentTasks.addLocked(task); + task.touchActiveTime(); + } + + void removeTaskFromRecents(TaskRecord task) { + mRecentTasks.remove(task); + task.removedFromRecents(); + } + void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess, boolean removeFromRecents) { if (removeFromRecents) { - mRecentTasks.remove(tr); - tr.removedFromRecents(); + removeTaskFromRecents(tr); } ComponentName component = tr.getBaseIntent().getComponent(); if (component == null) { @@ -2740,27 +2991,15 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D /** * Restores a recent task to a stack * @param task The recent task to be restored. - * @param stackId The stack to restore the task to (default launch stack will be used - * if stackId is {@link android.app.ActivityManager.StackId#INVALID_STACK_ID} - * or is not a static stack). + * @param aOptions The activity options to use for restoration. * @return true if the task has been restored successfully. */ - boolean restoreRecentTaskLocked(TaskRecord task, int stackId) { - if (!StackId.isStaticStack(stackId)) { - // If stack is not static (or stack id is invalid) - use the default one. - // This means that tasks that were on external displays will be restored on the - // primary display. - stackId = task.getLaunchStackId(); - } else if (stackId == DOCKED_STACK_ID && !task.supportsSplitScreen()) { - // Preferred stack is the docked stack, but the task can't go in the docked stack. - // Put it in the fullscreen stack. - stackId = FULLSCREEN_WORKSPACE_STACK_ID; - } - + boolean restoreRecentTaskLocked(TaskRecord task, ActivityOptions aOptions) { + final ActivityStack stack = getLaunchStack(null, aOptions, task, !ON_TOP); final ActivityStack currentStack = task.getStack(); if (currentStack != null) { // Task has already been restored once. See if we need to do anything more - if (currentStack.mStackId == stackId) { + if (currentStack == stack) { // Nothing else to do since it is already restored in the right stack. return true; } @@ -2769,19 +3008,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D currentStack.removeTask(task, "restoreRecentTaskLocked", REMOVE_TASK_MODE_MOVING); } - final ActivityStack stack = - getStack(stackId, CREATE_IF_NEEDED, !ON_TOP); - - if (stack == null) { - // What does this mean??? Not sure how we would get here... - if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, - "Unable to find/create stack to restore recent task=" + task); - return false; - } - - stack.addTask(task, false /* toTop */, "restoreRecentTask"); + stack.addTask(task, !ON_TOP, "restoreRecentTask"); // TODO: move call for creation here and other place into Stack.addTask() - task.createWindowContainer(false /* toTop */, true /* showForAllUsers */); + task.createWindowContainer(!ON_TOP, true /* showForAllUsers */); if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Added restored task=" + task + " to stack=" + stack); final ArrayList<ActivityRecord> activities = task.mActivities; @@ -2803,7 +3032,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown displayId=" + displayId); } - final ActivityStack stack = mStacks.get(stackId); + final ActivityStack stack = getStack(stackId); if (stack == null) { throw new IllegalArgumentException("moveStackToDisplayLocked: Unknown stackId=" + stackId); @@ -2828,8 +3057,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D * Returns the reparent target stack, creating the stack if necessary. This call also enforces * the various checks on tasks that are going to be reparented from one stack to another. */ - ActivityStack getReparentTargetStack(TaskRecord task, int stackId, boolean toTop) { + ActivityStack getReparentTargetStack(TaskRecord task, ActivityStack stack, boolean toTop) { final ActivityStack prevStack = task.getStack(); + final int stackId = stack.mStackId; + final boolean inMultiWindowMode = stack.inMultiWindowMode(); // Check that we aren't reparenting to the same stack that the task is already in if (prevStack != null && prevStack.mStackId == stackId) { @@ -2840,22 +3071,22 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Ensure that we aren't trying to move into a multi-window stack without multi-window // support - if (StackId.isMultiWindowStack(stackId) && !mService.mSupportsMultiWindow) { + if (inMultiWindowMode && !mService.mSupportsMultiWindow) { throw new IllegalArgumentException("Device doesn't support multi-window, can not" - + " reparent task=" + task + " to stackId=" + stackId); + + " reparent task=" + task + " to stack=" + stack); } // Ensure that we're not moving a task to a dynamic stack if device doesn't support // multi-display. - // TODO(multi-display): Support non-dynamic stacks on secondary displays. - if (StackId.isDynamicStack(stackId) && !mService.mSupportsMultiDisplay) { + if (stack.mDisplayId != DEFAULT_DISPLAY && !mService.mSupportsMultiDisplay) { throw new IllegalArgumentException("Device doesn't support multi-display, can not" + " reparent task=" + task + " to stackId=" + stackId); } // Ensure that we aren't trying to move into a freeform stack without freeform // support - if (stackId == FREEFORM_WORKSPACE_STACK_ID && !mService.mSupportsFreeformWindowManagement) { + if (stack.getWindowingMode() == WINDOWING_MODE_FREEFORM + && !mService.mSupportsFreeformWindowManagement) { throw new IllegalArgumentException("Device doesn't support freeform, can not reparent" + " task=" + task); } @@ -2864,25 +3095,25 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // used for split-screen mode and will cause things like the docked divider to show up. We // instead leave the task in its current stack or move it to the fullscreen stack if it // isn't currently in a stack. - if (stackId == DOCKED_STACK_ID && !task.isResizeable()) { - stackId = (prevStack != null) ? prevStack.mStackId : FULLSCREEN_WORKSPACE_STACK_ID; + if (inMultiWindowMode && !task.isResizeable()) { Slog.w(TAG, "Can not move unresizeable task=" + task + " to docked stack." + " Moving to stackId=" + stackId + " instead."); + // Temporarily disable resizeablility of the task as we don't want it to be resized if, + // for example, a docked stack is created which will lead to the stack we are moving + // from being resized and and its resizeable tasks being resized. + try { + task.mTemporarilyUnresizable = true; + stack = stack.getDisplay().createStack( + WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), toTop); + } finally { + task.mTemporarilyUnresizable = false; + } } - - // Temporarily disable resizeablility of the task as we don't want it to be resized if, for - // example, a docked stack is created which will lead to the stack we are moving from being - // resized and and its resizeable tasks being resized. - try { - task.mTemporarilyUnresizable = true; - return getStack(stackId, CREATE_IF_NEEDED, toTop); - } finally { - task.mTemporarilyUnresizable = false; - } + return stack; } boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect destBounds) { - final ActivityStack stack = getStack(stackId, !CREATE_IF_NEEDED, !ON_TOP); + final ActivityStack stack = getStack(stackId); if (stack == null) { throw new IllegalArgumentException( "moveTopStackActivityToPinnedStackLocked: Unknown stackId=" + stackId); @@ -2912,12 +3143,17 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mWindowManager.deferSurfaceLayout(); + final ActivityDisplay display = r.getStack().getDisplay(); + PinnedActivityStack stack = display.getPinnedStack(); + // This will clear the pinned stack by moving an existing task to the full screen stack, // ensuring only one task is present. - moveTasksToFullscreenStackLocked(PINNED_STACK_ID, !ON_TOP); + if (stack != null) { + moveTasksToFullscreenStackLocked(stack, !ON_TOP); + } // Need to make sure the pinned stack exist so we can resize it below... - final PinnedActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP); + stack = display.getOrCreateStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP); try { final TaskRecord task = r.getTask(); @@ -2941,8 +3177,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D moveHomeStackToFront(reason); } // Defer resume until below, and do not schedule PiP changes until we animate below - task.reparent(PINNED_STACK_ID, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, - DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason); + task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME, + false /* schedulePictureInPictureModeChange */, reason); } else { // There are multiple activities in the task and moving the top activity should // reveal/leave the other activities in their original task. @@ -2957,7 +3193,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D r.reparent(newTask, MAX_VALUE, "moveActivityToStack"); // Defer resume until below, and do not schedule PiP changes until we animate below - newTask.reparent(PINNED_STACK_ID, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, + newTask.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME, false /* schedulePictureInPictureModeChange */, reason); } @@ -2983,8 +3219,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); resumeFocusedStackTopActivityLocked(); - mService.mTaskChangeNotificationController.notifyActivityPinned(r.packageName, - r.getTask().taskId); + mService.mTaskChangeNotificationController.notifyActivityPinned(r); } /** Move activity with its stack to front and make the stack focused. */ @@ -3044,6 +3279,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // tasks should always have lower priority than any affinity-matching tasks // in the fullscreen stacks affinityMatch = mTmpFindTaskResult.r; + } else if (DEBUG_TASKS && mTmpFindTaskResult.matchedByRootAffinity) { + Slog.d(TAG_TASKS, "Skipping match on different display " + + mTmpFindTaskResult.r.getDisplayId() + " " + displayId); } } } @@ -3408,14 +3646,17 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D boolean switchUserLocked(int userId, UserState uss) { final int focusStackId = mFocusedStack.getStackId(); // We dismiss the docked stack whenever we switch users. - moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, focusStackId == DOCKED_STACK_ID); + final ActivityStack dockedStack = getDefaultDisplay().getSplitScreenStack(); + if (dockedStack != null) { + moveTasksToFullscreenStackLocked(dockedStack, mFocusedStack == dockedStack); + } // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will // also cause all tasks to be moved to the fullscreen stack at a position that is // appropriate. removeStackLocked(PINNED_STACK_ID); mUserStackInFront.put(mCurrentUser, focusStackId); - final int restoreStackId = mUserStackInFront.get(userId, HOME_STACK_ID); + final int restoreStackId = mUserStackInFront.get(userId, mHomeStack.mStackId); mCurrentUser = userId; mStartingUsers.add(uss); @@ -3448,7 +3689,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D /** Checks whether the userid is a profile of the current user. */ boolean isCurrentProfileLocked(int userId) { if (userId == mCurrentUser) return true; - return mService.mUserController.isCurrentProfileLocked(userId); + return mService.mUserController.isCurrentProfile(userId); } /** @@ -3555,15 +3796,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D pw.print(prefix); pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser); pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront); - pw.print(prefix); pw.println("mStacks=" + mStacks); - // TODO: move this to LockTaskController - final SparseArray<String[]> packages = mService.mLockTaskPackages; - if (packages.size() > 0) { - pw.print(prefix); pw.println("mLockTaskPackages (userId:packages)="); - for (int i = 0; i < packages.size(); ++i) { - pw.print(prefix); pw.print(prefix); pw.print(packages.keyAt(i)); - pw.print(":"); pw.println(Arrays.toString(packages.valueAt(i))); - } + for (int i = mActivityDisplays.size() - 1; i >= 0; --i) { + final ActivityDisplay display = mActivityDisplays.valueAt(i); + pw.println(prefix + "displayId=" + display.mDisplayId + " mStacks=" + display.mStacks); } if (!mWaitingForActivityVisible.isEmpty()) { pw.print(prefix); pw.println("mWaitingForActivityVisible="); @@ -3576,6 +3811,26 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mService.mLockTaskController.dump(pw, prefix); } + public void writeToProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + super.writeToProto(proto, CONFIGURATION_CONTAINER); + for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) { + ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx); + activityDisplay.writeToProto(proto, DISPLAYS); + } + mKeyguardController.writeToProto(proto, KEYGUARD_CONTROLLER); + if (mFocusedStack != null) { + proto.write(FOCUSED_STACK_ID, mFocusedStack.mStackId); + ActivityRecord focusedActivity = getResumedActivityLocked(); + if (focusedActivity != null) { + focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY); + } + } else { + proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID); + } + proto.end(token); + } + /** * Dump all connected displays' configurations. * @param prefix Prefix to apply to each line of the dump. @@ -3605,8 +3860,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks; for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) { ActivityStack stack = stacks.get(stackNdx); - if (!dumpVisibleStacksOnly || - stack.shouldBeVisible(null) == STACK_VISIBLE) { + if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) { activities.addAll(stack.getDumpActivitiesLocked(name)); } } @@ -3832,15 +4086,22 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return getActivityDisplayOrCreateLocked(displayId) != null; } + // TODO: Look into consolidating with getActivityDisplayOrCreateLocked() ActivityDisplay getActivityDisplay(int displayId) { return mActivityDisplays.get(displayId); } + // TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display. + ActivityDisplay getDefaultDisplay() { + return mActivityDisplays.get(DEFAULT_DISPLAY); + } + /** * Get an existing instance of {@link ActivityDisplay} or create new if there is a * corresponding record in display manager. */ - private ActivityDisplay getActivityDisplayOrCreateLocked(int displayId) { + // TODO: Look into consolidating with getActivityDisplay() + ActivityDisplay getActivityDisplayOrCreateLocked(int displayId) { ActivityDisplay activityDisplay = mActivityDisplays.get(displayId); if (activityDisplay != null) { return activityDisplay; @@ -3855,17 +4116,18 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return null; } // The display hasn't been added to ActivityManager yet, create a new record now. - activityDisplay = new ActivityDisplay(displayId); - if (activityDisplay.mDisplay == null) { - Slog.w(TAG, "Display " + displayId + " gone before initialization complete"); - return null; - } - mActivityDisplays.put(displayId, activityDisplay); + activityDisplay = new ActivityDisplay(this, displayId); + attachDisplay(activityDisplay); calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay); mWindowManager.onDisplayAdded(displayId); return activityDisplay; } + @VisibleForTesting + void attachDisplay(ActivityDisplay display) { + mActivityDisplays.put(display.mDisplayId, display); + } + private void calculateDefaultMinimalSizeOfResizeableTasks(ActivityDisplay display) { mDefaultMinSizeOfResizeableTask = mService.mContext.getResources().getDimensionPixelSize( @@ -3893,7 +4155,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Moving all tasks to fullscreen stack, because it's guaranteed to be // a valid launch stack for all activities. This way the task history from // external display will be preserved on primary after move. - moveTasksToFullscreenStackLocked(stack.getStackId(), true /* onTop */); + moveTasksToFullscreenStackLocked(stack, true /* onTop */); } } @@ -3955,7 +4217,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D if (display.mAllSleepTokens.isEmpty()) { return; } - for (SleepTokenImpl token : display.mAllSleepTokens) { + for (SleepToken token : display.mAllSleepTokens) { mSleepTokens.remove(token); } display.mAllSleepTokens.clear(); @@ -3963,7 +4225,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mService.updateSleepIfNeededLocked(); } - private StackInfo getStackInfoLocked(ActivityStack stack) { + private StackInfo getStackInfo(ActivityStack stack) { final int displayId = stack.mDisplayId; final ActivityDisplay display = mActivityDisplays.get(displayId); StackInfo info = new StackInfo(); @@ -3971,11 +4233,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D info.displayId = displayId; info.stackId = stack.mStackId; info.userId = stack.mCurrentUser; - info.visible = stack.shouldBeVisible(null) == STACK_VISIBLE; + info.visible = stack.shouldBeVisible(null); // A stack might be not attached to a display. - info.position = display != null - ? display.mStacks.indexOf(stack) - : 0; + info.position = display != null ? display.mStacks.indexOf(stack) : 0; + info.configuration.setTo(stack.getConfiguration()); ArrayList<TaskRecord> tasks = stack.getAllTasks(); final int numTasks = tasks.size(); @@ -4004,40 +4265,44 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return info; } - StackInfo getStackInfoLocked(int stackId) { + StackInfo getStackInfo(int stackId) { ActivityStack stack = getStack(stackId); if (stack != null) { - return getStackInfoLocked(stack); + return getStackInfo(stack); } return null; } + StackInfo getStackInfo(int windowingMode, int activityType) { + final ActivityStack stack = getStack(windowingMode, activityType); + return (stack != null) ? getStackInfo(stack) : null; + } + ArrayList<StackInfo> getAllStackInfosLocked() { ArrayList<StackInfo> list = new ArrayList<>(); for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) { ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks; for (int ndx = stacks.size() - 1; ndx >= 0; --ndx) { - list.add(getStackInfoLocked(stacks.get(ndx))); + list.add(getStackInfo(stacks.get(ndx))); } } return list; } - void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredStackId, + void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredWindowingMode, int preferredDisplayId, int actualStackId) { - handleNonResizableTaskIfNeeded(task, preferredStackId, preferredDisplayId, actualStackId, - false /* forceNonResizable */); + handleNonResizableTaskIfNeeded(task, preferredWindowingMode, preferredDisplayId, + actualStackId, false /* forceNonResizable */); } - void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredStackId, + void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredWindowingMode, int preferredDisplayId, int actualStackId, boolean forceNonResizable) { final boolean isSecondaryDisplayPreferred = - (preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY) - || StackId.isDynamicStack(preferredStackId); + (preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY); final ActivityStack actualStack = getStack(actualStackId); final boolean inSplitScreenMode = actualStack != null && actualStack.inSplitScreenWindowingMode(); - if (((!inSplitScreenMode && preferredStackId != DOCKED_STACK_ID) + if (((!inSplitScreenMode && preferredWindowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) && !isSecondaryDisplayPreferred) || task.isActivityTypeHome()) { return; } @@ -4065,7 +4330,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } final ActivityRecord topActivity = task.getTopActivity(); - if (launchOnSecondaryDisplayFailed || !task.supportsSplitScreen() || forceNonResizable) { + if (launchOnSecondaryDisplayFailed + || !task.supportsSplitScreenWindowingMode() || forceNonResizable) { if (launchOnSecondaryDisplayFailed) { // Display a warning toast that we tried to put a non-resizeable task on a secondary // display with config different from global config. @@ -4079,7 +4345,12 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Dismiss docked stack. If task appeared to be in docked stack but is not resizable - // we need to move it to top of fullscreen stack, otherwise it will be covered. - moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, actualStackId == DOCKED_STACK_ID); + + final ActivityStack dockedStack = task.getStack().getDisplay().getSplitScreenStack(); + if (dockedStack != null) { + moveTasksToFullscreenStackLocked(dockedStack, + actualStackId == dockedStack.getStackId()); + } } else if (topActivity != null && topActivity.isNonResizableOrForcedResizable() && !topActivity.noDisplay) { final String packageName = topActivity.appInfo.packageName; @@ -4284,122 +4555,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - // TODO: Move to its own file. - /** Exactly one of these classes per Display in the system. Capable of holding zero or more - * attached {@link ActivityStack}s */ - class ActivityDisplay extends ConfigurationContainer { - /** Actual Display this object tracks. */ - int mDisplayId; - Display mDisplay; - - /** All of the stacks on this display. Order matters, topmost stack is in front of all other - * stacks, bottommost behind. Accessed directly by ActivityManager package classes */ - final ArrayList<ActivityStack> mStacks = new ArrayList<>(); - - /** Array of all UIDs that are present on the display. */ - private IntArray mDisplayAccessUIDs = new IntArray(); - - /** All tokens used to put activities on this stack to sleep (including mOffToken) */ - final ArrayList<SleepTokenImpl> mAllSleepTokens = new ArrayList<>(); - /** The token acquired by ActivityStackSupervisor to put stacks on the display to sleep */ - SleepToken mOffToken; - - private boolean mSleeping; - - @VisibleForTesting - ActivityDisplay() { - mActivityDisplays.put(mDisplayId, this); - } - - // After instantiation, check that mDisplay is not null before using this. The alternative - // is for this to throw an exception if mDisplayManager.getDisplay() returns null. - ActivityDisplay(int displayId) { - final Display display = mDisplayManager.getDisplay(displayId); - if (display == null) { - return; - } - init(display); - } - - void init(Display display) { - mDisplay = display; - mDisplayId = display.getDisplayId(); - } - - void attachStack(ActivityStack stack, int position) { - if (DEBUG_STACK) Slog.v(TAG_STACK, "attachStack: attaching " + stack - + " to displayId=" + mDisplayId + " position=" + position); - mStacks.add(position, stack); - mService.updateSleepIfNeededLocked(); - } - - void detachStack(ActivityStack stack) { - if (DEBUG_STACK) Slog.v(TAG_STACK, "detachStack: detaching " + stack - + " from displayId=" + mDisplayId); - mStacks.remove(stack); - mService.updateSleepIfNeededLocked(); - } - - @Override - public String toString() { - return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}"; - } - - @Override - protected int getChildCount() { - return mStacks.size(); - } - - @Override - protected ConfigurationContainer getChildAt(int index) { - return mStacks.get(index); - } - - @Override - protected ConfigurationContainer getParent() { - return ActivityStackSupervisor.this; - } - - boolean isPrivate() { - return (mDisplay.getFlags() & FLAG_PRIVATE) != 0; - } - - boolean isUidPresent(int uid) { - for (ActivityStack stack : mStacks) { - if (stack.isUidPresent(uid)) { - return true; - } - } - return false; - } - - /** Update and get all UIDs that are present on the display and have access to it. */ - private IntArray getPresentUIDs() { - mDisplayAccessUIDs.clear(); - for (ActivityStack stack : mStacks) { - stack.getPresentUIDs(mDisplayAccessUIDs); - } - return mDisplayAccessUIDs; - } - - boolean shouldDestroyContentOnRemove() { - return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT; - } - - boolean shouldSleep() { - return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty()) - && (mService.mRunningVoice == null); - } - - boolean isSleeping() { - return mSleeping; - } - - void setIsSleeping(boolean asleep) { - mSleeping = asleep; - } - } - ActivityStack findStackBehind(ActivityStack stack) { // TODO(multi-display): We are only looking for stacks on the default display. final ActivityDisplay display = mActivityDisplays.get(DEFAULT_DISPLAY); @@ -4432,32 +4587,36 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D final String callingPackage; final Intent intent; final int userId; + int activityType = ACTIVITY_TYPE_UNDEFINED; + int windowingMode = WINDOWING_MODE_UNDEFINED; final ActivityOptions activityOptions = (bOptions != null) ? new ActivityOptions(bOptions) : null; - final int launchStackId = (activityOptions != null) - ? activityOptions.getLaunchStackId() : INVALID_STACK_ID; - if (StackId.isHomeOrRecentsStack(launchStackId)) { + if (activityOptions != null) { + activityType = activityOptions.getLaunchActivityType(); + windowingMode = activityOptions.getLaunchWindowingMode(); + } + if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) { throw new IllegalArgumentException("startActivityFromRecentsInner: Task " + taskId + " can't be launch in the home/recents stack."); } mWindowManager.deferSurfaceLayout(); try { - if (launchStackId == DOCKED_STACK_ID) { + if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { mWindowManager.setDockedStackCreateState( activityOptions.getDockCreateMode(), null /* initialBounds */); // Defer updating the stack in which recents is until the app transition is done, to // not run into issues where we still need to draw the task in recents but the // docked stack is already created. - deferUpdateBounds(RECENTS_STACK_ID); + deferUpdateBounds(ACTIVITY_TYPE_RECENTS); mWindowManager.prepareAppTransition(TRANSIT_DOCK_TASK_FROM_RECENTS, false); } task = anyTaskForIdLocked(taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, - launchStackId); + activityOptions); if (task == null) { - continueUpdateBounds(RECENTS_STACK_ID); + continueUpdateBounds(ACTIVITY_TYPE_RECENTS); mWindowManager.executeAppTransition(); throw new IllegalArgumentException( "startActivityFromRecentsInner: Task " + taskId + " not found."); @@ -4465,15 +4624,11 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Since we don't have an actual source record here, we assume that the currently // focused activity was the source. - final ActivityStack focusedStack = getFocusedStack(); - final ActivityRecord sourceRecord = - focusedStack != null ? focusedStack.topActivity() : null; - - if (launchStackId != INVALID_STACK_ID) { - if (task.getStackId() != launchStackId) { - task.reparent(launchStackId, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, - DEFER_RESUME, "startActivityFromRecents"); - } + final ActivityStack stack = getLaunchStack(null, activityOptions, task, ON_TOP); + + if (stack != null && task.getStack() != stack) { + task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME, + "startActivityFromRecents"); } // If the user must confirm credentials (e.g. when first launching a work app and the @@ -4492,15 +4647,12 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // If we are launching the task in the docked stack, put it into resizing mode so // the window renders full-screen with the background filling the void. Also only // call this at the end to make sure that tasks exists on the window manager side. - if (launchStackId == DOCKED_STACK_ID) { + if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { setResizingDuringAnimation(task); } mService.mActivityStarter.postStartActivityProcessing(task.getTopActivity(), - ActivityManager.START_TASK_TO_FRONT, - sourceRecord != null - ? sourceRecord.getTask().getStackId() : INVALID_STACK_ID, - sourceRecord, task.getStack()); + ActivityManager.START_TASK_TO_FRONT, task.getStack()); return ActivityManager.START_TASK_TO_FRONT; } callingUid = task.mCallingUid; @@ -4510,7 +4662,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D userId = task.userId; int result = mService.startActivityInPackage(callingUid, callingPackage, intent, null, null, null, 0, 0, bOptions, userId, task, "startActivityFromRecents"); - if (launchStackId == DOCKED_STACK_ID) { + if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { setResizingDuringAnimation(task); } return result; @@ -4532,8 +4684,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D for (int j = display.mStacks.size() - 1; j >= 0; j--) { final ActivityStack stack = display.mStacks.get(j); // Get top activity from a visible stack and add it to the list. - if (stack.shouldBeVisible(null /* starting */) - == ActivityStack.STACK_VISIBLE) { + if (stack.shouldBeVisible(null /* starting */)) { final ActivityRecord top = stack.topActivity(); if (top != null) { if (stack == mFocusedStack) { diff --git a/com/android/server/am/ActivityStarter.java b/com/android/server/am/ActivityStarter.java index 16abcfb6..d444c663 100644 --- a/com/android/server/am/ActivityStarter.java +++ b/com/android/server/am/ActivityStarter.java @@ -27,18 +27,19 @@ import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION; import static android.app.ActivityManager.START_SUCCESS; import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.ActivityManager.StackId; -import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; -import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; -import static android.app.ActivityManager.StackId.RECENTS_STACK_ID; import static android.app.ActivityManager.StackId.isDynamicStack; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; @@ -76,8 +77,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.am.ActivityManagerService.ANIMATE; import static com.android.server.am.ActivityStack.ActivityState.RESUMED; -import static com.android.server.am.ActivityStack.STACK_INVISIBLE; -import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED; import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME; import static com.android.server.am.ActivityStackSupervisor.ON_TOP; import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; @@ -167,7 +166,8 @@ class ActivityStarter { private boolean mDoResume; private int mStartFlags; private ActivityRecord mSourceRecord; - private int mSourceDisplayId; + // The display to launch the activity onto, barring any strong reason to do otherwise. + private int mPreferredDisplayId; private TaskRecord mInTask; private boolean mAddingToTask; @@ -186,6 +186,12 @@ class ActivityStarter { private boolean mAvoidMoveToFront; private boolean mPowerHintSent; + // We must track when we deliver the new intent since multiple code paths invoke + // {@link #deliverNewIntent}. This is due to early returns in the code path. This flag is used + // inside {@link #deliverNewIntent} to suppress duplicate requests and ensure the intent is + // delivered at most once. + private boolean mIntentDelivered; + private IVoiceInteractionSession mVoiceSession; private IVoiceInteractor mVoiceInteractor; @@ -222,7 +228,7 @@ class ActivityStarter { mDoResume = false; mStartFlags = 0; mSourceRecord = null; - mSourceDisplayId = INVALID_DISPLAY; + mPreferredDisplayId = INVALID_DISPLAY; mInTask = null; mAddingToTask = false; @@ -243,6 +249,8 @@ class ActivityStarter { mVoiceInteractor = null; mUsingVr2dDisplay = false; + + mIntentDelivered = false; } ActivityStarter(ActivityManagerService service, ActivityStackSupervisor supervisor) { @@ -589,9 +597,7 @@ class ActivityStarter { auxiliaryResponse.token, auxiliaryResponse.needsPhaseTwo); } - void postStartActivityProcessing( - ActivityRecord r, int result, int prevFocusedStackId, ActivityRecord sourceRecord, - ActivityStack targetStack) { + void postStartActivityProcessing(ActivityRecord r, int result, ActivityStack targetStack) { if (ActivityManager.isStartResultFatalError(result)) { return; @@ -613,7 +619,8 @@ class ActivityStarter { } if (startedActivityStackId == DOCKED_STACK_ID) { - final ActivityStack homeStack = mSupervisor.getStack(HOME_STACK_ID); + final ActivityStack homeStack = mSupervisor.getDefaultDisplay().getStack( + WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME); final boolean homeStackVisible = homeStack != null && homeStack.isVisible(); if (homeStackVisible) { // We launch an activity while being in home stack, which means either launcher or @@ -1001,8 +1008,7 @@ class ActivityStarter { mService.mWindowManager.continueSurfaceLayout(); } - postStartActivityProcessing(r, result, mSupervisor.getLastStack().mStackId, mSourceRecord, - mTargetStack); + postStartActivityProcessing(r, result, mTargetStack); return result; } @@ -1024,10 +1030,12 @@ class ActivityStarter { ActivityRecord reusedActivity = getReusableIntentActivity(); - final int preferredLaunchStackId = - (mOptions != null) ? mOptions.getLaunchStackId() : INVALID_STACK_ID; - final int preferredLaunchDisplayId = - (mOptions != null) ? mOptions.getLaunchDisplayId() : DEFAULT_DISPLAY; + int preferredWindowingMode = WINDOWING_MODE_UNDEFINED; + int preferredLaunchDisplayId = DEFAULT_DISPLAY; + if (mOptions != null) { + preferredWindowingMode = mOptions.getLaunchWindowingMode(); + preferredLaunchDisplayId = mOptions.getLaunchDisplayId(); + } if (reusedActivity != null) { // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but @@ -1077,9 +1085,7 @@ class ActivityStarter { // so make sure the task now has the identity of the new intent. top.getTask().setIntent(mStartActivity); } - ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask()); - top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, - mStartActivity.launchedFromPackage); + deliverNewIntent(top); } } @@ -1141,7 +1147,6 @@ class ActivityStarter { && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop || mLaunchSingleTask); if (dontStart) { - ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.getTask()); // For paranoia, make sure we have correctly resumed the top activity. topStack.mLastPausedActivity = null; if (mDoResume) { @@ -1153,12 +1158,12 @@ class ActivityStarter { // anything if that is the case, so this is it! return START_RETURN_INTENT_TO_CALLER; } - top.deliverNewIntentLocked( - mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage); + + deliverNewIntent(top); // Don't use mStartActivity.task to show the toast. We're not starting a new activity // but reusing 'top'. Fields in mStartActivity may not be fully initialized. - mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredLaunchStackId, + mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredWindowingMode, preferredLaunchDisplayId, topStack.mStackId); return START_DELIVERED_TO_TOP; @@ -1173,8 +1178,7 @@ class ActivityStarter { if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) { newTask = true; - result = setTaskFromReuseOrCreateNewTask( - taskToAffiliate, preferredLaunchStackId, topStack); + result = setTaskFromReuseOrCreateNewTask(taskToAffiliate, topStack); } else if (mSourceRecord != null) { result = setTaskFromSourceRecord(); } else if (mInTask != null) { @@ -1237,11 +1241,11 @@ class ActivityStarter { mOptions); } } else { - mTargetStack.addRecentActivityLocked(mStartActivity); + mSupervisor.addRecentActivity(mStartActivity); } mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack); - mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredLaunchStackId, + mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode, preferredLaunchDisplayId, mTargetStack.mStackId); return START_SUCCESS; @@ -1260,7 +1264,7 @@ class ActivityStarter { mVoiceSession = voiceSession; mVoiceInteractor = voiceInteractor; - mSourceDisplayId = getSourceDisplayId(mSourceRecord, mStartActivity); + mPreferredDisplayId = getPreferedDisplayId(mSourceRecord, mStartActivity, options); mLaunchBounds = getOverrideBounds(r, options, inTask); @@ -1515,7 +1519,7 @@ class ActivityStarter { !mLaunchSingleTask); } else { // Otherwise find the best task to put the activity in. - intentActivity = mSupervisor.findTaskLocked(mStartActivity, mSourceDisplayId); + intentActivity = mSupervisor.findTaskLocked(mStartActivity, mPreferredDisplayId); } } return intentActivity; @@ -1523,10 +1527,12 @@ class ActivityStarter { /** * Returns the ID of the display to use for a new activity. If the device is in VR mode, - * then return the Vr mode's virtual display ID. If not, if the source activity has - * a explicit display ID set, use that to launch the activity. + * then return the Vr mode's virtual display ID. If not, if the activity was started with + * a launchDisplayId, use that. Otherwise, if the source activity has a explicit display ID + * set, use that to launch the activity. */ - private int getSourceDisplayId(ActivityRecord sourceRecord, ActivityRecord startingActivity) { + private int getPreferedDisplayId( + ActivityRecord sourceRecord, ActivityRecord startingActivity, ActivityOptions options) { // Check if the Activity is a VR activity. If so, the activity should be launched in // main display. if (startingActivity != null && startingActivity.requestedVrComponent != null) { @@ -1543,6 +1549,13 @@ class ActivityStarter { return displayId; } + // If the caller requested a display, prefer that display. + final int launchDisplayId = + (options != null) ? options.getLaunchDisplayId() : INVALID_DISPLAY; + if (launchDisplayId != INVALID_DISPLAY) { + return launchDisplayId; + } + displayId = sourceRecord != null ? sourceRecord.getDisplayId() : INVALID_DISPLAY; // If the activity has a displayId set explicitly, launch it on the same displayId. if (displayId != INVALID_DISPLAY) { @@ -1601,12 +1614,11 @@ class ActivityStarter { mTargetStack.moveTaskToFrontLocked(intentTask, mNoAnimation, mOptions, mStartActivity.appTimeTracker, "bringingFoundTaskToFront"); mMovedToFront = true; - } else if (launchStack.mStackId == DOCKED_STACK_ID - || launchStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID) { + } else if (launchStack.inSplitScreenWindowingMode()) { if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) { // If we want to launch adjacent and mTargetStack is not the computed // launch stack - move task to top of computed stack. - intentTask.reparent(launchStack.mStackId, ON_TOP, + intentTask.reparent(launchStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME, "launchToSide"); } else { @@ -1623,17 +1635,17 @@ class ActivityStarter { // Target and computed stacks are on different displays and we've // found a matching task - move the existing instance to that display and // move it to front. - intentActivity.getTask().reparent(launchStack.mStackId, ON_TOP, + intentActivity.getTask().reparent(launchStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME, "reparentToDisplay"); mMovedToFront = true; - } else if (launchStack.getStackId() == StackId.HOME_STACK_ID - && mTargetStack.getStackId() != StackId.HOME_STACK_ID) { + } else if (launchStack.isActivityTypeHome() + && !mTargetStack.isActivityTypeHome()) { // It is possible for the home activity to be in another stack initially. // For example, the activity may have been initially started with an intent // which placed it in the fullscreen stack. To ensure the proper handling of // the activity based on home stack assumptions, we must move it over. - intentActivity.getTask().reparent(launchStack.mStackId, ON_TOP, + intentActivity.getTask().reparent(launchStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME, "reparentingHome"); mMovedToFront = true; @@ -1654,8 +1666,8 @@ class ActivityStarter { mTargetStack.moveToFront("intentActivityFound"); } - mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(), INVALID_STACK_ID, - DEFAULT_DISPLAY, mTargetStack.mStackId); + mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(), + WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY, mTargetStack.mStackId); // If the caller has requested that the target task be reset, then do so. if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) { @@ -1675,8 +1687,7 @@ class ActivityStarter { // Task will be launched over the home stack, so return home. task.setTaskToReturnTo(ACTIVITY_TYPE_HOME); return; - } else if (focusedStack != null && focusedStack != task.getStack() && - focusedStack.isActivityTypeAssistant()) { + } else if (focusedStack != task.getStack() && focusedStack.isActivityTypeAssistant()) { // Task was launched over the assistant stack, so return there task.setTaskToReturnTo(ACTIVITY_TYPE_ASSISTANT); return; @@ -1739,13 +1750,10 @@ class ActivityStarter { // desires. if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop) && intentActivity.realActivity.equals(mStartActivity.realActivity)) { - ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, - intentActivity.getTask()); if (intentActivity.frontOfTask) { intentActivity.getTask().setIntent(mStartActivity); } - intentActivity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, - mStartActivity.launchedFromPackage); + deliverNewIntent(intentActivity); } else if (!intentActivity.getTask().isSameIntentFilter(mStartActivity)) { // In this case we are launching the root activity of the task, but with a // different intent. We should start a new instance on top. @@ -1779,7 +1787,7 @@ class ActivityStarter { } private int setTaskFromReuseOrCreateNewTask( - TaskRecord taskToAffiliate, int preferredLaunchStackId, ActivityStack topStack) { + TaskRecord taskToAffiliate, ActivityStack topStack) { mTargetStack = computeStackFocus( mStartActivity, true, mLaunchBounds, mLaunchFlags, mOptions); @@ -1793,15 +1801,8 @@ class ActivityStarter { mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession, mVoiceInteractor, !mLaunchTaskBehind /* toTop */); addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask"); - if (mLaunchBounds != null) { - final int stackId = mTargetStack.mStackId; - if (StackId.resizeStackWithLaunchBounds(stackId)) { - mService.resizeStack( - stackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1); - } else { - mStartActivity.getTask().updateOverrideConfiguration(mLaunchBounds); - } - } + updateBounds(mStartActivity.getTask(), mLaunchBounds); + if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity + " in new task " + mStartActivity.getTask()); } else { @@ -1821,8 +1822,10 @@ class ActivityStarter { // If stack id is specified in activity options, usually it means that activity is // launched not from currently focused stack (e.g. from SysUI or from shell) - in // that case we check the target stack. + // TODO: Not sure I understand the value or use of the commented out code and the + // comment above. See if this causes any issues and why... updateTaskReturnToType(mStartActivity.getTask(), mLaunchFlags, - preferredLaunchStackId != INVALID_STACK_ID ? mTargetStack : topStack); + /*preferredLaunchStackId != INVALID_STACK_ID ? mTargetStack : */topStack); } if (mDoResume) { mTargetStack.moveToFront("reuseOrNewTask"); @@ -1830,6 +1833,17 @@ class ActivityStarter { return START_SUCCESS; } + private void deliverNewIntent(ActivityRecord activity) { + if (mIntentDelivered) { + return; + } + + ActivityStack.logStartActivity(AM_NEW_INTENT, activity, activity.getTask()); + activity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, + mStartActivity.launchedFromPackage); + mIntentDelivered = true; + } + private int setTaskFromSourceRecord() { if (mService.mLockTaskController.isLockTaskModeViolation(mSourceRecord.getTask())) { Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity); @@ -1867,8 +1881,8 @@ class ActivityStarter { if (mTargetStack == null) { mTargetStack = sourceStack; } else if (mTargetStack != sourceStack) { - sourceTask.reparent(mTargetStack.mStackId, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, - !ANIMATE, DEFER_RESUME, "launchToSide"); + sourceTask.reparent(mTargetStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, + DEFER_RESUME, "launchToSide"); } final TaskRecord topTask = mTargetStack.topTask(); @@ -1886,7 +1900,7 @@ class ActivityStarter { mKeepCurTransition = true; if (top != null) { ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask()); - top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage); + deliverNewIntent(top); // For paranoia, make sure we have correctly resumed the top activity. mTargetStack.mLastPausedActivity = null; if (mDoResume) { @@ -1905,7 +1919,7 @@ class ActivityStarter { task.moveActivityToFrontLocked(top); top.updateOptionsLocked(mOptions); ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, task); - top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage); + deliverNewIntent(top); mTargetStack.mLastPausedActivity = null; if (mDoResume) { mSupervisor.resumeFocusedStackTopActivityLocked(); @@ -1941,14 +1955,12 @@ class ActivityStarter { || mLaunchSingleTop || mLaunchSingleTask) { mTargetStack.moveTaskToFrontLocked(mInTask, mNoAnimation, mOptions, mStartActivity.appTimeTracker, "inTaskToFront"); - ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.getTask()); if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) { // We don't need to start a new activity, and the client said not to do // anything if that is the case, so this is it! return START_RETURN_INTENT_TO_CALLER; } - top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, - mStartActivity.launchedFromPackage); + deliverNewIntent(top); return START_DELIVERED_TO_TOP; } } @@ -1963,17 +1975,15 @@ class ActivityStarter { } if (mLaunchBounds != null) { - mInTask.updateOverrideConfiguration(mLaunchBounds); - int stackId = mInTask.getLaunchStackId(); - if (stackId != mInTask.getStackId()) { - mInTask.reparent(stackId, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE, + // TODO: Shouldn't we already know what stack to use by the time we get here? + ActivityStack stack = mSupervisor.getLaunchStack(null, null, mInTask, ON_TOP); + if (stack != mInTask.getStack()) { + mInTask.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE, DEFER_RESUME, "inTaskToFront"); - stackId = mInTask.getStackId(); mTargetStack = mInTask.getStack(); } - if (StackId.resizeStackWithLaunchBounds(stackId)) { - mService.resizeStack(stackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1); - } + + updateBounds(mInTask, mLaunchBounds); } mTargetStack.moveTaskToFrontLocked( @@ -1986,6 +1996,19 @@ class ActivityStarter { return START_SUCCESS; } + void updateBounds(TaskRecord task, Rect bounds) { + if (bounds == null) { + return; + } + + final int stackId = task.getStackId(); + if (StackId.resizeStackWithLaunchBounds(stackId)) { + mService.resizeStack(stackId, bounds, true, !PRESERVE_WINDOWS, ANIMATE, -1); + } else { + task.updateOverrideConfiguration(bounds); + } + } + private void setTaskToCurrentTopOrCreateNewTask() { mTargetStack = computeStackFocus(mStartActivity, false, null /* bounds */, mLaunchFlags, mOptions); @@ -2079,20 +2102,21 @@ class ActivityStarter { return mSupervisor.mFocusedStack; } - if (mSourceDisplayId != DEFAULT_DISPLAY) { + if (mPreferredDisplayId != DEFAULT_DISPLAY) { // Try to put the activity in a stack on a secondary display. - stack = mSupervisor.getValidLaunchStackOnDisplay(mSourceDisplayId, r); + stack = mSupervisor.getValidLaunchStackOnDisplay(mPreferredDisplayId, r); if (stack == null) { // If source display is not suitable - look for topmost valid stack in the system. if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, - "computeStackFocus: Can't launch on mSourceDisplayId=" + mSourceDisplayId - + ", looking on all displays."); - stack = mSupervisor.getNextValidLaunchStackLocked(r, mSourceDisplayId); + "computeStackFocus: Can't launch on mPreferredDisplayId=" + + mPreferredDisplayId + ", looking on all displays."); + stack = mSupervisor.getNextValidLaunchStackLocked(r, mPreferredDisplayId); } } if (stack == null) { // We first try to put the task in the first dynamic stack on home display. - final ArrayList<ActivityStack> homeDisplayStacks = mSupervisor.mHomeStack.mStacks; + final ArrayList<ActivityStack> homeDisplayStacks = + mSupervisor.getStacksOnDefaultDisplay(); for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) { stack = homeDisplayStacks.get(stackNdx); if (isDynamicStack(stack.mStackId)) { @@ -2102,10 +2126,7 @@ class ActivityStarter { } } // If there is no suitable dynamic stack then we figure out which static stack to use. - final int stackId = task != null ? task.getLaunchStackId() : - bounds != null ? FREEFORM_WORKSPACE_STACK_ID : - FULLSCREEN_WORKSPACE_STACK_ID; - stack = mSupervisor.getStack(stackId, CREATE_IF_NEEDED, ON_TOP); + stack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP); } if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r=" + r + " stackId=" + stack.mStackId); @@ -2113,37 +2134,40 @@ class ActivityStarter { } /** Check if provided activity record can launch in currently focused stack. */ + // TODO: This method can probably be consolidated into getLaunchStack() below. private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) { final ActivityStack focusedStack = mSupervisor.mFocusedStack; - final int focusedStackId = mSupervisor.mFocusedStack.mStackId; final boolean canUseFocusedStack; - switch (focusedStackId) { - case FULLSCREEN_WORKSPACE_STACK_ID: - // The fullscreen stack can contain any task regardless of if the task is resizeable - // or not. So, we let the task go in the fullscreen task if it is the focus stack. - canUseFocusedStack = true; - break; - case ASSISTANT_STACK_ID: - canUseFocusedStack = r.isActivityTypeAssistant(); - break; - case DOCKED_STACK_ID: - // Any activity which supports split screen can go in the docked stack. - canUseFocusedStack = r.supportsSplitScreen(); - break; - case FREEFORM_WORKSPACE_STACK_ID: - // Any activity which supports freeform can go in the freeform stack. - canUseFocusedStack = r.supportsFreeform(); - break; - default: - // Dynamic stacks behave similarly to the fullscreen stack and can contain any - // resizeable task. - canUseFocusedStack = isDynamicStack(focusedStackId) - && r.canBeLaunchedOnDisplay(focusedStack.mDisplayId); + final int focusedStackId = mSupervisor.mFocusedStack.mStackId; + if (focusedStack.isActivityTypeAssistant()) { + canUseFocusedStack = r.isActivityTypeAssistant(); + } else { + switch (focusedStack.getWindowingMode()) { + case WINDOWING_MODE_FULLSCREEN: + // The fullscreen stack can contain any task regardless of if the task is + // resizeable or not. So, we let the task go in the fullscreen task if it is the + // focus stack. + canUseFocusedStack = true; + break; + case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: + case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: + // Any activity which supports split screen can go in the docked stack. + canUseFocusedStack = r.supportsSplitScreenWindowingMode(); + break; + case WINDOWING_MODE_FREEFORM: + // Any activity which supports freeform can go in the freeform stack. + canUseFocusedStack = r.supportsFreeform(); + break; + default: + // Dynamic stacks behave similarly to the fullscreen stack and can contain any + // resizeable task. + canUseFocusedStack = isDynamicStack(focusedStackId) + && r.canBeLaunchedOnDisplay(focusedStack.mDisplayId); + } } - return canUseFocusedStack && !newTask - // We strongly prefer to launch activities on the same display as their source. - && (mSourceDisplayId == focusedStack.mDisplayId); + // Using the focus stack isn't important enough to override the preferred display. + && (mPreferredDisplayId == focusedStack.mDisplayId); } private ActivityStack getLaunchStack(ActivityRecord r, int launchFlags, TaskRecord task, @@ -2153,54 +2177,16 @@ class ActivityStarter { return mReuseTask.getStack(); } - // If the activity is of a specific type, return the associated stack, creating it if - // necessary - if (r.isActivityTypeHome()) { - return mSupervisor.mHomeStack; - } - if (r.isActivityTypeRecents()) { - return mSupervisor.getStack(RECENTS_STACK_ID, CREATE_IF_NEEDED, ON_TOP); - } - if (r.isActivityTypeAssistant()) { - return mSupervisor.getStack(ASSISTANT_STACK_ID, CREATE_IF_NEEDED, ON_TOP); - } - - final int launchDisplayId = - (aOptions != null) ? aOptions.getLaunchDisplayId() : INVALID_DISPLAY; - - final int launchStackId = - (aOptions != null) ? aOptions.getLaunchStackId() : INVALID_STACK_ID; - - if (launchStackId != INVALID_STACK_ID && launchDisplayId != INVALID_DISPLAY) { - throw new IllegalArgumentException( - "Stack and display id can't be set at the same time."); - } + final int vrDisplayId = mUsingVr2dDisplay ? mPreferredDisplayId : INVALID_DISPLAY; + final ActivityStack launchStack = mSupervisor.getLaunchStack(r, aOptions, task, ON_TOP, + vrDisplayId); - if (isValidLaunchStackId(launchStackId, launchDisplayId, r)) { - return mSupervisor.getStack(launchStackId, CREATE_IF_NEEDED, ON_TOP); - } - if (launchStackId == DOCKED_STACK_ID) { - // The preferred launch stack is the docked stack, but it isn't a valid launch stack - // for this activity, so we put the activity in the fullscreen stack. - return mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID, CREATE_IF_NEEDED, ON_TOP); - } - if (launchDisplayId != INVALID_DISPLAY) { - // Stack id has higher priority than display id. - return mSupervisor.getValidLaunchStackOnDisplay(launchDisplayId, r); - } - - // If we are using Vr2d display, find the virtual display stack. - if (mUsingVr2dDisplay) { - ActivityStack as = mSupervisor.getValidLaunchStackOnDisplay(mSourceDisplayId, r); - if (DEBUG_STACK) { - Slog.v(TAG, "Launch stack for app: " + r.toString() + - ", on virtual display stack:" + as.toString()); - } - return as; + if (launchStack != null) { + return launchStack; } if (((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) == 0) - || mSourceDisplayId != DEFAULT_DISPLAY) { + || mPreferredDisplayId != DEFAULT_DISPLAY) { return null; } // Otherwise handle adjacent launch. @@ -2222,15 +2208,16 @@ class ActivityStarter { if (parentStack != null && parentStack.isDockedStack()) { // If parent was in docked stack, the natural place to launch another activity // will be fullscreen, so it can appear alongside the docked window. - return mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID, CREATE_IF_NEEDED, - ON_TOP); + final int activityType = mSupervisor.resolveActivityType(r, mOptions, task); + return parentStack.getDisplay().getOrCreateStack( + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, activityType, ON_TOP); } else { // If the parent is not in the docked stack, we check if there is docked window // and if yes, we will launch into that stack. If not, we just put the new // activity into parent's stack, because we can't find a better place. - final ActivityStack dockedStack = mSupervisor.getStack(DOCKED_STACK_ID); - if (dockedStack != null - && dockedStack.shouldBeVisible(r) == STACK_INVISIBLE) { + final ActivityStack dockedStack = mSupervisor.getDefaultDisplay().getStack( + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED); + if (dockedStack != null && !dockedStack.shouldBeVisible(r)) { // There is a docked stack, but it isn't visible, so we can't launch into that. return null; } else { @@ -2240,39 +2227,11 @@ class ActivityStarter { } } - boolean isValidLaunchStackId(int stackId, int displayId, ActivityRecord r) { - switch (stackId) { - case INVALID_STACK_ID: - case HOME_STACK_ID: - return false; - case FULLSCREEN_WORKSPACE_STACK_ID: - return true; - case FREEFORM_WORKSPACE_STACK_ID: - return r.supportsFreeform(); - case DOCKED_STACK_ID: - return r.supportsSplitScreen(); - case PINNED_STACK_ID: - return r.supportsPictureInPicture(); - case RECENTS_STACK_ID: - return r.isActivityTypeRecents(); - case ASSISTANT_STACK_ID: - return r.isActivityTypeAssistant(); - default: - if (StackId.isDynamicStack(stackId)) { - return r.canBeLaunchedOnDisplay(displayId); - } - Slog.e(TAG, "isValidLaunchStackId: Unexpected stackId=" + stackId); - return false; - } - } - - Rect getOverrideBounds(ActivityRecord r, ActivityOptions options, TaskRecord inTask) { + private Rect getOverrideBounds(ActivityRecord r, ActivityOptions options, TaskRecord inTask) { Rect newBounds = null; - if (options != null && (r.isResizeable() || (inTask != null && inTask.isResizeable()))) { - if (mSupervisor.canUseActivityOptionsLaunchBounds( - options, options.getLaunchStackId())) { - newBounds = TaskRecord.validateBounds(options.getLaunchBounds()); - } + if (mSupervisor.canUseActivityOptionsLaunchBounds(options) + && (r.isResizeable() || (inTask != null && inTask.isResizeable()))) { + newBounds = TaskRecord.validateBounds(options.getLaunchBounds()); } return newBounds; } @@ -2281,15 +2240,6 @@ class ActivityStarter { mWindowManager = wm; } - void removePendingActivityLaunchesLocked(ActivityStack stack) { - for (int palNdx = mPendingActivityLaunches.size() - 1; palNdx >= 0; --palNdx) { - PendingActivityLaunch pal = mPendingActivityLaunches.get(palNdx); - if (pal.stack == stack) { - mPendingActivityLaunches.remove(palNdx); - } - } - } - static boolean isDocumentLaunchesIntoExisting(int flags) { return (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && (flags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0; diff --git a/com/android/server/am/AppErrors.java b/com/android/server/am/AppErrors.java index 440b3d3b..fe380970 100644 --- a/com/android/server/am/AppErrors.java +++ b/com/android/server/am/AppErrors.java @@ -507,7 +507,7 @@ class AppErrors { // launching the report UI under a different user. app.errorReportReceiver = null; - for (int userId : mService.mUserController.getCurrentProfileIdsLocked()) { + for (int userId : mService.mUserController.getCurrentProfileIds()) { if (app.userId == userId) { app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver( mContext, app.info.packageName, app.info.flags); @@ -728,7 +728,7 @@ class AppErrors { boolean isBackground = (UserHandle.getAppId(proc.uid) >= Process.FIRST_APPLICATION_UID && proc.pid != MY_PID); - for (int userId : mService.mUserController.getCurrentProfileIdsLocked()) { + for (int userId : mService.mUserController.getCurrentProfileIds()) { isBackground &= (proc.userId != userId); } if (isBackground && !showBackground) { diff --git a/com/android/server/am/BatteryStatsService.java b/com/android/server/am/BatteryStatsService.java index 3105e37f..7c9cd00e 100644 --- a/com/android/server/am/BatteryStatsService.java +++ b/com/android/server/am/BatteryStatsService.java @@ -220,7 +220,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub // Shutdown the thread we made. mWorker.shutdown(); } - + public static IBatteryStats getService() { if (sService != null) { return sService; @@ -300,10 +300,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub // TODO: remove this once we figure out properly where and how // PROCESS_EVENT = 1112 - // EVENT SUBTYPE: START = 1 - // KEY_NAME: 1 + // KEY_STATE = 1 + // KEY_PACKAGE_NAME: 1002 // KEY_UID: 2 - StatsLog.writeArray(1112, 1, 1, name, 2, uid); + StatsLog.writeArray(1112, 1, 1, 1002, name, 2, uid); } } @@ -313,10 +313,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub // TODO: remove this once we figure out properly where and how // PROCESS_EVENT = 1112 - // EVENT SUBTYPE: CRASH = 2 - // KEY_NAME: 1 + // KEY_STATE = 1 + // KEY_PACKAGE_NAME: 1002 // KEY_UID: 2 - StatsLog.writeArray(1112, 2, 1, name, 2, uid); + StatsLog.writeArray(1112, 1, 2, 1002, name, 2, uid); } } @@ -550,10 +550,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub synchronized (mStats) { mStats.noteScreenStateLocked(state); // TODO: remove this once we figure out properly where and how - // SCREEN_EVENT = 1003 - // State key: 1 + // SCREEN_EVENT = 2 + // KEY_STATE: 1 // State value: state. We can change this to our own def later. - StatsLog.writeArray(1003, 1, state); + StatsLog.writeArray(2, 1, state); } if (DBG) Slog.d(TAG, "end noteScreenState"); } @@ -564,14 +564,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub mStats.noteScreenBrightnessLocked(brightness); } } - + public void noteUserActivity(int uid, int event) { enforceCallingPermission(); synchronized (mStats) { mStats.noteUserActivityLocked(uid, event); } } - + public void noteWakeUp(String reason, int reasonUid) { enforceCallingPermission(); synchronized (mStats) { @@ -611,21 +611,21 @@ public final class BatteryStatsService extends IBatteryStats.Stub mStats.notePhoneOnLocked(); } } - + public void notePhoneOff() { enforceCallingPermission(); synchronized (mStats) { mStats.notePhoneOffLocked(); } } - + public void notePhoneSignalStrength(SignalStrength signalStrength) { enforceCallingPermission(); synchronized (mStats) { mStats.notePhoneSignalStrengthLocked(signalStrength); } } - + public void notePhoneDataConnectionState(int dataType, boolean hasData) { enforceCallingPermission(); synchronized (mStats) { @@ -647,7 +647,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub mStats.noteWifiOnLocked(); } } - + public void noteWifiOff() { enforceCallingPermission(); synchronized (mStats) { @@ -806,7 +806,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub mStats.noteFullWifiLockAcquiredLocked(uid); } } - + public void noteFullWifiLockReleased(int uid) { enforceCallingPermission(); synchronized (mStats) { @@ -1043,7 +1043,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub }); }); } - + public long getAwakeTimeBattery() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BATTERY_STATS, null); @@ -1186,6 +1186,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub int flags = 0; boolean useCheckinFormat = false; + boolean toProto = false; boolean isRealCheckin = false; boolean noOutput = false; boolean writeData = false; @@ -1212,6 +1213,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub } else if ("-c".equals(arg)) { useCheckinFormat = true; flags |= BatteryStats.DUMP_INCLUDE_HISTORY; + } else if ("--proto".equals(arg)) { + toProto = true; } else if ("--charged".equals(arg)) { flags |= BatteryStats.DUMP_CHARGED_ONLY; } else if ("--daily".equals(arg)) { @@ -1304,7 +1307,45 @@ public final class BatteryStatsService extends IBatteryStats.Stub } } - if (useCheckinFormat) { + if (toProto) { + List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications( + PackageManager.MATCH_ANY_USER | PackageManager.MATCH_ALL); + if (isRealCheckin) { + // For a real checkin, first we want to prefer to use the last complete checkin + // file if there is one. + synchronized (mStats.mCheckinFile) { + if (mStats.mCheckinFile.exists()) { + try { + byte[] raw = mStats.mCheckinFile.readFully(); + if (raw != null) { + Parcel in = Parcel.obtain(); + in.unmarshall(raw, 0, raw.length); + in.setDataPosition(0); + BatteryStatsImpl checkinStats = new BatteryStatsImpl( + null, mStats.mHandler, null, mUserManagerUserInfoProvider); + checkinStats.readSummaryFromParcel(in); + in.recycle(); + checkinStats.dumpProtoLocked(mContext, fd, apps, flags, + historyStart); + mStats.mCheckinFile.delete(); + return; + } + } catch (IOException | ParcelFormatException e) { + Slog.w(TAG, "Failure reading checkin file " + + mStats.mCheckinFile.getBaseFile(), e); + } + } + } + } + if (DBG) Slog.d(TAG, "begin dumpProtoLocked from UID " + Binder.getCallingUid()); + synchronized (mStats) { + mStats.dumpProtoLocked(mContext, fd, apps, flags, historyStart); + if (writeData) { + mStats.writeAsyncLocked(); + } + } + if (DBG) Slog.d(TAG, "end dumpProtoLocked"); + } else if (useCheckinFormat) { List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications( PackageManager.MATCH_ANY_USER | PackageManager.MATCH_ALL); if (isRealCheckin) { diff --git a/com/android/server/am/KeyguardController.java b/com/android/server/am/KeyguardController.java index cea80c8d..5c48f90d 100644 --- a/com/android/server/am/KeyguardController.java +++ b/com/android/server/am/KeyguardController.java @@ -19,12 +19,15 @@ package com.android.server.am; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS; import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE; import static android.view.WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; +import static com.android.server.am.proto.KeyguardControllerProto.KEYGUARD_OCCLUDED; +import static com.android.server.am.proto.KeyguardControllerProto.KEYGUARD_SHOWING; import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; @@ -38,6 +41,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.Trace; import android.util.Slog; +import android.util.proto.ProtoOutputStream; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.server.wm.WindowManagerService; @@ -66,6 +70,7 @@ class KeyguardController { private int mBeforeUnoccludeTransit; private int mVisibilityTransactionDepth; private SleepToken mSleepToken; + private int mSecondaryDisplayShowing = INVALID_DISPLAY; KeyguardController(ActivityManagerService service, ActivityStackSupervisor stackSupervisor) { @@ -78,10 +83,12 @@ class KeyguardController { } /** - * @return true if Keyguard is showing, not going away, and not being occluded, false otherwise + * @return true if Keyguard is showing, not going away, and not being occluded on the given + * display, false otherwise */ - boolean isKeyguardShowing() { - return mKeyguardShowing && !mKeyguardGoingAway && !mOccluded; + boolean isKeyguardShowing(int displayId) { + return mKeyguardShowing && !mKeyguardGoingAway && + (displayId == DEFAULT_DISPLAY ? !mOccluded : displayId == mSecondaryDisplayShowing); } /** @@ -94,15 +101,19 @@ class KeyguardController { /** * Update the Keyguard showing state. */ - void setKeyguardShown(boolean showing) { - if (showing == mKeyguardShowing) { + void setKeyguardShown(boolean showing, int secondaryDisplayShowing) { + boolean showingChanged = showing != mKeyguardShowing; + if (!showingChanged && secondaryDisplayShowing == mSecondaryDisplayShowing) { return; } mKeyguardShowing = showing; - dismissDockedStackIfNeeded(); - if (showing) { - setKeyguardGoingAway(false); - mDismissalRequested = false; + mSecondaryDisplayShowing = secondaryDisplayShowing; + if (showingChanged) { + dismissDockedStackIfNeeded(); + if (showing) { + setKeyguardGoingAway(false); + mDismissalRequested = false; + } } mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); updateKeyguardSleepToken(); @@ -331,15 +342,19 @@ class KeyguardController { // show on top of the lock screen. In this can we want to dismiss the docked // stack since it will be complicated/risky to try to put the activity on top // of the lock screen in the right fullscreen configuration. - mStackSupervisor.moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, - mStackSupervisor.mFocusedStack.getStackId() == DOCKED_STACK_ID); + final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getSplitScreenStack(); + if (stack == null) { + return; + } + mStackSupervisor.moveTasksToFullscreenStackLocked(stack, + mStackSupervisor.mFocusedStack == stack); } } private void updateKeyguardSleepToken() { - if (mSleepToken == null && isKeyguardShowing()) { + if (mSleepToken == null && isKeyguardShowing(DEFAULT_DISPLAY)) { mSleepToken = mService.acquireSleepToken("Keyguard", DEFAULT_DISPLAY); - } else if (mSleepToken != null && !isKeyguardShowing()) { + } else if (mSleepToken != null && !isKeyguardShowing(DEFAULT_DISPLAY)) { mSleepToken.release(); mSleepToken = null; } @@ -354,4 +369,11 @@ class KeyguardController { pw.println(prefix + " mDismissalRequested=" + mDismissalRequested); pw.println(prefix + " mVisibilityTransactionDepth=" + mVisibilityTransactionDepth); } + + void writeToProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + proto.write(KEYGUARD_SHOWING, mKeyguardShowing); + proto.write(KEYGUARD_OCCLUDED, mOccluded); + proto.end(token); + } } diff --git a/com/android/server/am/LockTaskController.java b/com/android/server/am/LockTaskController.java index d8706bcc..72b5de88 100644 --- a/com/android/server/am/LockTaskController.java +++ b/com/android/server/am/LockTaskController.java @@ -25,12 +25,14 @@ import static android.app.StatusBarManager.DISABLE_HOME; import static android.app.StatusBarManager.DISABLE_MASK; import static android.app.StatusBarManager.DISABLE_NONE; import static android.app.StatusBarManager.DISABLE_RECENT; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Context.DEVICE_POLICY_SERVICE; import static android.content.Context.STATUS_BAR_SERVICE; import static android.os.UserHandle.USER_ALL; import static android.os.UserHandle.USER_CURRENT; import static android.provider.Settings.Secure.LOCK_TO_APP_EXIT_LOCKED; import static android.view.Display.DEFAULT_DISPLAY; + import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; @@ -55,7 +57,9 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; import android.util.Slog; +import android.util.SparseArray; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.widget.LockPatternUtils; @@ -65,6 +69,7 @@ import com.android.server.wm.WindowManagerService; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; /** * Helper class that deals with all things related to task locking. This includes the screen pinning @@ -123,6 +128,11 @@ public class LockTaskController { private final ArrayList<TaskRecord> mLockTaskModeTasks = new ArrayList<>(); /** + * Packages that are allowed to be launched into the lock task mode for each user. + */ + private final SparseArray<String[]> mLockTaskPackages = new SparseArray<>(); + + /** * Store the current lock task mode. Possible values: * {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActivityManager#LOCK_TASK_MODE_LOCKED}, * {@link ActivityManager#LOCK_TASK_MODE_PINNED} @@ -159,8 +169,8 @@ public class LockTaskController { } /** - * @return whether the given task can be moved to the back of the stack. Locked tasks cannot be - * moved to the back of the stack. + * @return whether the given task is locked at the moment. Locked tasks cannot be moved to the + * back of the stack. */ boolean checkLockedTask(TaskRecord task) { if (mLockTaskModeTasks.contains(task)) { @@ -422,8 +432,8 @@ public class LockTaskController { mSupervisor.resumeFocusedStackTopActivityLocked(); mWindowManager.executeAppTransition(); } else if (lockTaskModeState != LOCK_TASK_MODE_NONE) { - mSupervisor.handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, DEFAULT_DISPLAY, - task.getStackId(), true /* forceNonResizable */); + mSupervisor.handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED, + DEFAULT_DISPLAY, task.getStackId(), true /* forceNonResizable */); } } @@ -452,29 +462,35 @@ public class LockTaskController { } /** - * Called when the list of packages whitelisted for lock task mode is changed. Any currently - * locked tasks that got removed from the whitelist will be finished. + * Update packages that are allowed to be launched in lock task mode. + * @param userId Which user this whitelist is associated with + * @param packages The whitelist of packages allowed in lock task mode + * @see #mLockTaskPackages */ - // TODO: Missing unit tests - void onLockTaskPackagesUpdated() { - boolean didSomething = false; + void updateLockTaskPackages(int userId, String[] packages) { + mLockTaskPackages.put(userId, packages); + + boolean taskChanged = false; for (int taskNdx = mLockTaskModeTasks.size() - 1; taskNdx >= 0; --taskNdx) { final TaskRecord lockedTask = mLockTaskModeTasks.get(taskNdx); - final boolean wasWhitelisted = - (lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) || - (lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED); + final boolean wasWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE + || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED; lockedTask.setLockTaskAuth(); - final boolean isWhitelisted = - (lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) || - (lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED); - if (wasWhitelisted && !isWhitelisted) { - // Lost whitelisting authorization. End it now. - if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " + - lockedTask + " mLockTaskAuth()=" + lockedTask.lockTaskAuthToString()); - removeLockedTask(lockedTask); - lockedTask.performClearTaskLocked(); - didSomething = true; + final boolean isWhitelisted = lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE + || lockedTask.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED; + + if (mLockTaskModeState != LOCK_TASK_MODE_LOCKED + || lockedTask.userId != userId + || !wasWhitelisted || isWhitelisted) { + continue; } + + // Terminate locked tasks that have recently lost whitelist authorization. + if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " + + lockedTask + " mLockTaskAuth()=" + lockedTask.lockTaskAuthToString()); + removeLockedTask(lockedTask); + lockedTask.performClearTaskLocked(); + taskChanged = true; } for (int displayNdx = mSupervisor.getChildCount() - 1; displayNdx >= 0; --displayNdx) { @@ -484,22 +500,40 @@ public class LockTaskController { stack.onLockTaskPackagesUpdatedLocked(); } } + final ActivityRecord r = mSupervisor.topRunningActivityLocked(); - final TaskRecord task = r != null ? r.getTask() : null; - if (mLockTaskModeTasks.isEmpty() && task != null + final TaskRecord task = (r != null) ? r.getTask() : null; + if (mLockTaskModeTasks.isEmpty() && task!= null && task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) { // This task must have just been authorized. if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: starting new locktask task=" + task); - setLockTaskMode(task, LOCK_TASK_MODE_LOCKED, "package updated", - false); - didSomething = true; + setLockTaskMode(task, LOCK_TASK_MODE_LOCKED, "package updated", false); + taskChanged = true; } - if (didSomething) { + + if (taskChanged) { mSupervisor.resumeFocusedStackTopActivityLocked(); } } + boolean isPackageWhitelisted(int userId, String pkg) { + if (pkg == null) { + return false; + } + String[] whitelist; + whitelist = mLockTaskPackages.get(userId); + if (whitelist == null) { + return false; + } + for (String whitelistedPkg : whitelist) { + if (pkg.equals(whitelistedPkg)) { + return true; + } + } + return false; + } + /** * @return the topmost locked task */ @@ -556,8 +590,18 @@ public class LockTaskController { } public void dump(PrintWriter pw, String prefix) { - pw.print(prefix); pw.print("mLockTaskModeState=" + lockTaskModeToString()); - pw.println(" mLockTaskModeTasks" + mLockTaskModeTasks); + pw.println(prefix + "LockTaskController"); + prefix = prefix + " "; + pw.println(prefix + "mLockTaskModeState=" + lockTaskModeToString()); + pw.println(prefix + "mLockTaskModeTasks="); + for (int i = 0; i < mLockTaskModeTasks.size(); ++i) { + pw.println(prefix + " #" + i + " " + mLockTaskModeTasks.get(i)); + } + pw.println(prefix + "mLockTaskPackages (userId:packages)="); + for (int i = 0; i < mLockTaskPackages.size(); ++i) { + pw.println(prefix + " u" + mLockTaskPackages.keyAt(i) + + ":" + Arrays.toString(mLockTaskPackages.valueAt(i))); + } } private String lockTaskModeToString() { diff --git a/com/android/server/am/PendingIntentRecord.java b/com/android/server/am/PendingIntentRecord.java index ee593866..7930f534 100644 --- a/com/android/server/am/PendingIntentRecord.java +++ b/com/android/server/am/PendingIntentRecord.java @@ -308,7 +308,7 @@ final class PendingIntentRecord extends IIntentSender.Stub { boolean sendFinish = finishedReceiver != null; int userId = key.userId; if (userId == UserHandle.USER_CURRENT) { - userId = owner.mUserController.getCurrentOrTargetUserIdLocked(); + userId = owner.mUserController.getCurrentOrTargetUserId(); } int res = 0; switch (key.type) { diff --git a/com/android/server/am/PinnedActivityStack.java b/com/android/server/am/PinnedActivityStack.java index a601ee1c..27269791 100644 --- a/com/android/server/am/PinnedActivityStack.java +++ b/com/android/server/am/PinnedActivityStack.java @@ -16,6 +16,9 @@ package com.android.server.am; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; + import android.app.RemoteAction; import android.content.res.Configuration; import android.graphics.Rect; @@ -32,15 +35,16 @@ import java.util.List; class PinnedActivityStack extends ActivityStack<PinnedStackWindowController> implements PinnedStackWindowListener { - PinnedActivityStack(ActivityStackSupervisor.ActivityDisplay display, int stackId, - ActivityStackSupervisor supervisor, RecentTasks recentTasks, boolean onTop) { - super(display, stackId, supervisor, recentTasks, onTop); + PinnedActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor, + boolean onTop) { + super(display, stackId, supervisor, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, onTop); } @Override PinnedStackWindowController createStackWindowController(int displayId, boolean onTop, Rect outBounds) { - return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds); + return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds, + mStackSupervisor.mWindowManager); } Rect getDefaultPictureInPictureBounds(float aspectRatio) { diff --git a/com/android/server/am/ProcessStatsService.java b/com/android/server/am/ProcessStatsService.java index 39aed7cf..effb86c1 100644 --- a/com/android/server/am/ProcessStatsService.java +++ b/com/android/server/am/ProcessStatsService.java @@ -23,11 +23,14 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; +import android.service.procstats.ProcessStatsProto; +import android.service.procstats.ProcessStatsServiceDumpProto; import android.util.ArrayMap; import android.util.AtomicFile; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.procstats.DumpUtils; @@ -622,13 +625,17 @@ public final class ProcessStatsService extends IProcessStats.Stub { long ident = Binder.clearCallingIdentity(); try { - dumpInner(fd, pw, args); + if (args.length > 0 && "--proto".equals(args[0])) { + dumpProto(fd); + } else { + dumpInner(pw, args); + } } finally { Binder.restoreCallingIdentity(ident); } } - private void dumpInner(FileDescriptor fd, PrintWriter pw, String[] args) { + private void dumpInner(PrintWriter pw, String[] args) { final long now = SystemClock.uptimeMillis(); boolean isCheckin = false; @@ -1038,4 +1045,44 @@ public final class ProcessStatsService extends IProcessStats.Stub { } } } + + private void dumpAggregatedStats(ProtoOutputStream proto, int aggregateHours, long now) { + ParcelFileDescriptor pfd = getStatsOverTime(aggregateHours*60*60*1000 + - (ProcessStats.COMMIT_PERIOD/2)); + if (pfd == null) { + return; + } + ProcessStats stats = new ProcessStats(false); + InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd); + stats.read(stream); + if (stats.mReadError != null) { + return; + } + stats.toProto(proto, now); + } + + private void dumpProto(FileDescriptor fd) { + final ProtoOutputStream proto = new ProtoOutputStream(fd); + + // dump current procstats + long nowToken = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_NOW); + long now; + synchronized (mAm) { + now = SystemClock.uptimeMillis(); + mProcessStats.toProto(proto, now); + } + proto.end(nowToken); + + // aggregated over last 3 hours procstats + long tokenOf3Hrs = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_OVER_3HRS); + dumpAggregatedStats(proto, 3, now); + proto.end(tokenOf3Hrs); + + // aggregated over last 24 hours procstats + long tokenOf24Hrs = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_OVER_24HRS); + dumpAggregatedStats(proto, 24, now); + proto.end(tokenOf24Hrs); + + proto.flush(); + } } diff --git a/com/android/server/am/ServiceRecord.java b/com/android/server/am/ServiceRecord.java index 027dc086..ac85e6b1 100644 --- a/com/android/server/am/ServiceRecord.java +++ b/com/android/server/am/ServiceRecord.java @@ -517,11 +517,14 @@ final class ServiceRecord extends Binder { } catch (PackageManager.NameNotFoundException e) { } } - if (localForegroundNoti.getSmallIcon() == null) { + if (localForegroundNoti.getSmallIcon() == null + || nm.getNotificationChannel(localPackageName, appUid, + localForegroundNoti.getChannelId()) == 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. + // Also every notification needs a channel. throw new RuntimeException("invalid service notification: " + foregroundNoti); } diff --git a/com/android/server/am/TaskChangeNotificationController.java b/com/android/server/am/TaskChangeNotificationController.java index 82971696..5a7e7ced 100644 --- a/com/android/server/am/TaskChangeNotificationController.java +++ b/com/android/server/am/TaskChangeNotificationController.java @@ -95,7 +95,8 @@ class TaskChangeNotificationController { }; private final TaskStackConsumer mNotifyActivityPinned = (l, m) -> { - l.onActivityPinned((String) m.obj, m.arg1); + final ActivityRecord r = (ActivityRecord) m.obj; + l.onActivityPinned(r.packageName, r.userId, r.getTask().taskId, r.getStackId()); }; private final TaskStackConsumer mNotifyActivityUnpinned = (l, m) -> { @@ -278,10 +279,9 @@ class TaskChangeNotificationController { } /** Notifies all listeners when an Activity is pinned. */ - void notifyActivityPinned(String packageName, int taskId) { + void notifyActivityPinned(ActivityRecord r) { mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG); - final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG, - taskId, 0, packageName); + final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG, r); forAllLocalListeners(mNotifyActivityPinned, msg); msg.sendToTarget(); } diff --git a/com/android/server/am/TaskPersister.java b/com/android/server/am/TaskPersister.java index 74c4826f..61994b55 100644 --- a/com/android/server/am/TaskPersister.java +++ b/com/android/server/am/TaskPersister.java @@ -54,9 +54,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; -import static android.app.ActivityManager.StackId.HOME_STACK_ID; -import static android.app.ActivityManager.StackId.INVALID_STACK_ID; - import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS; public class TaskPersister { @@ -472,8 +469,7 @@ public class TaskPersister { final int taskId = task.taskId; if (mStackSupervisor.anyTaskForIdLocked(taskId, - MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, - INVALID_STACK_ID) != null) { + MATCH_TASK_IN_STACKS_OR_RECENT_TASKS) != null) { // Should not happen. Slog.wtf(TAG, "Existing task with taskId " + taskId + "found"); } else if (userId != task.userId) { diff --git a/com/android/server/am/TaskRecord.java b/com/android/server/am/TaskRecord.java index 0e651845..0d8df796 100644 --- a/com/android/server/am/TaskRecord.java +++ b/com/android/server/am/TaskRecord.java @@ -18,19 +18,19 @@ package com.android.server.am; import static android.app.ActivityManager.RESIZE_MODE_FORCED; import static android.app.ActivityManager.RESIZE_MODE_SYSTEM; -import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID; -import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; -import static android.app.ActivityManager.StackId.RECENTS_STACK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS; import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY; @@ -65,6 +65,20 @@ import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING; import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_MOVING_TO_TOP; import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY; import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; +import static com.android.server.am.proto.TaskRecordProto.ACTIVITIES; +import static com.android.server.am.proto.TaskRecordProto.BOUNDS; +import static com.android.server.am.proto.TaskRecordProto.CONFIGURATION_CONTAINER; +import static com.android.server.am.proto.TaskRecordProto.FULLSCREEN; +import static com.android.server.am.proto.TaskRecordProto.ID; +import static com.android.server.am.proto.TaskRecordProto.LAST_NON_FULLSCREEN_BOUNDS; +import static com.android.server.am.proto.TaskRecordProto.MIN_HEIGHT; +import static com.android.server.am.proto.TaskRecordProto.MIN_WIDTH; +import static com.android.server.am.proto.TaskRecordProto.ORIG_ACTIVITY; +import static com.android.server.am.proto.TaskRecordProto.REAL_ACTIVITY; +import static com.android.server.am.proto.TaskRecordProto.RESIZE_MODE; +import static com.android.server.am.proto.TaskRecordProto.RETURN_TO_TYPE; +import static com.android.server.am.proto.TaskRecordProto.STACK_ID; +import static com.android.server.am.proto.TaskRecordProto.ACTIVITY_TYPE; import static java.lang.Integer.MAX_VALUE; @@ -78,7 +92,6 @@ import android.app.ActivityManager.TaskSnapshot; import android.app.ActivityOptions; import android.app.AppGlobals; import android.app.IActivityManager; -import android.app.WindowConfiguration; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -95,6 +108,7 @@ import android.provider.Settings; import android.service.voice.IVoiceInteractionSession; import android.util.DisplayMetrics; import android.util.Slog; +import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; @@ -508,8 +522,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi updateOverrideConfiguration(bounds); if (getStackId() != FREEFORM_WORKSPACE_STACK_ID) { // re-restore the task so it can have the proper stack association. - mService.mStackSupervisor.restoreRecentTaskLocked(this, - FREEFORM_WORKSPACE_STACK_ID); + mService.mStackSupervisor.restoreRecentTaskLocked(this, null); } return true; } @@ -559,36 +572,36 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi /** * Convenience method to reparent a task to the top or bottom position of the stack. */ - boolean reparent(int preferredStackId, boolean toTop, @ReparentMoveStackMode int moveStackMode, - boolean animate, boolean deferResume, String reason) { - return reparent(preferredStackId, toTop ? MAX_VALUE : 0, moveStackMode, animate, - deferResume, true /* schedulePictureInPictureModeChange */, reason); + boolean reparent(ActivityStack preferredStack, boolean toTop, + @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume, + String reason) { + return reparent(preferredStack, toTop ? MAX_VALUE : 0, moveStackMode, animate, deferResume, + true /* schedulePictureInPictureModeChange */, reason); } /** * Convenience method to reparent a task to the top or bottom position of the stack, with * an option to skip scheduling the picture-in-picture mode change. */ - boolean reparent(int preferredStackId, boolean toTop, @ReparentMoveStackMode int moveStackMode, - boolean animate, boolean deferResume, boolean schedulePictureInPictureModeChange, - String reason) { - return reparent(preferredStackId, toTop ? MAX_VALUE : 0, moveStackMode, animate, + boolean reparent(ActivityStack preferredStack, boolean toTop, + @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume, + boolean schedulePictureInPictureModeChange, String reason) { + return reparent(preferredStack, toTop ? MAX_VALUE : 0, moveStackMode, animate, deferResume, schedulePictureInPictureModeChange, reason); } - /** - * Convenience method to reparent a task to a specific position of the stack. - */ - boolean reparent(int preferredStackId, int position, @ReparentMoveStackMode int moveStackMode, - boolean animate, boolean deferResume, String reason) { - return reparent(preferredStackId, position, moveStackMode, animate, deferResume, + /** Convenience method to reparent a task to a specific position of the stack. */ + boolean reparent(ActivityStack preferredStack, int position, + @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume, + String reason) { + return reparent(preferredStack, position, moveStackMode, animate, deferResume, true /* schedulePictureInPictureModeChange */, reason); } /** * Reparents the task into a preferred stack, creating it if necessary. * - * @param preferredStackId the stack id of the target stack to move this task + * @param preferredStack the target stack to move this task * @param position the position to place this task in the new stack * @param animate whether or not we should wait for the new window created as a part of the * reparenting to be drawn and animated in @@ -602,13 +615,16 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi * @param reason the caller of this reparenting * @return whether the task was reparented */ - boolean reparent(int preferredStackId, int position, @ReparentMoveStackMode int moveStackMode, - boolean animate, boolean deferResume, boolean schedulePictureInPictureModeChange, - String reason) { + // TODO: Inspect all call sites and change to just changing windowing mode of the stack vs. + // re-parenting the task. Can only be done when we are no longer using static stack Ids like + /** {@link ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} */ + boolean reparent(ActivityStack preferredStack, int position, + @ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume, + boolean schedulePictureInPictureModeChange, String reason) { final ActivityStackSupervisor supervisor = mService.mStackSupervisor; final WindowManagerService windowManager = mService.mWindowManager; final ActivityStack sourceStack = getStack(); - final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStackId, + final ActivityStack toStack = supervisor.getReparentTargetStack(this, preferredStack, position == MAX_VALUE); if (toStack == sourceStack) { return false; @@ -691,19 +707,22 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi toStack.prepareFreezingTaskBounds(); // Make sure the task has the appropriate bounds/size for the stack it is in. + final int toStackWindowingMode = toStack.getWindowingMode(); + final boolean toStackSplitScreenPrimary = + toStackWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && !Objects.equals(mBounds, toStack.mBounds)) { kept = resize(toStack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow, deferResume); - } else if (stackId == FREEFORM_WORKSPACE_STACK_ID) { + } else if (toStackWindowingMode == WINDOWING_MODE_FREEFORM) { Rect bounds = getLaunchBounds(); if (bounds == null) { toStack.layoutTaskInStack(this, null); bounds = mBounds; } kept = resize(bounds, RESIZE_MODE_FORCED, !mightReplaceWindow, deferResume); - } else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) { - if (stackId == DOCKED_STACK_ID && moveStackMode == REPARENT_KEEP_STACK_AT_FRONT) { + } else if (toStackSplitScreenPrimary || toStackWindowingMode == WINDOWING_MODE_PINNED) { + if (toStackSplitScreenPrimary && moveStackMode == REPARENT_KEEP_STACK_AT_FRONT) { // Move recents to front so it is not behind home stack when going into docked // mode mService.mStackSupervisor.moveRecentsStackToFront(reason); @@ -730,10 +749,12 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi } // TODO: Handle incorrect request to move before the actual move, not after. - supervisor.handleNonResizableTaskIfNeeded(this, preferredStackId, DEFAULT_DISPLAY, stackId); + final boolean inSplitScreenMode = supervisor.getDefaultDisplay().hasSplitScreenStack(); + supervisor.handleNonResizableTaskIfNeeded(this, preferredStack.getWindowingMode(), + DEFAULT_DISPLAY, stackId); - boolean successful = (preferredStackId == stackId); - if (successful && stackId == DOCKED_STACK_ID) { + boolean successful = (preferredStack == toStack); + if (successful && toStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { // If task moved to docked stack - show recents if needed. mService.mWindowManager.showRecentApps(false /* fromHome */); } @@ -857,8 +878,13 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi } mResizeMode = info.resizeMode; mSupportsPictureInPicture = info.supportsPictureInPicture(); - mLockTaskMode = info.lockTaskLaunchMode; mPrivileged = (info.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0; + mLockTaskMode = info.lockTaskLaunchMode; + if (!mPrivileged && (mLockTaskMode == LOCK_TASK_LAUNCH_MODE_ALWAYS + || mLockTaskMode == LOCK_TASK_LAUNCH_MODE_NEVER)) { + // Non-priv apps are not allowed to use always or never, fall back to default + mLockTaskMode = LOCK_TASK_LAUNCH_MODE_DEFAULT; + } setLockTaskAuth(); } @@ -921,8 +947,8 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi mNextAffiliateTaskId = nextAffiliate == null ? INVALID_TASK_ID : nextAffiliate.taskId; } - ActivityStack getStack() { - return mStack; + <T extends ActivityStack> T getStack() { + return (T) mStack; } /** @@ -1396,16 +1422,11 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi } void setLockTaskAuth() { - if (!mPrivileged && - (mLockTaskMode == LOCK_TASK_LAUNCH_MODE_ALWAYS || - mLockTaskMode == LOCK_TASK_LAUNCH_MODE_NEVER)) { - // Non-priv apps are not allowed to use always or never, fall back to default - mLockTaskMode = LOCK_TASK_LAUNCH_MODE_DEFAULT; - } + final String pkg = (realActivity != null) ? realActivity.getPackageName() : null; switch (mLockTaskMode) { case LOCK_TASK_LAUNCH_MODE_DEFAULT: - mLockTaskAuth = isLockTaskWhitelistedLocked() ? - LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE; + mLockTaskAuth = mService.mLockTaskController.isPackageWhitelisted(userId, pkg) + ? LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE; break; case LOCK_TASK_LAUNCH_MODE_NEVER: @@ -1417,31 +1438,14 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi break; case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED: - mLockTaskAuth = isLockTaskWhitelistedLocked() ? - LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE; + mLockTaskAuth = mService.mLockTaskController.isPackageWhitelisted(userId, pkg) + ? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE; break; } if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "setLockTaskAuth: task=" + this + " mLockTaskAuth=" + lockTaskAuthToString()); } - private boolean isLockTaskWhitelistedLocked() { - String pkg = (realActivity != null) ? realActivity.getPackageName() : null; - if (pkg == null) { - return false; - } - String[] packages = mService.mLockTaskPackages.get(userId); - if (packages == null) { - return false; - } - for (int i = packages.length - 1; i >= 0; --i) { - if (pkg.equals(packages[i])) { - return true; - } - } - return false; - } - boolean isOverHomeStack() { return mTaskToReturnTo == ACTIVITY_TYPE_HOME; } @@ -1459,10 +1463,12 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi return isResizeable(true /* checkSupportsPip */); } - boolean supportsSplitScreen() { + @Override + public boolean supportsSplitScreenWindowingMode() { // A task can not be docked even if it is considered resizeable because it only supports // picture-in-picture mode but has a non-resizeable resizeMode - return mService.mSupportsSplitScreenMultiWindow + return super.supportsSplitScreenWindowingMode() + && mService.mSupportsSplitScreenMultiWindow && (mService.mForceResizableActivities || (isResizeable(false /* checkSupportsPip */) && !ActivityInfo.isPreserveOrientationMode(mResizeMode))); @@ -2097,39 +2103,16 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi } } - /** - * Returns the correct stack to use based on task type and currently set bounds, - * regardless of the focused stack and current stack association of the task. - * The task will be moved (and stack focus changed) later if necessary. - */ - int getLaunchStackId() { - if (isActivityTypeRecents()) { - return RECENTS_STACK_ID; - } - if (isActivityTypeHome()) { - return HOME_STACK_ID; - } - if (isActivityTypeAssistant()) { - return ASSISTANT_STACK_ID; - } - if (mBounds != null) { - return FREEFORM_WORKSPACE_STACK_ID; - } - return FULLSCREEN_WORKSPACE_STACK_ID; - } - /** Returns the bounds that should be used to launch this task. */ private Rect getLaunchBounds() { if (mStack == null) { return null; } - final int stackId = mStack.mStackId; - if (stackId == HOME_STACK_ID - || stackId == RECENTS_STACK_ID - || stackId == ASSISTANT_STACK_ID - || stackId == FULLSCREEN_WORKSPACE_STACK_ID - || (stackId == DOCKED_STACK_ID && !isResizeable())) { + final int windowingMode = getWindowingMode(); + if (!isActivityTypeStandardOrUndefined() + || windowingMode == WINDOWING_MODE_FULLSCREEN + || (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && !isResizeable())) { return isResizeable() ? mStack.mBounds : null; } else if (!getWindowConfiguration().persistTaskBounds()) { return mStack.mBounds; @@ -2275,4 +2258,34 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi stringName = sb.toString(); return toString(); } + + public void writeToProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + super.writeToProto(proto, CONFIGURATION_CONTAINER); + proto.write(ID, taskId); + for (int i = mActivities.size() - 1; i >= 0; i--) { + ActivityRecord activity = mActivities.get(i); + activity.writeToProto(proto, ACTIVITIES); + } + proto.write(STACK_ID, mStack.mStackId); + if (mLastNonFullscreenBounds != null) { + mLastNonFullscreenBounds.writeToProto(proto, LAST_NON_FULLSCREEN_BOUNDS); + } + if (realActivity != null) { + proto.write(REAL_ACTIVITY, realActivity.flattenToShortString()); + } + if (origActivity != null) { + proto.write(ORIG_ACTIVITY, origActivity.flattenToShortString()); + } + proto.write(ACTIVITY_TYPE, getActivityType()); + proto.write(RETURN_TO_TYPE, mTaskToReturnTo); + proto.write(RESIZE_MODE, mResizeMode); + proto.write(FULLSCREEN, mFullscreen); + if (mBounds != null) { + mBounds.writeToProto(proto, BOUNDS); + } + proto.write(MIN_WIDTH, mMinWidth); + proto.write(MIN_HEIGHT, mMinHeight); + proto.end(token); + } } diff --git a/com/android/server/am/UserController.java b/com/android/server/am/UserController.java index db6bb7d8..5a295942 100644 --- a/com/android/server/am/UserController.java +++ b/com/android/server/am/UserController.java @@ -22,6 +22,7 @@ import static android.app.ActivityManager.USER_OP_ERROR_IS_SYSTEM; import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP; import static android.app.ActivityManager.USER_OP_IS_CURRENT; import static android.app.ActivityManager.USER_OP_SUCCESS; +import static android.os.Process.SHELL_UID; 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; @@ -30,14 +31,6 @@ import static com.android.server.am.ActivityManagerService.ALLOW_FULL_ONLY; import static com.android.server.am.ActivityManagerService.ALLOW_NON_FULL; import static com.android.server.am.ActivityManagerService.ALLOW_NON_FULL_IN_PROFILE; import static com.android.server.am.ActivityManagerService.MY_PID; -import static com.android.server.am.ActivityManagerService.REPORT_LOCKED_BOOT_COMPLETE_MSG; -import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_COMPLETE_MSG; -import static com.android.server.am.ActivityManagerService.REPORT_USER_SWITCH_MSG; -import static com.android.server.am.ActivityManagerService.SYSTEM_USER_CURRENT_MSG; -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_CALLBACKS_TIMEOUT_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; @@ -68,6 +61,7 @@ import android.os.IBinder; import android.os.IProgressListener; import android.os.IRemoteCallback; import android.os.IUserManager; +import android.os.Message; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -78,6 +72,7 @@ import android.os.UserManager; import android.os.UserManagerInternal; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; +import android.text.format.DateUtils; import android.util.ArraySet; import android.util.IntArray; import android.util.Pair; @@ -93,6 +88,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; import com.android.internal.widget.LockPatternUtils; import com.android.server.LocalServices; +import com.android.server.SystemServiceManager; import com.android.server.pm.UserManagerService; import com.android.server.wm.WindowManagerService; @@ -107,25 +103,64 @@ import java.util.concurrent.atomic.AtomicInteger; /** * Helper class for {@link ActivityManagerService} responsible for multi-user functionality. + * + * <p>This class use {@link #mLock} to synchronize access to internal state. Methods that require + * {@link #mLock} to be held should have "LU" suffix in the name. + * + * <p><strong>Important:</strong> Synchronized code, i.e. one executed inside a synchronized(mLock) + * block or inside LU method, should only access internal state of this class or make calls to + * other LU methods. Non-LU method calls or calls to external classes are discouraged as they + * may cause lock inversion. */ -class UserController { +class UserController implements Handler.Callback { private static final String TAG = TAG_WITH_CLASS_NAME ? "UserController" : TAG_AM; - // Maximum number of users we allow to be running at a time. + /** + * Maximum number of users we allow to be running at a time, including the system user and + * its profiles. + * Note changing this to 2 is not recommended, since that would mean, if the user uses + * work profile and then switch to a secondary user, then the work profile user would be killed, + * which should work fine, but aggressively killing the work profile user that has just been + * running could cause data loss. (Even without work profile, witching from secondary user A + * to secondary user B would cause similar issues on user B.) + * + * TODO: Consider adding or replacing with "MAX_RUNNING_*SECONDARY*_USERS", which is the max + * number of running *secondary, switchable* users. + */ static final int MAX_RUNNING_USERS = 3; // Amount of time we wait for observers to handle a user switch before // giving up on them and unfreezing the screen. static final int USER_SWITCH_TIMEOUT_MS = 3 * 1000; + // ActivityManager thread message constants + static final int REPORT_USER_SWITCH_MSG = 10; + static final int CONTINUE_USER_SWITCH_MSG = 20; + static final int USER_SWITCH_TIMEOUT_MSG = 30; + static final int START_PROFILES_MSG = 40; + static final int SYSTEM_USER_START_MSG = 50; + static final int SYSTEM_USER_CURRENT_MSG = 60; + static final int FOREGROUND_PROFILE_CHANGED_MSG = 70; + static final int REPORT_USER_SWITCH_COMPLETE_MSG = 80; + static final int USER_SWITCH_CALLBACKS_TIMEOUT_MSG = 90; + static final int SYSTEM_USER_UNLOCK_MSG = 100; + static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 110; + static final int START_USER_SWITCH_FG_MSG = 120; + + // UI thread message constants + static final int START_USER_SWITCH_UI_MSG = 1000; + // If a callback wasn't called within USER_SWITCH_CALLBACKS_TIMEOUT_MS after // USER_SWITCH_TIMEOUT_MS, an error is reported. Usually it indicates a problem in the observer // when it never calls back. private static final int USER_SWITCH_CALLBACKS_TIMEOUT_MS = 5 * 1000; - private final Object mLock; + // Lock for internal state. + private final Object mLock = new Object(); + private final Injector mInjector; private final Handler mHandler; + private final Handler mUiHandler; // Holds the current foreground user's id. Use mLock when updating @GuardedBy("mLock") @@ -161,7 +196,8 @@ class UserController { /** * Mapping from each known user ID to the profile group ID it is associated with. */ - private final SparseIntArray mUserProfileGroupIdsSelfLocked = new SparseIntArray(); + @GuardedBy("mLock") + private final SparseIntArray mUserProfileGroupIds = new SparseIntArray(); /** * Registered observers of the user switching mechanics. @@ -192,26 +228,25 @@ class UserController { @VisibleForTesting UserController(Injector injector) { mInjector = injector; - mLock = injector.getLock(); - mHandler = injector.getHandler(); + mHandler = mInjector.getHandler(this); + mUiHandler = mInjector.getUiHandler(this); // User 0 is the first and only user that runs at boot. final UserState uss = new UserState(UserHandle.SYSTEM); mStartedUsers.put(UserHandle.USER_SYSTEM, uss); mUserLru.add(UserHandle.USER_SYSTEM); mLockPatternUtils = mInjector.getLockPatternUtils(); - updateStartedUserArrayLocked(); + updateStartedUserArrayLU(); } void finishUserSwitch(UserState uss) { + finishUserBoot(uss); + startProfiles(); synchronized (mLock) { - finishUserBoot(uss); - - startProfilesLocked(); - stopRunningUsersLocked(MAX_RUNNING_USERS); + stopRunningUsersLU(MAX_RUNNING_USERS); } } - void stopRunningUsersLocked(int maxRunningUsers) { + void stopRunningUsersLU(int maxRunningUsers) { int num = mUserLru.size(); int i = 0; while (num > maxRunningUsers && i < mUserLru.size()) { @@ -240,7 +275,7 @@ class UserController { continue; } // This is a user to be stopped. - if (stopUsersLocked(oldUserId, false, null) != USER_OP_SUCCESS) { + if (stopUsersLU(oldUserId, false, null) != USER_OP_SUCCESS) { num--; } num--; @@ -258,55 +293,57 @@ class UserController { Slog.d(TAG, "Finishing user boot " + userId); synchronized (mLock) { // Bail if we ended up with a stale user - if (mStartedUsers.get(userId) != uss) return; + if (mStartedUsers.get(userId) != uss) { + return; + } + } - // We always walk through all the user lifecycle states to send - // 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.setState(STATE_BOOTING, STATE_RUNNING_LOCKED)) { - mInjector.getUserManagerInternal().setUserState(userId, uss.state); - // Do not report secondary users, runtime restarts or first boot/upgrade - if (userId == UserHandle.USER_SYSTEM - && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) { - int uptimeSeconds = (int)(SystemClock.elapsedRealtime() / 1000); - MetricsLogger.histogram(mInjector.getContext(), - "framework_locked_boot_completed", uptimeSeconds); - final int MAX_UPTIME_SECONDS = 120; - if (uptimeSeconds > MAX_UPTIME_SECONDS) { - Slog.wtf("SystemServerTiming", - "finishUserBoot took too long. uptimeSeconds=" + uptimeSeconds); - } + // We always walk through all the user lifecycle states to send + // 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.setState(STATE_BOOTING, STATE_RUNNING_LOCKED)) { + mInjector.getUserManagerInternal().setUserState(userId, uss.state); + // Do not report secondary users, runtime restarts or first boot/upgrade + if (userId == UserHandle.USER_SYSTEM + && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) { + int uptimeSeconds = (int)(SystemClock.elapsedRealtime() / 1000); + MetricsLogger.histogram(mInjector.getContext(), + "framework_locked_boot_completed", uptimeSeconds); + final int MAX_UPTIME_SECONDS = 120; + if (uptimeSeconds > MAX_UPTIME_SECONDS) { + Slog.wtf("SystemServerTiming", + "finishUserBoot took too long. uptimeSeconds=" + uptimeSeconds); } + } - mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG, - userId, 0)); - Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null); - intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT - | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); - mInjector.broadcastIntentLocked(intent, null, resultTo, 0, null, null, - new String[] { android.Manifest.permission.RECEIVE_BOOT_COMPLETED }, - AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId); - } - - // We need to delay unlocking managed profiles until the parent user - // is also unlocked. - if (mInjector.getUserManager().isManagedProfile(userId)) { - final UserInfo parent = mInjector.getUserManager().getProfileParent(userId); - if (parent != null - && isUserRunningLocked(parent.id, ActivityManager.FLAG_AND_UNLOCKED)) { - Slog.d(TAG, "User " + userId + " (parent " + parent.id - + "): attempting unlock because parent is unlocked"); - maybeUnlockUser(userId); - } else { - String parentId = (parent == null) ? "<null>" : String.valueOf(parent.id); - Slog.d(TAG, "User " + userId + " (parent " + parentId - + "): delaying unlock because parent is locked"); - } - } else { + mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG, + userId, 0)); + Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null); + intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT + | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + mInjector.broadcastIntent(intent, null, resultTo, 0, null, null, + new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED}, + AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId); + } + + // We need to delay unlocking managed profiles until the parent user + // is also unlocked. + if (mInjector.getUserManager().isManagedProfile(userId)) { + final UserInfo parent = mInjector.getUserManager().getProfileParent(userId); + if (parent != null + && isUserRunning(parent.id, ActivityManager.FLAG_AND_UNLOCKED)) { + Slog.d(TAG, "User " + userId + " (parent " + parent.id + + "): attempting unlock because parent is unlocked"); maybeUnlockUser(userId); + } else { + String parentId = (parent == null) ? "<null>" : String.valueOf(parent.id); + Slog.d(TAG, "User " + userId + " (parent " + parentId + + "): delaying unlock because parent is locked"); } + } else { + maybeUnlockUser(userId); } } @@ -316,34 +353,30 @@ class UserController { */ private void finishUserUnlocking(final UserState uss) { final int userId = uss.mHandle.getIdentifier(); - boolean proceedWithUnlock = false; + // Only keep marching forward if user is actually unlocked + if (!StorageManager.isUserKeyUnlocked(userId)) return; synchronized (mLock) { // 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 (!StorageManager.isUserKeyUnlocked(userId)) return; - - if (uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) { - mInjector.getUserManagerInternal().setUserState(userId, uss.state); - proceedWithUnlock = true; + // Do not proceed if unexpected state + if (!uss.setState(STATE_RUNNING_LOCKED, STATE_RUNNING_UNLOCKING)) { + return; } } + mInjector.getUserManagerInternal().setUserState(userId, uss.state); + uss.mUnlockProgress.start(); - if (proceedWithUnlock) { - uss.mUnlockProgress.start(); - - // Prepare app storage before we go any further - uss.mUnlockProgress.setProgress(5, - mInjector.getContext().getString(R.string.android_start_title)); - mInjector.getUserManager().onBeforeUnlockUser(userId); - uss.mUnlockProgress.setProgress(20); + // Prepare app storage before we go any further + uss.mUnlockProgress.setProgress(5, + mInjector.getContext().getString(R.string.android_start_title)); + mInjector.getUserManager().onBeforeUnlockUser(userId); + uss.mUnlockProgress.setProgress(20); - // Dispatch unlocked to system services; when fully dispatched, - // that calls through to the next "unlocked" phase - mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss) - .sendToTarget(); - } + // Dispatch unlocked to system services; when fully dispatched, + // that calls through to the next "unlocked" phase + mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0, uss) + .sendToTarget(); } /** @@ -352,63 +385,63 @@ class UserController { */ void finishUserUnlocked(final UserState uss) { final int userId = uss.mHandle.getIdentifier(); + // Only keep marching forward if user is actually unlocked + if (!StorageManager.isUserKeyUnlocked(userId)) return; synchronized (mLock) { // 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 (!StorageManager.isUserKeyUnlocked(userId)) return; - - if (uss.setState(STATE_RUNNING_UNLOCKING, STATE_RUNNING_UNLOCKED)) { - mInjector.getUserManagerInternal().setUserState(userId, uss.state); - uss.mUnlockProgress.finish(); - - // Dispatch unlocked to external apps - final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED); - unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - unlockedIntent.addFlags( - Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); - mInjector.broadcastIntentLocked(unlockedIntent, null, null, 0, null, - null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, - userId); - - if (getUserInfo(userId).isManagedProfile()) { - UserInfo parent = mInjector.getUserManager().getProfileParent(userId); - if (parent != null) { - final Intent profileUnlockedIntent = new Intent( - Intent.ACTION_MANAGED_PROFILE_UNLOCKED); - profileUnlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)); - profileUnlockedIntent.addFlags( - Intent.FLAG_RECEIVER_REGISTERED_ONLY + // Do not proceed if unexpected state + if (!uss.setState(STATE_RUNNING_UNLOCKING, STATE_RUNNING_UNLOCKED)) { + return; + } + } + mInjector.getUserManagerInternal().setUserState(userId, uss.state); + uss.mUnlockProgress.finish(); + // Dispatch unlocked to external apps + final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED); + unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + unlockedIntent.addFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); + mInjector.broadcastIntent(unlockedIntent, null, null, 0, null, + null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, + userId); + + if (getUserInfo(userId).isManagedProfile()) { + UserInfo parent = mInjector.getUserManager().getProfileParent(userId); + if (parent != null) { + final Intent profileUnlockedIntent = new Intent( + Intent.ACTION_MANAGED_PROFILE_UNLOCKED); + profileUnlockedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(userId)); + profileUnlockedIntent.addFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); - mInjector.broadcastIntentLocked(profileUnlockedIntent, - null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, - parent.id); - } - } + mInjector.broadcastIntent(profileUnlockedIntent, + null, null, 0, null, null, null, AppOpsManager.OP_NONE, + null, false, false, MY_PID, SYSTEM_UID, + parent.id); + } + } - // Send PRE_BOOT broadcasts if user fingerprint changed; we - // purposefully block sending BOOT_COMPLETED until after all - // PRE_BOOT receivers are finished to avoid ANR'ing apps - final UserInfo info = getUserInfo(userId); - if (!Objects.equals(info.lastLoggedInFingerprint, Build.FINGERPRINT)) { - // Suppress double notifications for managed profiles that - // were unlocked automatically as part of their parent user - // being unlocked. - final boolean quiet; - if (info.isManagedProfile()) { - quiet = !uss.tokenProvided - || !mLockPatternUtils.isSeparateProfileChallengeEnabled(userId); - } else { - quiet = false; - } - mInjector.sendPreBootBroadcast(userId, quiet, - () -> finishUserUnlockedCompleted(uss)); - } else { - finishUserUnlockedCompleted(uss); - } + // Send PRE_BOOT broadcasts if user fingerprint changed; we + // purposefully block sending BOOT_COMPLETED until after all + // PRE_BOOT receivers are finished to avoid ANR'ing apps + final UserInfo info = getUserInfo(userId); + if (!Objects.equals(info.lastLoggedInFingerprint, Build.FINGERPRINT)) { + // Suppress double notifications for managed profiles that + // were unlocked automatically as part of their parent user + // being unlocked. + final boolean quiet; + if (info.isManagedProfile()) { + quiet = !uss.tokenProvided + || !mLockPatternUtils.isSeparateProfileChallengeEnabled(userId); + } else { + quiet = false; } + mInjector.sendPreBootBroadcast(userId, quiet, + () -> finishUserUnlockedCompleted(uss)); + } else { + finishUserUnlockedCompleted(uss); } } @@ -417,60 +450,59 @@ class UserController { synchronized (mLock) { // Bail if we ended up with a stale user if (mStartedUsers.get(uss.mHandle.getIdentifier()) != uss) return; - final UserInfo userInfo = getUserInfo(userId); - if (userInfo == null) { - return; - } + } + UserInfo userInfo = getUserInfo(userId); + if (userInfo == null) { + return; + } + // Only keep marching forward if user is actually unlocked + if (!StorageManager.isUserKeyUnlocked(userId)) return; - // Only keep marching forward if user is actually unlocked - if (!StorageManager.isUserKeyUnlocked(userId)) return; - - // Remember that we logged in - mInjector.getUserManager().onUserLoggedIn(userId); - - if (!userInfo.isInitialized()) { - if (userId != UserHandle.USER_SYSTEM) { - Slog.d(TAG, "Initializing user #" + userId); - Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE); - intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND - | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); - mInjector.broadcastIntentLocked(intent, null, - new IIntentReceiver.Stub() { - @Override - public void performReceive(Intent intent, int resultCode, - String data, Bundle extras, boolean ordered, - boolean sticky, int sendingUser) { - // Note: performReceive is called with mService lock held - mInjector.getUserManager().makeInitialized(userInfo.id); - } - }, 0, null, null, null, AppOpsManager.OP_NONE, - null, true, false, MY_PID, SYSTEM_UID, userId); - } - } + // Remember that we logged in + mInjector.getUserManager().onUserLoggedIn(userId); - Slog.i(TAG, "Sending BOOT_COMPLETE user #" + userId); - // Do not report secondary users, runtime restarts or first boot/upgrade - if (userId == UserHandle.USER_SYSTEM - && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) { - int uptimeSeconds = (int) (SystemClock.elapsedRealtime() / 1000); - MetricsLogger.histogram(mInjector.getContext(), "framework_boot_completed", - uptimeSeconds); + if (!userInfo.isInitialized()) { + if (userId != UserHandle.USER_SYSTEM) { + Slog.d(TAG, "Initializing user #" + userId); + Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE); + intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND + | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + mInjector.broadcastIntent(intent, null, + new IIntentReceiver.Stub() { + @Override + public void performReceive(Intent intent, int resultCode, + String data, Bundle extras, boolean ordered, + boolean sticky, int sendingUser) { + // Note: performReceive is called with mService lock held + mInjector.getUserManager().makeInitialized(userInfo.id); + } + }, 0, null, null, null, AppOpsManager.OP_NONE, + null, true, false, MY_PID, SYSTEM_UID, userId); } - final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null); - bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT - | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); - mInjector.broadcastIntentLocked(bootIntent, null, new IIntentReceiver.Stub() { - @Override - public void performReceive(Intent intent, int resultCode, String data, - Bundle extras, boolean ordered, boolean sticky, int sendingUser) - throws RemoteException { - Slog.i(UserController.TAG, "Finished processing BOOT_COMPLETED for u" + userId); - } - }, 0, null, null, - new String[] { android.Manifest.permission.RECEIVE_BOOT_COMPLETED }, - AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId); } + + Slog.i(TAG, "Sending BOOT_COMPLETE user #" + userId); + // Do not report secondary users, runtime restarts or first boot/upgrade + if (userId == UserHandle.USER_SYSTEM + && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) { + int uptimeSeconds = (int) (SystemClock.elapsedRealtime() / 1000); + MetricsLogger.histogram(mInjector.getContext(), "framework_boot_completed", + uptimeSeconds); + } + final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null); + bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT + | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + mInjector.broadcastIntent(bootIntent, null, new IIntentReceiver.Stub() { + @Override + public void performReceive(Intent intent, int resultCode, String data, + Bundle extras, boolean ordered, boolean sticky, int sendingUser) + throws RemoteException { + Slog.i(UserController.TAG, "Finished processing BOOT_COMPLETED for u" + userId); + } + }, 0, null, null, + new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED}, + AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId); } int restartUser(final int userId, final boolean foreground) { @@ -499,35 +531,35 @@ class UserController { if (userId < 0 || userId == UserHandle.USER_SYSTEM) { throw new IllegalArgumentException("Can't stop system user " + userId); } - mInjector.enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId); + enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, userId); synchronized (mLock) { - return stopUsersLocked(userId, force, callback); + return stopUsersLU(userId, force, callback); } } /** * Stops the user along with its related users. The method calls - * {@link #getUsersToStopLocked(int)} to determine the list of users that should be stopped. + * {@link #getUsersToStopLU(int)} to determine the list of users that should be stopped. */ - private int stopUsersLocked(final int userId, boolean force, final IStopUserCallback callback) { + private int stopUsersLU(final int userId, boolean force, final IStopUserCallback callback) { if (userId == UserHandle.USER_SYSTEM) { return USER_OP_ERROR_IS_SYSTEM; } - if (isCurrentUserLocked(userId)) { + if (isCurrentUserLU(userId)) { return USER_OP_IS_CURRENT; } - int[] usersToStop = getUsersToStopLocked(userId); + int[] usersToStop = getUsersToStopLU(userId); // If one of related users is system or current, no related users should be stopped for (int i = 0; i < usersToStop.length; i++) { int relatedUserId = usersToStop[i]; - if ((UserHandle.USER_SYSTEM == relatedUserId) || isCurrentUserLocked(relatedUserId)) { + if ((UserHandle.USER_SYSTEM == relatedUserId) || isCurrentUserLU(relatedUserId)) { if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked cannot stop related user " + relatedUserId); // We still need to stop the requested user if it's a force stop. if (force) { Slog.i(TAG, "Force stop user " + userId + ". Related users will not be stopped"); - stopSingleUserLocked(userId, callback); + stopSingleUserLU(userId, callback); return USER_OP_SUCCESS; } return USER_OP_ERROR_RELATED_USERS_CANNOT_STOP; @@ -535,25 +567,22 @@ class UserController { } if (DEBUG_MU) Slog.i(TAG, "stopUsersLocked usersToStop=" + Arrays.toString(usersToStop)); for (int userIdToStop : usersToStop) { - stopSingleUserLocked(userIdToStop, userIdToStop == userId ? callback : null); + stopSingleUserLU(userIdToStop, userIdToStop == userId ? callback : null); } return USER_OP_SUCCESS; } - private void stopSingleUserLocked(final int userId, final IStopUserCallback callback) { + private void stopSingleUserLU(final int userId, final IStopUserCallback callback) { if (DEBUG_MU) Slog.i(TAG, "stopSingleUserLocked userId=" + userId); final UserState uss = mStartedUsers.get(userId); if (uss == null) { // User is not started, nothing to do... but we do need to // callback if requested. if (callback != null) { - mHandler.post(new Runnable() { - @Override - public void run() { - try { - callback.userStopped(userId); - } catch (RemoteException e) { - } + mHandler.post(() -> { + try { + callback.userStopped(userId); + } catch (RemoteException e) { } }); } @@ -568,10 +597,10 @@ class UserController { && uss.state != UserState.STATE_SHUTDOWN) { uss.setState(UserState.STATE_STOPPING); mInjector.getUserManagerInternal().setUserState(userId, uss.state); - updateStartedUserArrayLocked(); + updateStartedUserArrayLU(); - long ident = Binder.clearCallingIdentity(); - try { + // Post to handler to obtain amLock + mHandler.post(() -> { // We are going to broadcast ACTION_USER_STOPPING and then // once that is done send a final ACTION_SHUTDOWN and then // stop the user. @@ -584,31 +613,24 @@ class UserController { @Override public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) { - mHandler.post(new Runnable() { - @Override - public void run() { - finishUserStopping(userId, uss); - } - }); + mHandler.post(() -> finishUserStopping(userId, uss)); } }; + // Clear broadcast queue for the user to avoid delivering stale broadcasts - mInjector.clearBroadcastQueueForUserLocked(userId); + mInjector.clearBroadcastQueueForUser(userId); // Kick things off. - mInjector.broadcastIntentLocked(stoppingIntent, + mInjector.broadcastIntent(stoppingIntent, null, stoppingReceiver, 0, null, null, new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); - } finally { - Binder.restoreCallingIdentity(ident); - } + }); } } void finishUserStopping(final int userId, final UserState uss) { // On to the next. final Intent shutdownIntent = new Intent(Intent.ACTION_SHUTDOWN); - shutdownIntent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); // This is the result receiver for the final shutdown broadcast. final IIntentReceiver shutdownReceiver = new IIntentReceiver.Stub() { @Override @@ -635,20 +657,19 @@ class UserController { mInjector.batteryStatsServiceNoteEvent( BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH, Integer.toString(userId), userId); - mInjector.systemServiceManagerStopUser(userId); + mInjector.getSystemServiceManager().stopUser(userId); - synchronized (mLock) { - mInjector.broadcastIntentLocked(shutdownIntent, - null, shutdownReceiver, 0, null, null, null, - AppOpsManager.OP_NONE, - null, true, false, MY_PID, SYSTEM_UID, userId); - } + mInjector.broadcastIntent(shutdownIntent, + null, shutdownReceiver, 0, null, null, null, + AppOpsManager.OP_NONE, + null, true, false, MY_PID, SYSTEM_UID, userId); } void finishUserStopped(UserState uss) { final int userId = uss.mHandle.getIdentifier(); boolean stopped; ArrayList<IStopUserCallback> callbacks; + boolean forceStopUser = false; synchronized (mLock) { callbacks = new ArrayList<>(uss.mStopCallbacks); if (mStartedUsers.get(userId) != uss) { @@ -659,16 +680,18 @@ class UserController { stopped = true; // User can no longer run. mStartedUsers.remove(userId); - mInjector.getUserManagerInternal().removeUserState(userId); mUserLru.remove(Integer.valueOf(userId)); - updateStartedUserArrayLocked(); - - mInjector.activityManagerOnUserStopped(userId); - // Clean up all state and processes associated with the user. - // Kill all the processes for the user. - forceStopUserLocked(userId, "finish user"); + updateStartedUserArrayLU(); + forceStopUser = true; } } + if (forceStopUser) { + mInjector.getUserManagerInternal().removeUserState(userId); + mInjector.activityManagerOnUserStopped(userId); + // Clean up all state and processes associated with the user. + // Kill all the processes for the user. + forceStopUser(userId, "finish user"); + } for (int i = 0; i < callbacks.size(); i++) { try { @@ -680,9 +703,7 @@ class UserController { if (stopped) { mInjector.systemServiceManagerCleanupUser(userId); - synchronized (mLock) { - mInjector.getActivityStackSupervisor().removeUserLocked(userId); - } + mInjector.stackSupervisorRemoveUser(userId); // Remove the user if it is ephemeral. if (getUserInfo(userId).isEphemeral()) { mInjector.getUserManager().removeUser(userId); @@ -700,39 +721,36 @@ class UserController { * Determines the list of users that should be stopped together with the specified * {@code userId}. The returned list includes {@code userId}. */ - private @NonNull int[] getUsersToStopLocked(int userId) { + private @NonNull int[] getUsersToStopLU(int userId) { int startedUsersSize = mStartedUsers.size(); IntArray userIds = new IntArray(); userIds.add(userId); - synchronized (mUserProfileGroupIdsSelfLocked) { - int userGroupId = mUserProfileGroupIdsSelfLocked.get(userId, + int userGroupId = mUserProfileGroupIds.get(userId, UserInfo.NO_PROFILE_GROUP_ID); + for (int i = 0; i < startedUsersSize; i++) { + UserState uss = mStartedUsers.valueAt(i); + int startedUserId = uss.mHandle.getIdentifier(); + // Skip unrelated users (profileGroupId mismatch) + int startedUserGroupId = mUserProfileGroupIds.get(startedUserId, UserInfo.NO_PROFILE_GROUP_ID); - for (int i = 0; i < startedUsersSize; i++) { - UserState uss = mStartedUsers.valueAt(i); - int startedUserId = uss.mHandle.getIdentifier(); - // Skip unrelated users (profileGroupId mismatch) - int startedUserGroupId = mUserProfileGroupIdsSelfLocked.get(startedUserId, - UserInfo.NO_PROFILE_GROUP_ID); - boolean sameGroup = (userGroupId != UserInfo.NO_PROFILE_GROUP_ID) - && (userGroupId == startedUserGroupId); - // userId has already been added - boolean sameUserId = startedUserId == userId; - if (!sameGroup || sameUserId) { - continue; - } - userIds.add(startedUserId); + boolean sameGroup = (userGroupId != UserInfo.NO_PROFILE_GROUP_ID) + && (userGroupId == startedUserGroupId); + // userId has already been added + boolean sameUserId = startedUserId == userId; + if (!sameGroup || sameUserId) { + continue; } + userIds.add(startedUserId); } return userIds.toArray(); } - private void forceStopUserLocked(int userId, String reason) { - mInjector.activityManagerForceStopPackageLocked(userId, reason); + private void forceStopUser(int userId, String reason) { + mInjector.activityManagerForceStopPackage(userId, reason); Intent intent = new Intent(Intent.ACTION_USER_STOPPED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - mInjector.broadcastIntentLocked(intent, + mInjector.broadcastIntent(intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); } @@ -741,6 +759,7 @@ class UserController { * Stops the guest or ephemeral user if it has gone to the background. */ private void stopGuestOrEphemeralUserIfBackground() { + IntArray userIds = new IntArray(); synchronized (mLock) { final int num = mUserLru.size(); for (int i = 0; i < num; i++) { @@ -751,28 +770,42 @@ class UserController { || oldUss.state == UserState.STATE_SHUTDOWN) { continue; } - UserInfo userInfo = getUserInfo(oldUserId); - if (userInfo.isEphemeral()) { - LocalServices.getService(UserManagerInternal.class) - .onEphemeralUserStop(oldUserId); - } - if (userInfo.isGuest() || userInfo.isEphemeral()) { - // This is a user to be stopped. - stopUsersLocked(oldUserId, true, null); - break; + userIds.add(oldUserId); + } + } + final int userIdsSize = userIds.size(); + for (int i = 0; i < userIdsSize; i++) { + int oldUserId = userIds.get(i); + UserInfo userInfo = getUserInfo(oldUserId); + if (userInfo.isEphemeral()) { + LocalServices.getService(UserManagerInternal.class).onEphemeralUserStop(oldUserId); + } + if (userInfo.isGuest() || userInfo.isEphemeral()) { + // This is a user to be stopped. + synchronized (mLock) { + stopUsersLU(oldUserId, true, null); } + break; } } } - void startProfilesLocked() { + void scheduleStartProfiles() { + if (!mHandler.hasMessages(START_PROFILES_MSG)) { + mHandler.sendMessageDelayed(mHandler.obtainMessage(START_PROFILES_MSG), + DateUtils.SECOND_IN_MILLIS); + } + } + + void startProfiles() { + int currentUserId = getCurrentUserId(); if (DEBUG_MU) Slog.i(TAG, "startProfilesLocked"); List<UserInfo> profiles = mInjector.getUserManager().getProfiles( - mCurrentUserId, false /* enabledOnly */); + currentUserId, false /* enabledOnly */); List<UserInfo> profilesToStart = new ArrayList<>(profiles.size()); for (UserInfo user : profiles) { if ((user.flags & UserInfo.FLAG_INITIALIZED) == UserInfo.FLAG_INITIALIZED - && user.id != mCurrentUserId && !user.isQuietModeEnabled()) { + && user.id != currentUserId && !user.isQuietModeEnabled()) { profilesToStart.add(user); } } @@ -834,143 +867,156 @@ class UserController { final long ident = Binder.clearCallingIdentity(); try { - synchronized (mLock) { - final int oldUserId = mCurrentUserId; - if (oldUserId == userId) { - return true; - } + final int oldUserId = getCurrentUserId(); + if (oldUserId == userId) { + return true; + } - if (foreground) { - // TODO: I don't think this does what the caller think it does. Seems to only - // remove one locked task and won't work if multiple locked tasks are present. - mInjector.getLockTaskController().clearLockTaskMode("startUser"); - } + if (foreground) { + // TODO: I don't think this does what the caller think it does. Seems to only + // remove one locked task and won't work if multiple locked tasks are present. + mInjector.clearLockTaskMode("startUser"); + } - final UserInfo userInfo = getUserInfo(userId); - if (userInfo == null) { - Slog.w(TAG, "No user info for user #" + userId); - return false; - } - if (foreground && userInfo.isManagedProfile()) { - Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user"); - return false; - } + final UserInfo userInfo = getUserInfo(userId); + if (userInfo == null) { + Slog.w(TAG, "No user info for user #" + userId); + return false; + } + if (foreground && userInfo.isManagedProfile()) { + Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user"); + return false; + } - if (foreground && mUserSwitchUiEnabled) { - mInjector.getWindowManager().startFreezingScreen( - R.anim.screen_user_exit, R.anim.screen_user_enter); - } + if (foreground && mUserSwitchUiEnabled) { + mInjector.getWindowManager().startFreezingScreen( + R.anim.screen_user_exit, R.anim.screen_user_enter); + } - boolean needStart = false; + boolean needStart = false; + boolean updateUmState = false; + UserState uss; - // If the user we are switching to is not currently started, then - // we need to start it now. - if (mStartedUsers.get(userId) == null) { - UserState userState = new UserState(UserHandle.of(userId)); - mStartedUsers.put(userId, userState); - mInjector.getUserManagerInternal().setUserState(userId, userState.state); - updateStartedUserArrayLocked(); + // If the user we are switching to is not currently started, then + // we need to start it now. + synchronized (mLock) { + uss = mStartedUsers.get(userId); + if (uss == null) { + uss = new UserState(UserHandle.of(userId)); + mStartedUsers.put(userId, uss); + updateStartedUserArrayLU(); needStart = true; + updateUmState = true; } - - final UserState uss = mStartedUsers.get(userId); final Integer userIdInt = userId; mUserLru.remove(userIdInt); mUserLru.add(userIdInt); - - if (foreground) { + } + if (updateUmState) { + mInjector.getUserManagerInternal().setUserState(userId, uss.state); + } + if (foreground) { + synchronized (mLock) { mCurrentUserId = userId; - mInjector.updateUserConfigurationLocked(); mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up - updateCurrentProfileIdsLocked(); - mInjector.getWindowManager().setCurrentUser(userId, mCurrentProfileIds); - // Once the internal notion of the active user has switched, we lock the device - // with the option to show the user switcher on the keyguard. - if (mUserSwitchUiEnabled) { - mInjector.getWindowManager().setSwitchingUser(true); - mInjector.getWindowManager().lockNow(null); - } - } else { - final Integer currentUserIdInt = mCurrentUserId; - updateCurrentProfileIdsLocked(); - mInjector.getWindowManager().setCurrentProfileIds(mCurrentProfileIds); + } + mInjector.updateUserConfiguration(); + updateCurrentProfileIds(); + mInjector.getWindowManager().setCurrentUser(userId, getCurrentProfileIds()); + // Once the internal notion of the active user has switched, we lock the device + // with the option to show the user switcher on the keyguard. + if (mUserSwitchUiEnabled) { + mInjector.getWindowManager().setSwitchingUser(true); + mInjector.getWindowManager().lockNow(null); + } + } else { + final Integer currentUserIdInt = mCurrentUserId; + updateCurrentProfileIds(); + mInjector.getWindowManager().setCurrentProfileIds(getCurrentProfileIds()); + synchronized (mLock) { mUserLru.remove(currentUserIdInt); mUserLru.add(currentUserIdInt); } + } - // Make sure user is in the started state. If it is currently - // stopping, we need to knock that off. - if (uss.state == UserState.STATE_STOPPING) { - // If we are stopping, we haven't sent ACTION_SHUTDOWN, - // so we can just fairly silently bring the user back from - // the almost-dead. - uss.setState(uss.lastState); - mInjector.getUserManagerInternal().setUserState(userId, uss.state); - updateStartedUserArrayLocked(); - needStart = true; - } else if (uss.state == UserState.STATE_SHUTDOWN) { - // This means ACTION_SHUTDOWN has been sent, so we will - // need to treat this as a new boot of the user. - uss.setState(UserState.STATE_BOOTING); - mInjector.getUserManagerInternal().setUserState(userId, uss.state); - updateStartedUserArrayLocked(); - needStart = true; + // Make sure user is in the started state. If it is currently + // stopping, we need to knock that off. + if (uss.state == UserState.STATE_STOPPING) { + // If we are stopping, we haven't sent ACTION_SHUTDOWN, + // so we can just fairly silently bring the user back from + // the almost-dead. + uss.setState(uss.lastState); + mInjector.getUserManagerInternal().setUserState(userId, uss.state); + synchronized (mLock) { + updateStartedUserArrayLU(); } + needStart = true; + } else if (uss.state == UserState.STATE_SHUTDOWN) { + // This means ACTION_SHUTDOWN has been sent, so we will + // need to treat this as a new boot of the user. + uss.setState(UserState.STATE_BOOTING); + mInjector.getUserManagerInternal().setUserState(userId, uss.state); + synchronized (mLock) { + updateStartedUserArrayLU(); + } + needStart = true; + } - if (uss.state == UserState.STATE_BOOTING) { - // Give user manager a chance to propagate user restrictions - // to other services and prepare app storage - mInjector.getUserManager().onBeforeStartUser(userId); + if (uss.state == UserState.STATE_BOOTING) { + // Give user manager a chance to propagate user restrictions + // to other services and prepare app storage + mInjector.getUserManager().onBeforeStartUser(userId); - // Booting up a new user, need to tell system services about it. - // Note that this is on the same handler as scheduling of broadcasts, - // which is important because it needs to go first. - mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0)); - } + // Booting up a new user, need to tell system services about it. + // Note that this is on the same handler as scheduling of broadcasts, + // which is important because it needs to go first. + mHandler.sendMessage( + mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId, 0)); + } - if (foreground) { - mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId, - oldUserId)); - mHandler.removeMessages(REPORT_USER_SWITCH_MSG); - mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG); - mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG, - oldUserId, userId, uss)); - mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG, - oldUserId, userId, uss), USER_SWITCH_TIMEOUT_MS); - } + if (foreground) { + mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId, + oldUserId)); + mHandler.removeMessages(REPORT_USER_SWITCH_MSG); + mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG); + mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG, + oldUserId, userId, uss)); + mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_TIMEOUT_MSG, + oldUserId, userId, uss), USER_SWITCH_TIMEOUT_MS); + } - if (needStart) { - // Send USER_STARTED broadcast - Intent intent = new Intent(Intent.ACTION_USER_STARTED); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY - | Intent.FLAG_RECEIVER_FOREGROUND); - intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - mInjector.broadcastIntentLocked(intent, - null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, userId); - } + if (needStart) { + // Send USER_STARTED broadcast + Intent intent = new Intent(Intent.ACTION_USER_STARTED); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY + | Intent.FLAG_RECEIVER_FOREGROUND); + intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + mInjector.broadcastIntent(intent, + null, null, 0, null, null, null, AppOpsManager.OP_NONE, + null, false, false, MY_PID, SYSTEM_UID, userId); + } - if (foreground) { - moveUserToForegroundLocked(uss, oldUserId, userId); - } else { - finishUserBoot(uss); - } + if (foreground) { + moveUserToForeground(uss, oldUserId, userId); + } else { + finishUserBoot(uss); + } - if (needStart) { - Intent intent = new Intent(Intent.ACTION_USER_STARTING); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); - mInjector.broadcastIntentLocked(intent, - null, new IIntentReceiver.Stub() { - @Override - public void performReceive(Intent intent, int resultCode, - String data, Bundle extras, boolean ordered, boolean sticky, - int sendingUser) throws RemoteException { - } - }, 0, null, null, - new String[] {INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE, - null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); - } + if (needStart) { + Intent intent = new Intent(Intent.ACTION_USER_STARTING); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + mInjector.broadcastIntent(intent, + null, new IIntentReceiver.Stub() { + @Override + public void performReceive(Intent intent, int resultCode, + String data, Bundle extras, boolean ordered, + boolean sticky, + int sendingUser) throws RemoteException { + } + }, 0, null, null, + new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE, + null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); } } finally { Binder.restoreCallingIdentity(ident); @@ -1014,7 +1060,7 @@ class UserController { * when the the credential-encrypted storage isn't tied to a user-provided * PIN or pattern. */ - boolean maybeUnlockUser(final int userId) { + private boolean maybeUnlockUser(final int userId) { // Try unlocking storage using empty token return unlockUserCleared(userId, null, null, null); } @@ -1027,65 +1073,106 @@ class UserController { } } - boolean unlockUserCleared(final int userId, byte[] token, byte[] secret, + private boolean unlockUserCleared(final int userId, byte[] token, byte[] secret, IProgressListener listener) { UserState uss; - synchronized (mLock) { - // TODO Move this block outside of synchronized if it causes lock contention - if (!StorageManager.isUserKeyUnlocked(userId)) { - final UserInfo userInfo = getUserInfo(userId); - final IStorageManager storageManager = getStorageManager(); - try { - // We always want to unlock user storage, even user is not started yet - storageManager.unlockUserKey(userId, userInfo.serialNumber, token, secret); - } catch (RemoteException | RuntimeException e) { - Slog.w(TAG, "Failed to unlock: " + e.getMessage()); - } + if (!StorageManager.isUserKeyUnlocked(userId)) { + final UserInfo userInfo = getUserInfo(userId); + final IStorageManager storageManager = getStorageManager(); + try { + // We always want to unlock user storage, even user is not started yet + storageManager.unlockUserKey(userId, userInfo.serialNumber, token, secret); + } catch (RemoteException | RuntimeException e) { + Slog.w(TAG, "Failed to unlock: " + e.getMessage()); } - // Bail if user isn't actually running, otherwise register the given - // listener to watch for unlock progress + } + synchronized (mLock) { + // Register the given listener to watch for unlock progress uss = mStartedUsers.get(userId); - if (uss == null) { - notifyFinished(userId, listener); - return false; - } else { + if (uss != null) { uss.mUnlockProgress.addListener(listener); uss.tokenProvided = (token != null); } } + // Bail if user isn't actually running + if (uss == null) { + notifyFinished(userId, listener); + return false; + } finishUserUnlocking(uss); - final ArraySet<Integer> childProfilesToUnlock = new ArraySet<>(); - synchronized (mLock) { + // We just unlocked a user, so let's now attempt to unlock any + // managed profiles under that user. - // We just unlocked a user, so let's now attempt to unlock any - // managed profiles under that user. - for (int i = 0; i < mStartedUsers.size(); i++) { - final int testUserId = mStartedUsers.keyAt(i); - final UserInfo parent = mInjector.getUserManager().getProfileParent(testUserId); - if (parent != null && parent.id == userId && testUserId != userId) { - Slog.d(TAG, "User " + testUserId + " (parent " + parent.id - + "): attempting unlock because parent was just unlocked"); - childProfilesToUnlock.add(testUserId); - } + // First, get list of userIds. Requires mLock, so we cannot make external calls, e.g. to UMS + int[] userIds; + synchronized (mLock) { + userIds = new int[mStartedUsers.size()]; + for (int i = 0; i < userIds.length; i++) { + userIds[i] = mStartedUsers.keyAt(i); } } - - final int size = childProfilesToUnlock.size(); - for (int i = 0; i < size; i++) { - maybeUnlockUser(childProfilesToUnlock.valueAt(i)); + for (int testUserId : userIds) { + final UserInfo parent = mInjector.getUserManager().getProfileParent(testUserId); + if (parent != null && parent.id == userId && testUserId != userId) { + Slog.d(TAG, "User " + testUserId + " (parent " + parent.id + + "): attempting unlock because parent was just unlocked"); + maybeUnlockUser(testUserId); + } } return true; } - void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) { + boolean switchUser(final int targetUserId) { + enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, targetUserId); + int currentUserId = getCurrentUserId(); + UserInfo targetUserInfo = getUserInfo(targetUserId); + if (targetUserId == currentUserId) { + Slog.i(TAG, "user #" + targetUserId + " is already the current user"); + return true; + } + if (targetUserInfo == null) { + Slog.w(TAG, "No user info for user #" + targetUserId); + return false; + } + if (!targetUserInfo.isDemo() && UserManager.isDeviceInDemoMode(mInjector.getContext())) { + Slog.w(TAG, "Cannot switch to non-demo user #" + targetUserId + + " when device is in demo mode"); + return false; + } + if (!targetUserInfo.supportsSwitchTo()) { + Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported"); + return false; + } + if (targetUserInfo.isManagedProfile()) { + Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user"); + return false; + } + synchronized (mLock) { + mTargetUserId = targetUserId; + } + if (mUserSwitchUiEnabled) { + UserInfo currentUserInfo = getUserInfo(currentUserId); + Pair<UserInfo, UserInfo> userNames = new Pair<>(currentUserInfo, targetUserInfo); + mUiHandler.removeMessages(START_USER_SWITCH_UI_MSG); + mUiHandler.sendMessage(mHandler.obtainMessage( + START_USER_SWITCH_UI_MSG, userNames)); + } else { + mHandler.removeMessages(START_USER_SWITCH_FG_MSG); + mHandler.sendMessage(mHandler.obtainMessage( + START_USER_SWITCH_FG_MSG, targetUserId, 0)); + } + return true; + } + + private void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) { // The dialog will show and then initiate the user switch by calling startUserInForeground mInjector.showUserSwitchingDialog(fromToUserPair.first, fromToUserPair.second); } - void dispatchForegroundProfileChanged(int userId) { + private void dispatchForegroundProfileChanged(int userId) { final int observerCount = mUserSwitchObservers.beginBroadcast(); for (int i = 0; i < observerCount; i++) { try { @@ -1110,7 +1197,7 @@ class UserController { mUserSwitchObservers.finishBroadcast(); } - void dispatchLockedBootComplete(int userId) { + private void dispatchLockedBootComplete(int userId) { final int observerCount = mUserSwitchObservers.beginBroadcast(); for (int i = 0; i < observerCount; i++) { try { @@ -1136,23 +1223,23 @@ class UserController { synchronized (mLock) { if (DEBUG_MU) Slog.i(TAG, "stopBackgroundUsersIfEnforced stopping " + oldUserId + " and related users"); - stopUsersLocked(oldUserId, false, null); + stopUsersLU(oldUserId, false, null); } } - void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) { + private void timeoutUserSwitch(UserState uss, int oldUserId, int newUserId) { synchronized (mLock) { Slog.e(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId); mTimeoutUserSwitchCallbacks = mCurWaitingUserSwitchCallbacks; mHandler.removeMessages(USER_SWITCH_CALLBACKS_TIMEOUT_MSG); - sendContinueUserSwitchLocked(uss, oldUserId, newUserId); + sendContinueUserSwitchLU(uss, oldUserId, newUserId); // Report observers that never called back (USER_SWITCH_CALLBACKS_TIMEOUT) mHandler.sendMessageDelayed(mHandler.obtainMessage(USER_SWITCH_CALLBACKS_TIMEOUT_MSG, oldUserId, newUserId), USER_SWITCH_CALLBACKS_TIMEOUT_MS); } } - void timeoutUserSwitchCallbacks(int oldUserId, int newUserId) { + private void timeoutUserSwitchCallbacks(int oldUserId, int newUserId) { synchronized (mLock) { if (mTimeoutUserSwitchCallbacks != null && !mTimeoutUserSwitchCallbacks.isEmpty()) { Slog.wtf(TAG, "User switch timeout: from " + oldUserId + " to " + newUserId @@ -1195,7 +1282,7 @@ class UserController { if (waitingCallbacksCount.decrementAndGet() == 0 && (curWaitingUserSwitchCallbacks == mCurWaitingUserSwitchCallbacks)) { - sendContinueUserSwitchLocked(uss, oldUserId, newUserId); + sendContinueUserSwitchLU(uss, oldUserId, newUserId); } } } @@ -1206,25 +1293,23 @@ class UserController { } } else { synchronized (mLock) { - sendContinueUserSwitchLocked(uss, oldUserId, newUserId); + sendContinueUserSwitchLU(uss, oldUserId, newUserId); } } mUserSwitchObservers.finishBroadcast(); } - void sendContinueUserSwitchLocked(UserState uss, int oldUserId, int newUserId) { + void sendContinueUserSwitchLU(UserState uss, int oldUserId, int newUserId) { mCurWaitingUserSwitchCallbacks = null; mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG); - mHandler.sendMessage(mHandler.obtainMessage(ActivityManagerService.CONTINUE_USER_SWITCH_MSG, + mHandler.sendMessage(mHandler.obtainMessage(CONTINUE_USER_SWITCH_MSG, oldUserId, newUserId, uss)); } void continueUserSwitch(UserState uss, int oldUserId, int newUserId) { Slog.d(TAG, "Continue user switch oldUser #" + oldUserId + ", newUser #" + newUserId); if (mUserSwitchUiEnabled) { - synchronized (mLock) { - mInjector.getWindowManager().stopFreezingScreen(); - } + mInjector.getWindowManager().stopFreezingScreen(); } uss.switching = false; mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG); @@ -1234,19 +1319,18 @@ class UserController { stopBackgroundUsersIfEnforced(oldUserId); } - void moveUserToForegroundLocked(UserState uss, int oldUserId, int newUserId) { - boolean homeInFront = - mInjector.getActivityStackSupervisor().switchUserLocked(newUserId, uss); + private void moveUserToForeground(UserState uss, int oldUserId, int newUserId) { + boolean homeInFront = mInjector.stackSupervisorSwitchUser(newUserId, uss); if (homeInFront) { - mInjector.startHomeActivityLocked(newUserId, "moveUserToForeground"); + mInjector.startHomeActivity(newUserId, "moveUserToForeground"); } else { - mInjector.getActivityStackSupervisor().resumeFocusedStackTopActivityLocked(); + mInjector.stackSupervisorResumeFocusedStackTopActivity(); } EventLogTags.writeAmSwitchUser(newUserId); - sendUserSwitchBroadcastsLocked(oldUserId, newUserId); + sendUserSwitchBroadcasts(oldUserId, newUserId); } - void sendUserSwitchBroadcastsLocked(int oldUserId, int newUserId) { + void sendUserSwitchBroadcasts(int oldUserId, int newUserId) { long ident = Binder.clearCallingIdentity(); try { Intent intent; @@ -1260,7 +1344,7 @@ class UserController { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId); - mInjector.broadcastIntentLocked(intent, + mInjector.broadcastIntent(intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, profileUserId); } @@ -1275,7 +1359,7 @@ class UserController { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId); - mInjector.broadcastIntentLocked(intent, + mInjector.broadcastIntent(intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, profileUserId); } @@ -1283,7 +1367,7 @@ class UserController { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId); - mInjector.broadcastIntentLocked(intent, + mInjector.broadcastIntent(intent, null, null, 0, null, null, new String[] {android.Manifest.permission.MANAGE_USERS}, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, @@ -1308,7 +1392,7 @@ class UserController { // the value the caller will receive and someone else changing it. // We assume that USER_CURRENT_OR_SELF will use the current user; later // we will switch to the calling user if access to the current user fails. - int targetUserId = unsafeConvertIncomingUserLocked(userId); + int targetUserId = unsafeConvertIncomingUser(userId); if (callingUid != 0 && callingUid != SYSTEM_UID) { final boolean allow; @@ -1376,9 +1460,9 @@ class UserController { return targetUserId; } - int unsafeConvertIncomingUserLocked(int userId) { + int unsafeConvertIncomingUser(int userId) { return (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF) - ? getCurrentUserIdLocked(): userId; + ? getCurrentUserId(): userId; } void registerUserSwitchObserver(IUserSwitchObserver observer, String name) { @@ -1395,19 +1479,26 @@ class UserController { mUserSwitchObservers.register(observer, name); } + void sendForegroundProfileChanged(int userId) { + mHandler.removeMessages(FOREGROUND_PROFILE_CHANGED_MSG); + mHandler.obtainMessage(FOREGROUND_PROFILE_CHANGED_MSG, userId, 0).sendToTarget(); + } + void unregisterUserSwitchObserver(IUserSwitchObserver observer) { mUserSwitchObservers.unregister(observer); } - UserState getStartedUserStateLocked(int userId) { - return mStartedUsers.get(userId); + UserState getStartedUserState(int userId) { + synchronized (mLock) { + return mStartedUsers.get(userId); + } } boolean hasStartedUserState(int userId) { return mStartedUsers.get(userId) != null; } - private void updateStartedUserArrayLocked() { + private void updateStartedUserArrayLU() { int num = 0; for (int i = 0; i < mStartedUsers.size(); i++) { UserState uss = mStartedUsers.valueAt(i); @@ -1428,15 +1519,20 @@ class UserController { } } - void sendBootCompletedLocked(IIntentReceiver resultTo) { - for (int i = 0; i < mStartedUsers.size(); i++) { - UserState uss = mStartedUsers.valueAt(i); + void sendBootCompleted(IIntentReceiver resultTo) { + // Get a copy of mStartedUsers to use outside of lock + SparseArray<UserState> startedUsers; + synchronized (mLock) { + startedUsers = mStartedUsers.clone(); + } + for (int i = 0; i < startedUsers.size(); i++) { + UserState uss = startedUsers.valueAt(i); finishUserBoot(uss, resultTo); } } void onSystemReady() { - updateCurrentProfileIdsLocked(); + updateCurrentProfileIds(); } /** @@ -1444,33 +1540,35 @@ class UserController { * user switch happens or when a new related user is started in the * background. */ - private void updateCurrentProfileIdsLocked() { - final List<UserInfo> profiles = mInjector.getUserManager().getProfiles(mCurrentUserId, + private void updateCurrentProfileIds() { + final List<UserInfo> profiles = mInjector.getUserManager().getProfiles(getCurrentUserId(), false /* enabledOnly */); int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null for (int i = 0; i < currentProfileIds.length; i++) { currentProfileIds[i] = profiles.get(i).id; } - mCurrentProfileIds = currentProfileIds; + final List<UserInfo> users = mInjector.getUserManager().getUsers(false); + synchronized (mLock) { + mCurrentProfileIds = currentProfileIds; - synchronized (mUserProfileGroupIdsSelfLocked) { - mUserProfileGroupIdsSelfLocked.clear(); - final List<UserInfo> users = mInjector.getUserManager().getUsers(false); + mUserProfileGroupIds.clear(); for (int i = 0; i < users.size(); i++) { UserInfo user = users.get(i); if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) { - mUserProfileGroupIdsSelfLocked.put(user.id, user.profileGroupId); + mUserProfileGroupIds.put(user.id, user.profileGroupId); } } } } - int[] getStartedUserArrayLocked() { - return mStartedUserArray; + int[] getStartedUserArray() { + synchronized (mLock) { + return mStartedUserArray; + } } - boolean isUserRunningLocked(int userId, int flags) { - UserState state = getStartedUserStateLocked(userId); + boolean isUserRunning(int userId, int flags) { + UserState state = getStartedUserState(userId); if (state == null) { return false; } @@ -1533,29 +1631,38 @@ class UserController { return getUserInfo(mCurrentUserId); } synchronized (mLock) { - return getCurrentUserLocked(); + return getCurrentUserLU(); } } - UserInfo getCurrentUserLocked() { + UserInfo getCurrentUserLU() { int userId = mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId; return getUserInfo(userId); } - int getCurrentOrTargetUserIdLocked() { + int getCurrentOrTargetUserId() { + synchronized (mLock) { + return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId; + } + } + + int getCurrentOrTargetUserIdLU() { return mTargetUserId != UserHandle.USER_NULL ? mTargetUserId : mCurrentUserId; } - int getCurrentUserIdLocked() { + + int getCurrentUserIdLU() { return mCurrentUserId; } - private boolean isCurrentUserLocked(int userId) { - return userId == getCurrentOrTargetUserIdLocked(); + int getCurrentUserId() { + synchronized (mLock) { + return mCurrentUserId; + } } - int setTargetUserIdLocked(int targetUserId) { - return mTargetUserId = targetUserId; + private boolean isCurrentUserLU(int userId) { + return userId == getCurrentOrTargetUserIdLU(); } int[] getUsers() { @@ -1575,6 +1682,15 @@ class UserController { return mInjector.getUserManager().exists(userId); } + void enforceShellRestriction(String restriction, int userHandle) { + if (Binder.getCallingUid() == SHELL_UID) { + if (userHandle < 0 || hasUserRestriction(restriction, userHandle)) { + throw new SecurityException("Shell does not have permission to access user " + + userHandle); + } + } + } + boolean hasUserRestriction(String restriction, int userId) { return mInjector.getUserManager().hasUserRestriction(restriction, userId); } @@ -1593,22 +1709,26 @@ class UserController { if (callingUserId == targetUserId) { return true; } - synchronized (mUserProfileGroupIdsSelfLocked) { - int callingProfile = mUserProfileGroupIdsSelfLocked.get(callingUserId, + synchronized (mLock) { + int callingProfile = mUserProfileGroupIds.get(callingUserId, UserInfo.NO_PROFILE_GROUP_ID); - int targetProfile = mUserProfileGroupIdsSelfLocked.get(targetUserId, + int targetProfile = mUserProfileGroupIds.get(targetUserId, UserInfo.NO_PROFILE_GROUP_ID); return callingProfile != UserInfo.NO_PROFILE_GROUP_ID && callingProfile == targetProfile; } } - boolean isCurrentProfileLocked(int userId) { - return ArrayUtils.contains(mCurrentProfileIds, userId); + boolean isCurrentProfile(int userId) { + synchronized (mLock) { + return ArrayUtils.contains(mCurrentProfileIds, userId); + } } - int[] getCurrentProfileIdsLocked() { - return mCurrentProfileIds; + int[] getCurrentProfileIds() { + synchronized (mLock) { + return mCurrentProfileIds; + } } /** @@ -1633,40 +1753,107 @@ class UserController { } void dump(PrintWriter pw, boolean dumpAll) { - pw.println(" mStartedUsers:"); - for (int i = 0; i < mStartedUsers.size(); i++) { - UserState uss = mStartedUsers.valueAt(i); - pw.print(" User #"); pw.print(uss.mHandle.getIdentifier()); - pw.print(": "); uss.dump("", pw); - } - pw.print(" mStartedUserArray: ["); - for (int i = 0; i < mStartedUserArray.length; i++) { - if (i > 0) pw.print(", "); - pw.print(mStartedUserArray[i]); - } - pw.println("]"); - pw.print(" mUserLru: ["); - for (int i = 0; i < mUserLru.size(); i++) { - if (i > 0) pw.print(", "); - pw.print(mUserLru.get(i)); - } - pw.println("]"); - if (dumpAll) { - pw.print(" mStartedUserArray: "); pw.println(Arrays.toString(mStartedUserArray)); - } - synchronized (mUserProfileGroupIdsSelfLocked) { - if (mUserProfileGroupIdsSelfLocked.size() > 0) { + synchronized (mLock) { + pw.println(" mStartedUsers:"); + for (int i = 0; i < mStartedUsers.size(); i++) { + UserState uss = mStartedUsers.valueAt(i); + pw.print(" User #"); + pw.print(uss.mHandle.getIdentifier()); + pw.print(": "); + uss.dump("", pw); + } + pw.print(" mStartedUserArray: ["); + for (int i = 0; i < mStartedUserArray.length; i++) { + if (i > 0) + pw.print(", "); + pw.print(mStartedUserArray[i]); + } + pw.println("]"); + pw.print(" mUserLru: ["); + for (int i = 0; i < mUserLru.size(); i++) { + if (i > 0) + pw.print(", "); + pw.print(mUserLru.get(i)); + } + pw.println("]"); + if (dumpAll) { + pw.print(" mStartedUserArray: "); + pw.println(Arrays.toString(mStartedUserArray)); + } + if (mUserProfileGroupIds.size() > 0) { pw.println(" mUserProfileGroupIds:"); - for (int i=0; i<mUserProfileGroupIdsSelfLocked.size(); i++) { + for (int i=0; i< mUserProfileGroupIds.size(); i++) { pw.print(" User #"); - pw.print(mUserProfileGroupIdsSelfLocked.keyAt(i)); + pw.print(mUserProfileGroupIds.keyAt(i)); pw.print(" -> profile #"); - pw.println(mUserProfileGroupIdsSelfLocked.valueAt(i)); + pw.println(mUserProfileGroupIds.valueAt(i)); } } } } + public boolean handleMessage(Message msg) { + switch (msg.what) { + case START_USER_SWITCH_FG_MSG: + startUserInForeground(msg.arg1); + break; + case REPORT_USER_SWITCH_MSG: + dispatchUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2); + break; + case CONTINUE_USER_SWITCH_MSG: + continueUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2); + break; + case USER_SWITCH_TIMEOUT_MSG: + timeoutUserSwitch((UserState) msg.obj, msg.arg1, msg.arg2); + break; + case USER_SWITCH_CALLBACKS_TIMEOUT_MSG: + timeoutUserSwitchCallbacks(msg.arg1, msg.arg2); + break; + case START_PROFILES_MSG: + startProfiles(); + break; + case SYSTEM_USER_START_MSG: + mInjector.batteryStatsServiceNoteEvent( + BatteryStats.HistoryItem.EVENT_USER_RUNNING_START, + Integer.toString(msg.arg1), msg.arg1); + mInjector.getSystemServiceManager().startUser(msg.arg1); + break; + case SYSTEM_USER_UNLOCK_MSG: + final int userId = msg.arg1; + mInjector.getSystemServiceManager().unlockUser(userId); + mInjector.loadUserRecents(userId); + if (userId == UserHandle.USER_SYSTEM) { + mInjector.startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_UNAWARE); + } + mInjector.installEncryptionUnawareProviders(userId); + finishUserUnlocked((UserState) msg.obj); + break; + case SYSTEM_USER_CURRENT_MSG: + mInjector.batteryStatsServiceNoteEvent( + BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_FINISH, + Integer.toString(msg.arg2), msg.arg2); + mInjector.batteryStatsServiceNoteEvent( + BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START, + Integer.toString(msg.arg1), msg.arg1); + + mInjector.getSystemServiceManager().switchUser(msg.arg1); + break; + case FOREGROUND_PROFILE_CHANGED_MSG: + dispatchForegroundProfileChanged(msg.arg1); + break; + case REPORT_USER_SWITCH_COMPLETE_MSG: + dispatchUserSwitchComplete(msg.arg1); + break; + case REPORT_LOCKED_BOOT_COMPLETE_MSG: + dispatchLockedBootComplete(msg.arg1); + break; + case START_USER_SWITCH_UI_MSG: + showUserSwitchDialog((Pair<UserInfo, UserInfo>) msg.obj); + break; + } + return false; + } + @VisibleForTesting static class Injector { private final ActivityManagerService mService; @@ -1677,12 +1864,12 @@ class UserController { mService = service; } - protected Object getLock() { - return mService; + protected Handler getHandler(Handler.Callback callback) { + return new Handler(mService.mHandlerThread.getLooper(), callback); } - protected Handler getHandler() { - return mService.mHandler; + protected Handler getUiHandler(Handler.Callback callback) { + return new Handler(mService.mUiHandler.getLooper(), callback); } protected Context getContext() { @@ -1693,13 +1880,16 @@ class UserController { return new LockPatternUtils(getContext()); } - protected int broadcastIntentLocked(Intent intent, String resolvedType, + protected int broadcastIntent(Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) { - return mService.broadcastIntentLocked(null, null, intent, resolvedType, resultTo, - resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, - ordered, sticky, callingPid, callingUid, userId); + // TODO b/64165549 Verify that mLock is not held before calling AMS methods + synchronized (mService) { + return mService.broadcastIntentLocked(null, null, intent, resolvedType, resultTo, + resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, + ordered, sticky, callingPid, callingUid, userId); + } } int checkCallingPermission(String permission) { @@ -1710,7 +1900,9 @@ class UserController { return mService.mWindowManager; } void activityManagerOnUserStopped(int userId) { - mService.onUserStoppedLocked(userId); + synchronized (mService) { + mService.onUserStoppedLocked(userId); + } } void systemServiceManagerCleanupUser(int userId) { @@ -1740,14 +1932,14 @@ class UserController { mService.mBatteryStatsService.noteEvent(code, name, uid); } - void systemServiceManagerStopUser(int userId) { - mService.mSystemServiceManager.stopUser(userId); - } - boolean isRuntimeRestarted() { return mService.mSystemServiceManager.isRuntimeRestarted(); } + SystemServiceManager getSystemServiceManager() { + return mService.mSystemServiceManager; + } + boolean isFirstBootOrUpgrade() { IPackageManager pm = AppGlobals.getPackageManager(); try { @@ -1766,9 +1958,11 @@ class UserController { }.sendNext(); } - void activityManagerForceStopPackageLocked(int userId, String reason) { - mService.forceStopPackageLocked(null, -1, false, false, true, false, false, - userId, reason); + void activityManagerForceStopPackage(int userId, String reason) { + synchronized (mService) { + mService.forceStopPackageLocked(null, -1, false, false, true, false, false, + userId, reason); + } }; int checkComponentPermission(String permission, int pid, int uid, int owningUid, @@ -1776,20 +1970,36 @@ class UserController { return mService.checkComponentPermission(permission, pid, uid, owningUid, exported); } - void startHomeActivityLocked(int userId, String reason) { - mService.startHomeActivityLocked(userId, reason); + protected void startHomeActivity(int userId, String reason) { + synchronized (mService) { + mService.startHomeActivityLocked(userId, reason); + } + } + + void updateUserConfiguration() { + synchronized (mService) { + mService.updateUserConfigurationLocked(); + } } - void updateUserConfigurationLocked() { - mService.updateUserConfigurationLocked(); + void clearBroadcastQueueForUser(int userId) { + synchronized (mService) { + mService.clearBroadcastQueueForUserLocked(userId); + } + } + + void loadUserRecents(int userId) { + synchronized (mService) { + mService.mRecentTasks.loadUserRecentsLocked(userId); + } } - void clearBroadcastQueueForUserLocked(int userId) { - mService.clearBroadcastQueueForUserLocked(userId); + void startPersistentApps(int matchFlags) { + mService.startPersistentApps(matchFlags); } - void enforceShellRestriction(String restriction, int userId) { - mService.enforceShellRestriction(restriction, userId); + void installEncryptionUnawareProviders(int userId) { + mService.installEncryptionUnawareProviders(userId); } void showUserSwitchingDialog(UserInfo fromUser, UserInfo toUser) { @@ -1798,12 +2008,28 @@ class UserController { d.show(); } - ActivityStackSupervisor getActivityStackSupervisor() { - return mService.mStackSupervisor; + void stackSupervisorRemoveUser(int userId) { + synchronized (mService) { + mService.mStackSupervisor.removeUserLocked(userId); + } + } + + protected boolean stackSupervisorSwitchUser(int userId, UserState uss) { + synchronized (mService) { + return mService.mStackSupervisor.switchUserLocked(userId, uss); + } } - LockTaskController getLockTaskController() { - return mService.mLockTaskController; + protected void stackSupervisorResumeFocusedStackTopActivity() { + synchronized (mService) { + mService.mStackSupervisor.resumeFocusedStackTopActivityLocked(); + } + } + + protected void clearLockTaskMode(String reason) { + synchronized (mService) { + mService.mLockTaskController.clearLockTaskMode(reason); + } } } } diff --git a/com/android/server/am/UserState.java b/com/android/server/am/UserState.java index 2e27387a..d36d9cbe 100644 --- a/com/android/server/am/UserState.java +++ b/com/android/server/am/UserState.java @@ -59,8 +59,10 @@ public final class UserState { /** * The last time that a provider was reported to usage stats as being brought to important * foreground procstate. + * <p><strong>Important: </strong>Only access this field when holding ActivityManagerService + * lock. */ - public final ArrayMap<String,Long> mProviderLastReportedFg = new ArrayMap<>(); + final ArrayMap<String,Long> mProviderLastReportedFg = new ArrayMap<>(); public UserState(UserHandle handle) { mHandle = handle; diff --git a/com/android/server/appwidget/AppWidgetServiceImpl.java b/com/android/server/appwidget/AppWidgetServiceImpl.java index 80b54770..a6aaaa67 100644 --- a/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -2427,14 +2427,14 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku out.attribute(null, "p", Integer.toHexString(widget.provider.tag)); } if (widget.options != null) { - out.attribute(null, "min_width", Integer.toHexString(widget.options.getInt( - AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH))); - out.attribute(null, "min_height", Integer.toHexString(widget.options.getInt( - AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT))); - out.attribute(null, "max_width", Integer.toHexString(widget.options.getInt( - AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH))); - out.attribute(null, "max_height", Integer.toHexString(widget.options.getInt( - AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT))); + int minWidth = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH); + int minHeight = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT); + int maxWidth = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH); + int maxHeight = widget.options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT); + out.attribute(null, "min_width", Integer.toHexString((minWidth > 0) ? minWidth : 0)); + out.attribute(null, "min_height", Integer.toHexString((minHeight > 0) ? minHeight : 0)); + out.attribute(null, "max_width", Integer.toHexString((maxWidth > 0) ? maxWidth : 0)); + out.attribute(null, "max_height", Integer.toHexString((maxHeight > 0) ? maxHeight : 0)); out.attribute(null, "host_category", Integer.toHexString(widget.options.getInt( AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY))); } diff --git a/com/android/server/audio/AudioEventLogger.java b/com/android/server/audio/AudioEventLogger.java index c96138ff..9ebd75bd 100644 --- a/com/android/server/audio/AudioEventLogger.java +++ b/com/android/server/audio/AudioEventLogger.java @@ -16,6 +16,8 @@ package com.android.server.audio; +import android.util.Log; + import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; @@ -47,6 +49,22 @@ public class AudioEventLogger { } /** + * Causes the string message for the event to appear in the logcat. + * Here is an example of how to create a new event (a StringEvent), adding it to the logger + * (an instance of AudioEventLogger) while also making it show in the logcat: + * <pre> + * myLogger.log( + * (new StringEvent("something for logcat and logger")).printLog(MyClass.TAG) ); + * </pre> + * @param tag the tag for the android.util.Log.v + * @return the same instance of the event + */ + public Event printLog(String tag) { + Log.i(tag, eventToString()); + return this; + } + + /** * Convert event to String. * This method is only called when the logger history is about to the dumped, * so this method is where expensive String conversions should be made, not when the Event diff --git a/com/android/server/audio/AudioService.java b/com/android/server/audio/AudioService.java index 91b15912..5eb2a8d2 100644 --- a/com/android/server/audio/AudioService.java +++ b/com/android/server/audio/AudioService.java @@ -461,6 +461,8 @@ public class AudioService extends IAudioService.Stub // Forced device usage for communications private int mForcedUseForComm; + private int mForcedUseForCommExt; // External state returned by getters: always consistent + // with requests by setters // List of binder death handlers for setMode() client processes. // The last process to have called setMode() is at the top of the list. @@ -749,6 +751,9 @@ public class AudioService extends IAudioService.Stub // relies on audio policy having correct ranges for volume indexes. mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex(); + mPlaybackMonitor = + new PlaybackActivityMonitor(context, MAX_STREAM_VOLUME[AudioSystem.STREAM_ALARM]); + mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor); mRecordMonitor = new RecordingActivityMonitor(mContext); @@ -2544,13 +2549,15 @@ public class AudioService extends IAudioService.Stub } } int status = AudioSystem.AUDIO_STATUS_OK; + int actualMode; do { + actualMode = mode; if (mode == AudioSystem.MODE_NORMAL) { // get new mode from client at top the list if any if (!mSetModeDeathHandlers.isEmpty()) { hdlr = mSetModeDeathHandlers.get(0); cb = hdlr.getBinder(); - mode = hdlr.getMode(); + actualMode = hdlr.getMode(); if (DEBUG_MODE) { Log.w(TAG, " using mode=" + mode + " instead due to death hdlr at pid=" + hdlr.mPid); @@ -2574,12 +2581,11 @@ public class AudioService extends IAudioService.Stub hdlr.setMode(mode); } - if (mode != mMode) { - status = AudioSystem.setPhoneState(mode); + if (actualMode != mMode) { + status = AudioSystem.setPhoneState(actualMode); if (status == AudioSystem.AUDIO_STATUS_OK) { - if (DEBUG_MODE) { Log.v(TAG, " mode successfully set to " + mode); } - mMode = mode; - mModeLogger.log(new PhoneStateEvent(caller, pid, mode)); + if (DEBUG_MODE) { Log.v(TAG, " mode successfully set to " + actualMode); } + mMode = actualMode; } else { if (hdlr != null) { mSetModeDeathHandlers.remove(hdlr); @@ -2595,13 +2601,16 @@ public class AudioService extends IAudioService.Stub } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty()); if (status == AudioSystem.AUDIO_STATUS_OK) { - if (mode != AudioSystem.MODE_NORMAL) { + if (actualMode != AudioSystem.MODE_NORMAL) { if (mSetModeDeathHandlers.isEmpty()) { Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack"); } else { newModeOwnerPid = mSetModeDeathHandlers.get(0).getPid(); } } + // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL + mModeLogger.log( + new PhoneStateEvent(caller, pid, mode, newModeOwnerPid, actualMode)); int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); int device = getDeviceForStream(streamType); int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device); @@ -2890,13 +2899,14 @@ public class AudioService extends IAudioService.Stub mForcedUseForComm = AudioSystem.FORCE_NONE; } + mForcedUseForCommExt = mForcedUseForComm; sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE, AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource, 0); } /** @see AudioManager#isSpeakerphoneOn() */ public boolean isSpeakerphoneOn() { - return (mForcedUseForComm == AudioSystem.FORCE_SPEAKER); + return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER); } /** @see AudioManager#setBluetoothScoOn(boolean) */ @@ -2904,6 +2914,13 @@ public class AudioService extends IAudioService.Stub if (!checkAudioSettingsPermission("setBluetoothScoOn()")) { return; } + + // Only enable calls from system components + if (Binder.getCallingUid() >= FIRST_APPLICATION_UID) { + mForcedUseForCommExt = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE; + return; + } + // for logging only final String eventSource = new StringBuilder("setBluetoothScoOn(").append(on) .append(") from u/pid:").append(Binder.getCallingUid()).append("/") @@ -2913,11 +2930,21 @@ public class AudioService extends IAudioService.Stub public void setBluetoothScoOnInt(boolean on, String eventSource) { if (on) { + // do not accept SCO ON if SCO audio is not connected + synchronized(mScoClients) { + if ((mBluetoothHeadset != null) && + (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) + != BluetoothHeadset.STATE_AUDIO_CONNECTED)) { + mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO; + return; + } + } mForcedUseForComm = AudioSystem.FORCE_BT_SCO; } else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) { mForcedUseForComm = AudioSystem.FORCE_NONE; } - + mForcedUseForCommExt = mForcedUseForComm; + AudioSystem.setParameters("BT_SCO="+ (on ? "on" : "off")); sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE, AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource, 0); sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE, @@ -2926,7 +2953,7 @@ public class AudioService extends IAudioService.Stub /** @see AudioManager#isBluetoothScoOn() */ public boolean isBluetoothScoOn() { - return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO); + return (mForcedUseForCommExt == AudioSystem.FORCE_BT_SCO); } /** @see AudioManager#setBluetoothA2dpOn(boolean) */ @@ -4134,7 +4161,8 @@ public class AudioService extends IAudioService.Stub newDevice, AudioSystem.getOutputDeviceName(newDevice))); } synchronized (mConnectedDevices) { - if ((newDevice & DEVICE_MEDIA_UNMUTED_ON_PLUG) != 0 + if (mNm.getZenMode() != Settings.Global.ZEN_MODE_NO_INTERRUPTIONS + && (newDevice & DEVICE_MEDIA_UNMUTED_ON_PLUG) != 0 && mStreamStates[AudioSystem.STREAM_MUSIC].mIsMuted && mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(newDevice) != 0 && (newDevice & AudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC)) != 0) @@ -6134,12 +6162,12 @@ public class AudioService extends IAudioService.Stub private int mSafeMediaVolumeIndex; // mSafeUsbMediaVolumeIndex is used for USB Headsets and is the music volume UI index // corresponding to a gain of -30 dBFS in audio flinger mixer. - // We remove -15 dBs from the theoretical -15dB to account for the EQ boost when bands are set - // to max gain. + // We remove -22 dBs from the theoretical -15dB to account for the EQ + bass boost + // amplification when both effects are on with all band gains at maximum. // This level corresponds to a loudness of 85 dB SPL for the warning to be displayed when // the headset is compliant to EN 60950 with a max loudness of 100dB SPL. private int mSafeUsbMediaVolumeIndex; - private static final float SAFE_VOLUME_GAIN_DBFS = -30.0f; + private static final float SAFE_VOLUME_GAIN_DBFS = -37.0f; // mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced, private final int mSafeMediaVolumeDevices = AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE | @@ -6952,7 +6980,7 @@ public class AudioService extends IAudioService.Stub //====================== // Audio playback notification //====================== - private final PlaybackActivityMonitor mPlaybackMonitor = new PlaybackActivityMonitor(); + private final PlaybackActivityMonitor mPlaybackMonitor; public void registerPlaybackCallback(IPlaybackConfigDispatcher pcdb) { final boolean isPrivileged = diff --git a/com/android/server/audio/AudioServiceEvents.java b/com/android/server/audio/AudioServiceEvents.java index 634c8c27..9d9e35bd 100644 --- a/com/android/server/audio/AudioServiceEvents.java +++ b/com/android/server/audio/AudioServiceEvents.java @@ -26,20 +26,27 @@ public class AudioServiceEvents { final static class PhoneStateEvent extends AudioEventLogger.Event { final String mPackage; - final int mPid; - final int mMode; + final int mOwnerPid; + final int mRequesterPid; + final int mRequestedMode; + final int mActualMode; - PhoneStateEvent(String callingPackage, int pid, int mode) { + PhoneStateEvent(String callingPackage, int requesterPid, int requestedMode, + int ownerPid, int actualMode) { mPackage = callingPackage; - mPid = pid; - mMode = mode; + mRequesterPid = requesterPid; + mRequestedMode = requestedMode; + mOwnerPid = ownerPid; + mActualMode = actualMode; } @Override public String eventToString() { - return new StringBuilder("setMode(").append(AudioSystem.modeToString(mMode)) + return new StringBuilder("setMode(").append(AudioSystem.modeToString(mRequestedMode)) .append(") from package=").append(mPackage) - .append(" pid=").append(mPid).toString(); + .append(" pid=").append(mRequesterPid) + .append(" selected mode=").append(AudioSystem.modeToString(mActualMode)) + .append(" by pid=").append(mOwnerPid).toString(); } } diff --git a/com/android/server/audio/MediaFocusControl.java b/com/android/server/audio/MediaFocusControl.java index 7d742ffd..c5f563c7 100644 --- a/com/android/server/audio/MediaFocusControl.java +++ b/com/android/server/audio/MediaFocusControl.java @@ -89,6 +89,9 @@ public class MediaFocusControl implements PlayerFocusEnforcer { pw.println("\nMediaFocusControl dump time: " + DateFormat.getTimeInstance().format(new Date())); dumpFocusStack(pw); + pw.println("\n"); + // log + mEventLogger.dump(pw); } //================================================================= @@ -120,6 +123,14 @@ public class MediaFocusControl implements PlayerFocusEnforcer { private final static Object mAudioFocusLock = new Object(); /** + * Arbitrary maximum size of audio focus stack to prevent apps OOM'ing this process. + */ + private static final int MAX_STACK_SIZE = 100; + + private static final AudioEventLogger mEventLogger = new AudioEventLogger(50, + "focus commands as seen by MediaFocusControl"); + + /** * Discard the current audio focus owner. * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign * focus), remove it from the stack, and clear the remote control display. @@ -643,11 +654,14 @@ public class MediaFocusControl implements PlayerFocusEnforcer { protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags, int sdk) { - Log.i(TAG, " AudioFocus requestAudioFocus() from uid/pid " + Binder.getCallingUid() - + "/" + Binder.getCallingPid() - + " clientId=" + clientId - + " req=" + focusChangeHint - + " flags=0x" + Integer.toHexString(flags)); + mEventLogger.log((new AudioEventLogger.StringEvent( + "requestAudioFocus() from uid/pid " + Binder.getCallingUid() + + "/" + Binder.getCallingPid() + + " clientId=" + clientId + " callingPack=" + callingPackageName + + " req=" + focusChangeHint + + " flags=0x" + Integer.toHexString(flags) + + " sdk=" + sdk)) + .printLog(TAG)); // we need a valid binder callback for clients if (!cb.pingBinder()) { Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting."); @@ -660,6 +674,11 @@ public class MediaFocusControl implements PlayerFocusEnforcer { } synchronized(mAudioFocusLock) { + if (mFocusStack.size() > MAX_STACK_SIZE) { + Log.e(TAG, "Max AudioFocus stack size reached, failing requestAudioFocus()"); + return AudioManager.AUDIOFOCUS_REQUEST_FAILED; + } + boolean enteringRingOrCall = !mRingOrCallActive & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0); if (enteringRingOrCall) { mRingOrCallActive = true; } @@ -770,10 +789,12 @@ public class MediaFocusControl implements PlayerFocusEnforcer { * */ protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa, String callingPackageName) { - // AudioAttributes are currently ignored, to be used for zones - Log.i(TAG, " AudioFocus abandonAudioFocus() from uid/pid " + Binder.getCallingUid() - + "/" + Binder.getCallingPid() - + " clientId=" + clientId); + // AudioAttributes are currently ignored, to be used for zones / a11y + mEventLogger.log((new AudioEventLogger.StringEvent( + "abandonAudioFocus() from uid/pid " + Binder.getCallingUid() + + "/" + Binder.getCallingPid() + + " clientId=" + clientId)) + .printLog(TAG)); try { // this will take care of notifying the new focus owner if needed synchronized(mAudioFocusLock) { diff --git a/com/android/server/audio/PlaybackActivityMonitor.java b/com/android/server/audio/PlaybackActivityMonitor.java index d1a37af5..6506cf7f 100644 --- a/com/android/server/audio/PlaybackActivityMonitor.java +++ b/com/android/server/audio/PlaybackActivityMonitor.java @@ -17,6 +17,8 @@ package com.android.server.audio; import android.annotation.NonNull; +import android.content.Context; +import android.content.pm.PackageManager; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.AudioPlaybackConfiguration; @@ -90,7 +92,14 @@ public final class PlaybackActivityMonitor private final HashMap<Integer, AudioPlaybackConfiguration> mPlayers = new HashMap<Integer, AudioPlaybackConfiguration>(); - PlaybackActivityMonitor() { + private final Context mContext; + private int mSavedAlarmVolume = -1; + private final int mMaxAlarmVolume; + private int mPrivilegedAlarmActiveCount = 0; + + PlaybackActivityMonitor(Context context, int maxAlarmVolume) { + mContext = context; + mMaxAlarmVolume = maxAlarmVolume; PlayMonitorClient.sListenerDeathMonitor = this; AudioPlaybackConfiguration.sPlayerDeathMonitor = this; } @@ -105,7 +114,7 @@ public final class PlaybackActivityMonitor if (index >= 0) { if (!disable) { if (DEBUG) { // hidden behind DEBUG, too noisy otherwise - mEventLogger.log(new AudioEventLogger.StringEvent("unbanning uid:" + uid)); + sEventLogger.log(new AudioEventLogger.StringEvent("unbanning uid:" + uid)); } mBannedUids.remove(index); // nothing else to do, future playback requests from this uid are ok @@ -116,7 +125,7 @@ public final class PlaybackActivityMonitor checkBanPlayer(apc, uid); } if (DEBUG) { // hidden behind DEBUG, too noisy otherwise - mEventLogger.log(new AudioEventLogger.StringEvent("banning uid:" + uid)); + sEventLogger.log(new AudioEventLogger.StringEvent("banning uid:" + uid)); } mBannedUids.add(new Integer(uid)); } // no else to handle, uid already not in list, so enabling again is no-op @@ -151,7 +160,7 @@ public final class PlaybackActivityMonitor new AudioPlaybackConfiguration(pic, newPiid, Binder.getCallingUid(), Binder.getCallingPid()); apc.init(); - mEventLogger.log(new NewPlayerEvent(apc)); + sEventLogger.log(new NewPlayerEvent(apc)); synchronized(mPlayerLock) { mPlayers.put(newPiid, apc); } @@ -163,7 +172,7 @@ public final class PlaybackActivityMonitor synchronized(mPlayerLock) { final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid)); if (checkConfigurationCaller(piid, apc, binderUid)) { - mEventLogger.log(new AudioAttrEvent(piid, attr)); + sEventLogger.log(new AudioAttrEvent(piid, attr)); change = apc.handleAudioAttributesEvent(attr); } else { Log.e(TAG, "Error updating audio attributes"); @@ -175,6 +184,38 @@ public final class PlaybackActivityMonitor } } + private void checkVolumeForPrivilegedAlarm(AudioPlaybackConfiguration apc, int event) { + if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED || + apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { + if ((apc.getAudioAttributes().getAllFlags() & + AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0 && + apc.getAudioAttributes().getUsage() == AudioAttributes.USAGE_ALARM && + mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, + apc.getClientPid(), apc.getClientUid()) == + PackageManager.PERMISSION_GRANTED) { + if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED && + apc.getPlayerState() != AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { + if (mPrivilegedAlarmActiveCount++ == 0) { + mSavedAlarmVolume = AudioSystem.getStreamVolumeIndex( + AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER); + AudioSystem.setStreamVolumeIndex(AudioSystem.STREAM_ALARM, + mMaxAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER); + } + } else if (event != AudioPlaybackConfiguration.PLAYER_STATE_STARTED && + apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { + if (--mPrivilegedAlarmActiveCount == 0) { + if (AudioSystem.getStreamVolumeIndex( + AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER) == + mMaxAlarmVolume) { + AudioSystem.setStreamVolumeIndex(AudioSystem.STREAM_ALARM, + mSavedAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER); + } + } + } + } + } + } + public void playerEvent(int piid, int event, int binderUid) { if (DEBUG) { Log.v(TAG, String.format("playerEvent(piid=%d, event=%d)", piid, event)); } final boolean change; @@ -183,12 +224,12 @@ public final class PlaybackActivityMonitor if (apc == null) { return; } - mEventLogger.log(new PlayerEvent(piid, event)); + sEventLogger.log(new PlayerEvent(piid, event)); if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { for (Integer uidInteger: mBannedUids) { if (checkBanPlayer(apc, uidInteger.intValue())) { // player was banned, do not update its state - mEventLogger.log(new AudioEventLogger.StringEvent( + sEventLogger.log(new AudioEventLogger.StringEvent( "not starting piid:" + piid + " ,is banned")); return; } @@ -200,6 +241,7 @@ public final class PlaybackActivityMonitor } if (checkConfigurationCaller(piid, apc, binderUid)) { //TODO add generation counter to only update to the latest state + checkVolumeForPrivilegedAlarm(apc, event); change = apc.handleStateEvent(event); } else { Log.e(TAG, "Error handling event " + event); @@ -216,7 +258,7 @@ public final class PlaybackActivityMonitor public void playerHasOpPlayAudio(int piid, boolean hasOpPlayAudio, int binderUid) { // no check on UID yet because this is only for logging at the moment - mEventLogger.log(new PlayerOpPlayAudioEvent(piid, hasOpPlayAudio, binderUid)); + sEventLogger.log(new PlayerOpPlayAudioEvent(piid, hasOpPlayAudio, binderUid)); } public void releasePlayer(int piid, int binderUid) { @@ -224,10 +266,11 @@ public final class PlaybackActivityMonitor synchronized(mPlayerLock) { final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid)); if (checkConfigurationCaller(piid, apc, binderUid)) { - mEventLogger.log(new AudioEventLogger.StringEvent( + sEventLogger.log(new AudioEventLogger.StringEvent( "releasing player piid:" + piid)); mPlayers.remove(new Integer(piid)); mDuckingManager.removeReleased(apc); + checkVolumeForPrivilegedAlarm(apc, AudioPlaybackConfiguration.PLAYER_STATE_RELEASED); apc.handleStateEvent(AudioPlaybackConfiguration.PLAYER_STATE_RELEASED); } } @@ -278,7 +321,7 @@ public final class PlaybackActivityMonitor } pw.println("\n"); // log - mEventLogger.dump(pw); + sEventLogger.dump(pw); } } @@ -456,7 +499,8 @@ public final class PlaybackActivityMonitor } if (mute) { try { - Log.v(TAG, "call: muting player" + piid + " uid:" + apc.getClientUid()); + sEventLogger.log((new AudioEventLogger.StringEvent("call: muting piid:" + + piid + " uid:" + apc.getClientUid())).printLog(TAG)); apc.getPlayerProxy().setVolume(0.0f); mMutedPlayers.add(new Integer(piid)); } catch (Exception e) { @@ -480,7 +524,8 @@ public final class PlaybackActivityMonitor final AudioPlaybackConfiguration apc = mPlayers.get(piid); if (apc != null) { try { - Log.v(TAG, "call: unmuting player" + piid + " uid:" + apc.getClientUid()); + sEventLogger.log(new AudioEventLogger.StringEvent("call: unmuting piid:" + + piid).printLog(TAG)); apc.getPlayerProxy().setVolume(1.0f); } catch (Exception e) { Log.e(TAG, "call: error unmuting player " + piid + " uid:" @@ -669,8 +714,7 @@ public final class PlaybackActivityMonitor return; } try { - Log.v(TAG, "ducking (skipRamp=" + skipRamp + ") player piid:" - + apc.getPlayerInterfaceId() + " uid:" + mUid); + sEventLogger.log((new DuckEvent(apc, skipRamp)).printLog(TAG)); apc.getPlayerProxy().applyVolumeShaper( DUCK_VSHAPE, skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED); @@ -685,7 +729,8 @@ public final class PlaybackActivityMonitor final AudioPlaybackConfiguration apc = players.get(piid); if (apc != null) { try { - Log.v(TAG, "unducking player " + piid + " uid:" + mUid); + sEventLogger.log((new AudioEventLogger.StringEvent("unducking piid:" + + piid)).printLog(TAG)); apc.getPlayerProxy().applyVolumeShaper( DUCK_ID, VolumeShaper.Operation.REVERSE); @@ -772,7 +817,28 @@ public final class PlaybackActivityMonitor } } - private final static class AudioAttrEvent extends AudioEventLogger.Event { + private static final class DuckEvent extends AudioEventLogger.Event { + private final int mPlayerIId; + private final boolean mSkipRamp; + private final int mClientUid; + private final int mClientPid; + + DuckEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) { + mPlayerIId = apc.getPlayerInterfaceId(); + mSkipRamp = skipRamp; + mClientUid = apc.getClientUid(); + mClientPid = apc.getClientPid(); + } + + @Override + public String eventToString() { + return new StringBuilder("ducking player piid:").append(mPlayerIId) + .append(" uid/pid:").append(mClientUid).append("/").append(mClientPid) + .append(" skip ramp:").append(mSkipRamp).toString(); + } + } + + private static final class AudioAttrEvent extends AudioEventLogger.Event { private final int mPlayerIId; private final AudioAttributes mPlayerAttr; @@ -787,6 +853,6 @@ public final class PlaybackActivityMonitor } } - private final AudioEventLogger mEventLogger = new AudioEventLogger(100, + private static final AudioEventLogger sEventLogger = new AudioEventLogger(100, "playback activity as reported through PlayerBase"); } diff --git a/com/android/server/autofill/AutofillManagerService.java b/com/android/server/autofill/AutofillManagerService.java index ddc819d3..1f4161ac 100644 --- a/com/android/server/autofill/AutofillManagerService.java +++ b/com/android/server/autofill/AutofillManagerService.java @@ -115,11 +115,24 @@ public final class AutofillManagerService extends SystemService { private final SparseBooleanArray mDisabledUsers = new SparseBooleanArray(); private final LocalLog mRequestsHistory = new LocalLog(20); + private final LocalLog mUiLatencyHistory = new LocalLog(20); private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { + if (sDebug) Slog.d(TAG, "Close system dialogs"); + + // TODO(b/64940307): we need to destroy all sessions that are finished but showing + // Save UI because there is no way to show the Save UI back when the activity + // beneath it is brought back to top. Ideally, we should just hide the UI and + // bring it back when the activity resumes. + synchronized (mLock) { + for (int i = 0; i < mServicesCache.size(); i++) { + mServicesCache.valueAt(i).destroyFinishedSessionsLocked(); + } + } + mUi.hideAll(null); } } @@ -294,7 +307,7 @@ public final class AutofillManagerService extends SystemService { AutofillManagerServiceImpl service = mServicesCache.get(resolvedUserId); if (service == null) { service = new AutofillManagerServiceImpl(mContext, mLock, mRequestsHistory, - resolvedUserId, mUi, mDisabledUsers.get(resolvedUserId)); + mUiLatencyHistory, resolvedUserId, mUi, mDisabledUsers.get(resolvedUserId)); mServicesCache.put(userId, service); } return service; @@ -650,7 +663,7 @@ public final class AutofillManagerService extends SystemService { synchronized (mLock) { final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId); if (service == null) return false; - return Objects.equals(packageName, service.getPackageName()); + return Objects.equals(packageName, service.getServicePackageName()); } } @@ -724,6 +737,8 @@ public final class AutofillManagerService extends SystemService { if (showHistory) { pw.println("Requests history:"); mRequestsHistory.reverseDump(fd, pw, args); + pw.println("UI latency history:"); + mUiLatencyHistory.reverseDump(fd, pw, args); } } finally { setDebugLocked(oldDebug); diff --git a/com/android/server/autofill/AutofillManagerServiceImpl.java b/com/android/server/autofill/AutofillManagerServiceImpl.java index f2f96f82..862070ad 100644 --- a/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -35,6 +35,7 @@ import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.metrics.LogMaker; import android.os.AsyncTask; import android.os.Binder; import android.os.Bundle; @@ -63,6 +64,8 @@ import android.view.autofill.IAutoFillManagerClient; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.os.HandlerCaller; import com.android.server.autofill.ui.AutoFillUI; @@ -89,6 +92,7 @@ final class AutofillManagerServiceImpl { private final Context mContext; private final Object mLock; private final AutoFillUI mUi; + private final MetricsLogger mMetricsLogger = new MetricsLogger(); private RemoteCallbackList<IAutoFillManagerClient> mClients; private AutofillServiceInfo mInfo; @@ -96,6 +100,8 @@ final class AutofillManagerServiceImpl { private static final Random sRandom = new Random(); private final LocalLog mRequestsHistory; + private final LocalLog mUiLatencyHistory; + /** * Whether service was disabled for user due to {@link UserManager} restrictions. */ @@ -137,17 +143,19 @@ final class AutofillManagerServiceImpl { private long mLastPrune = 0; AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory, - int userId, AutoFillUI ui, boolean disabled) { + LocalLog uiLatencyHistory, int userId, AutoFillUI ui, boolean disabled) { mContext = context; mLock = lock; mRequestsHistory = requestsHistory; + mUiLatencyHistory = uiLatencyHistory; mUserId = userId; mUi = ui; updateLocked(disabled); } + @Nullable CharSequence getServiceName() { - final String packageName = getPackageName(); + final String packageName = getServicePackageName(); if (packageName == null) { return null; } @@ -162,7 +170,8 @@ final class AutofillManagerServiceImpl { } } - String getPackageName() { + @Nullable + String getServicePackageName() { final ComponentName serviceComponent = getServiceComponentName(); if (serviceComponent != null) { return serviceComponent.getPackageName(); @@ -216,8 +225,10 @@ final class AutofillManagerServiceImpl { if (serviceInfo != null) { mInfo = new AutofillServiceInfo(mContext.getPackageManager(), serviceComponent, mUserId); + if (sDebug) Slog.d(TAG, "Set component for user " + mUserId + " as " + mInfo); } else { mInfo = null; + if (sDebug) Slog.d(TAG, "Reset component for user " + mUserId); } final boolean isEnabled = isEnabled(); if (wasEnabled != isEnabled) { @@ -343,17 +354,31 @@ final class AutofillManagerServiceImpl { } void disableOwnedAutofillServicesLocked(int uid) { - if (mInfo == null || mInfo.getServiceInfo().applicationInfo.uid != uid) { + Slog.i(TAG, "disableOwnedServices(" + uid + "): " + mInfo); + if (mInfo == null) return; + + final ServiceInfo serviceInfo = mInfo.getServiceInfo(); + if (serviceInfo.applicationInfo.uid != uid) { + Slog.w(TAG, "disableOwnedServices(): ignored when called by UID " + uid + + " instead of " + serviceInfo.applicationInfo.uid + + " for service " + mInfo); return; } + + final long identity = Binder.clearCallingIdentity(); try { final String autoFillService = getComponentNameFromSettings(); - if (mInfo.getServiceInfo().getComponentName().equals( - ComponentName.unflattenFromString(autoFillService))) { + final ComponentName componentName = serviceInfo.getComponentName(); + if (componentName.equals(ComponentName.unflattenFromString(autoFillService))) { + mMetricsLogger.action(MetricsEvent.AUTOFILL_SERVICE_DISABLED_SELF, + componentName.getPackageName()); Settings.Secure.putStringForUser(mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, null, mUserId); destroySessionsLocked(); + } else { + Slog.w(TAG, "disableOwnedServices(): ignored because current service (" + + serviceInfo + ") does not match Settings (" + autoFillService + ")"); } } finally { Binder.restoreCallingIdentity(identity); @@ -377,7 +402,7 @@ final class AutofillManagerServiceImpl { final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock, sessionId, uid, activityToken, appCallbackToken, hasCallback, - mInfo.getServiceInfo().getComponentName(), packageName); + mUiLatencyHistory, mInfo.getServiceInfo().getComponentName(), packageName); mSessions.put(newSession.id, newSession); return newSession; @@ -450,7 +475,7 @@ final class AutofillManagerServiceImpl { final int sessionCount = mSessions.size(); for (int i = sessionCount - 1; i >= 0; i--) { final Session session = mSessions.valueAt(i); - if (session.isSaveUiPendingForToken(token)) { + if (session.isSaveUiPendingForTokenLocked(token)) { session.onPendingSaveUi(operation, token); return; } @@ -636,7 +661,7 @@ final class AutofillManagerServiceImpl { void destroySessionsLocked() { if (mSessions.size() == 0) { - mUi.destroyAll(null, null); + mUi.destroyAll(null, null, false); return; } while (mSessions.size() > 0) { @@ -644,6 +669,18 @@ final class AutofillManagerServiceImpl { } } + // TODO(b/64940307): remove this method if SaveUI is refactored to be attached on activities + void destroyFinishedSessionsLocked() { + final int sessionCount = mSessions.size(); + for (int i = sessionCount - 1; i >= 0; i--) { + final Session session = mSessions.valueAt(i); + if (session.isSavingLocked()) { + if (sDebug) Slog.d(TAG, "destroyFinishedSessionsLocked(): " + session.id); + session.forceRemoveSelfLocked(); + } + } + } + void listSessionsLocked(ArrayList<String> output) { final int numSessions = mSessions.size(); for (int i = 0; i < numSessions; i++) { diff --git a/com/android/server/autofill/Helper.java b/com/android/server/autofill/Helper.java index 086dd29f..236fbfd9 100644 --- a/com/android/server/autofill/Helper.java +++ b/com/android/server/autofill/Helper.java @@ -18,6 +18,7 @@ package com.android.server.autofill; import android.annotation.NonNull; import android.annotation.Nullable; +import android.metrics.LogMaker; import android.os.Bundle; import android.service.autofill.Dataset; import android.util.ArrayMap; @@ -25,6 +26,8 @@ import android.util.ArraySet; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; + import java.util.ArrayList; import java.util.Arrays; import java.util.Objects; @@ -99,4 +102,14 @@ public final class Helper { } return fields; } + + @NonNull + public static LogMaker newLogMaker(int category, String packageName, + String servicePackageName) { + final LogMaker log = new LogMaker(category).setPackageName(packageName); + if (servicePackageName != null) { + log.addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName); + } + return log; + } } diff --git a/com/android/server/autofill/RemoteFillService.java b/com/android/server/autofill/RemoteFillService.java index f3151485..af55807f 100644 --- a/com/android/server/autofill/RemoteFillService.java +++ b/com/android/server/autofill/RemoteFillService.java @@ -578,9 +578,8 @@ final class RemoteFillService implements DeathRecipient { public void run() { synchronized (mLock) { if (isCancelledLocked()) { - // TODO(b/653742740): we should probably return here, but for now we're justing - // logging to confirm this is the problem if it happens again. - Slog.e(LOG_TAG, "run() called after canceled: " + mRequest); + if (sDebug) Slog.d(LOG_TAG, "run() called after canceled: " + mRequest); + return; } } final RemoteFillService remoteService = getService(); diff --git a/com/android/server/autofill/Session.java b/com/android/server/autofill/Session.java index 171053fd..ed00ffed 100644 --- a/com/android/server/autofill/Session.java +++ b/com/android/server/autofill/Session.java @@ -50,19 +50,24 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Parcelable; import android.os.RemoteException; +import android.os.SystemClock; import android.service.autofill.AutofillService; import android.service.autofill.Dataset; import android.service.autofill.FillContext; import android.service.autofill.FillRequest; import android.service.autofill.FillResponse; +import android.service.autofill.InternalSanitizer; import android.service.autofill.InternalValidator; import android.service.autofill.SaveInfo; import android.service.autofill.SaveRequest; +import android.service.autofill.Transformation; import android.service.autofill.ValueFinder; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.LocalLog; import android.util.Slog; import android.util.SparseArray; +import android.util.TimeUtils; import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; import android.view.autofill.AutofillValue; @@ -183,6 +188,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private ArrayList<String> mSelectedDatasetIds; /** + * When the session started (using elapsed time since boot). + */ + private final long mStartTime; + + /** + * When the UI was shown for the first time (using elapsed time since boot). + */ + @GuardedBy("mLock") + private long mUiShownTime; + + @GuardedBy("mLock") + private final LocalLog mUiLatencyHistory; + + /** * Receiver of assist data from the app's {@link Activity}. */ private final IResultReceiver mAssistReceiver = new IResultReceiver.Stub() { @@ -403,10 +422,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Session(@NonNull AutofillManagerServiceImpl service, @NonNull AutoFillUI ui, @NonNull Context context, @NonNull HandlerCaller handlerCaller, int userId, @NonNull Object lock, int sessionId, int uid, @NonNull IBinder activityToken, - @NonNull IBinder client, boolean hasCallback, + @NonNull IBinder client, boolean hasCallback, @NonNull LocalLog uiLatencyHistory, @NonNull ComponentName componentName, @NonNull String packageName) { id = sessionId; this.uid = uid; + mStartTime = SystemClock.elapsedRealtime(); mService = service; mLock = lock; mUi = ui; @@ -414,10 +434,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mRemoteFillService = new RemoteFillService(context, componentName, userId, this); mActivityToken = activityToken; mHasCallback = hasCallback; + mUiLatencyHistory = uiLatencyHistory; mPackageName = packageName; mClient = IAutoFillManagerClient.Stub.asInterface(client); - mMetricsLogger.action(MetricsEvent.AUTOFILL_SESSION_STARTED, mPackageName); + writeLog(MetricsEvent.AUTOFILL_SESSION_STARTED); } /** @@ -471,19 +492,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if ((response.getDatasets() == null || response.getDatasets().isEmpty()) && response.getAuthentication() == null) { // Response is "empty" from an UI point of view, need to notify client. - notifyUnavailableToClient(); + notifyUnavailableToClient(false); } synchronized (mLock) { processResponseLocked(response, requestFlags); } - final LogMaker log = (new LogMaker(MetricsEvent.AUTOFILL_REQUEST)) + final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_REQUEST, servicePackageName) .setType(MetricsEvent.TYPE_SUCCESS) - .setPackageName(mPackageName) .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, - response.getDatasets() == null ? 0 : response.getDatasets().size()) - .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, - servicePackageName); + response.getDatasets() == null ? 0 : response.getDatasets().size()); mMetricsLogger.write(log); } @@ -499,10 +517,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } mService.resetLastResponse(); } - LogMaker log = (new LogMaker(MetricsEvent.AUTOFILL_REQUEST)) - .setType(MetricsEvent.TYPE_FAILURE) - .setPackageName(mPackageName) - .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName); + LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_REQUEST, servicePackageName) + .setType(MetricsEvent.TYPE_FAILURE); mMetricsLogger.write(log); getUiForShowing().showError(message, this); @@ -521,11 +537,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } } - LogMaker log = (new LogMaker( - MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST)) - .setType(MetricsEvent.TYPE_SUCCESS) - .setPackageName(mPackageName) - .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName); + LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName) + .setType(MetricsEvent.TYPE_SUCCESS); mMetricsLogger.write(log); // Nothing left to do... @@ -545,11 +558,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } } - LogMaker log = (new LogMaker( - MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST)) - .setType(MetricsEvent.TYPE_FAILURE) - .setPackageName(mPackageName) - .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, servicePackageName); + LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName) + .setType(MetricsEvent.TYPE_FAILURE); mMetricsLogger.write(log); getUiForShowing().showError(message, this); @@ -583,6 +593,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // FillServiceCallbacks @Override public void authenticate(int requestId, int datasetIndex, IntentSender intent, Bundle extras) { + if (sDebug) { + Slog.d(TAG, "authenticate(): requestId=" + requestId + "; datasetIdx=" + datasetIndex + + "; intentSender=" + intent); + } final Intent fillInIntent; synchronized (mLock) { if (mDestroyed) { @@ -591,6 +605,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } fillInIntent = createAuthFillInIntentLocked(requestId, extras); + if (fillInIntent == null) { + forceRemoveSelfLocked(); + return; + } } mService.setAuthenticationSelected(id, mClientState); @@ -746,21 +764,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT); if (sDebug) Slog.d(TAG, "setAuthenticationResultLocked(): result=" + result); if (result instanceof FillResponse) { - final FillResponse response = (FillResponse) result; - mMetricsLogger.action(MetricsEvent.AUTOFILL_AUTHENTICATED, mPackageName); - replaceResponseLocked(authenticatedResponse, response); + writeLog(MetricsEvent.AUTOFILL_AUTHENTICATED); + replaceResponseLocked(authenticatedResponse, (FillResponse) result); } else if (result instanceof Dataset) { - // TODO: add proper metric if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) { + writeLog(MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED); final Dataset dataset = (Dataset) result; authenticatedResponse.getDatasets().set(datasetIdx, dataset); autoFill(requestId, datasetIdx, dataset, false); + } else { + writeLog(MetricsEvent.AUTOFILL_INVALID_DATASET_AUTHENTICATION); } } else { if (result != null) { Slog.w(TAG, "service returned invalid auth type: " + result); } - // TODO: add proper metric (on else) + writeLog(MetricsEvent.AUTOFILL_INVALID_AUTHENTICATION); processNullResponseLocked(0); } } @@ -774,43 +793,57 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mHasCallback = hasIt; } - /** - * Shows the save UI, when session can be saved. - * - * @return {@code true} if session is done, or {@code false} if it's pending user action. - */ - public boolean showSaveLocked() { - if (mDestroyed) { - Slog.w(TAG, "Call to Session#showSaveLocked() rejected - session: " - + id + " destroyed"); - return false; - } + @Nullable + private FillResponse getLastResponseLocked(@Nullable String logPrefix) { if (mContexts == null) { - Slog.d(TAG, "showSaveLocked(): no contexts"); - return true; + if (sDebug && logPrefix != null) Slog.d(TAG, logPrefix + ": no contexts"); + return null; } if (mResponses == null) { // Happens when the activity / session was finished before the service replied, or // when the service cannot autofill it (and returned a null response). - if (sVerbose) { - Slog.v(TAG, "showSaveLocked(): no responses on session"); + if (sVerbose && logPrefix != null) { + Slog.v(TAG, logPrefix + ": no responses on session"); } - return true; + return null; } final int lastResponseIdx = getLastResponseIndexLocked(); if (lastResponseIdx < 0) { - Slog.w(TAG, "showSaveLocked(): did not get last response. mResponses=" + mResponses - + ", mViewStates=" + mViewStates); - return true; + if (logPrefix != null) { + Slog.w(TAG, logPrefix + ": did not get last response. mResponses=" + mResponses + + ", mViewStates=" + mViewStates); + } + return null; } final FillResponse response = mResponses.valueAt(lastResponseIdx); - final SaveInfo saveInfo = response.getSaveInfo(); - if (sVerbose) { - Slog.v(TAG, "showSaveLocked(): mResponses=" + mResponses + ", mContexts=" + mContexts + if (sVerbose && logPrefix != null) { + Slog.v(TAG, logPrefix + ": mResponses=" + mResponses + ", mContexts=" + mContexts + ", mViewStates=" + mViewStates); } + return response; + } + + @Nullable + private SaveInfo getSaveInfoLocked() { + final FillResponse response = getLastResponseLocked(null); + return response == null ? null : response.getSaveInfo(); + } + + /** + * Shows the save UI, when session can be saved. + * + * @return {@code true} if session is done, or {@code false} if it's pending user action. + */ + public boolean showSaveLocked() { + if (mDestroyed) { + Slog.w(TAG, "Call to Session#showSaveLocked() rejected - session: " + + id + " destroyed"); + return false; + } + final FillResponse response = getLastResponseLocked("showSaveLocked()"); + final SaveInfo saveInfo = response == null ? null : response.getSaveInfo(); /* * The Save dialog is only shown if all conditions below are met: @@ -825,6 +858,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return true; } + final ArrayMap<AutofillId, InternalSanitizer> sanitizers = createSanitizers(saveInfo); + // Cache used to make sure changed fields do not belong to a dataset. final ArrayMap<AutofillId, AutofillValue> currentValues = new ArrayMap<>(); final ArraySet<AutofillId> allIds = new ArraySet<>(); @@ -864,6 +899,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState break; } } + value = getSanitizedValue(sanitizers, id, value); currentValues.put(id, value); final AutofillValue filledValue = viewState.getAutofilledValue(); @@ -922,15 +958,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final InternalValidator validator = saveInfo.getValidator(); if (validator != null) { + final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_SAVE_VALIDATION); boolean isValid; try { isValid = validator.isValid(valueFinder); + log.setType(isValid + ? MetricsEvent.TYPE_SUCCESS + : MetricsEvent.TYPE_DISMISS); } catch (Exception e) { - Slog.e(TAG, "Not showing save UI because of exception during validation " - + e.getClass()); + Slog.e(TAG, "Not showing save UI because validation failed:", e); + log.setType(MetricsEvent.TYPE_FAILURE); + mMetricsLogger.write(log); return true; } + mMetricsLogger.write(log); if (!isValid) { Slog.i(TAG, "not showing save UI because fields failed validation"); return true; @@ -978,7 +1020,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final IAutoFillManagerClient client = getClient(); mPendingSaveUi = new PendingUi(mActivityToken, id, client); getUiForShowing().showSaveUi(mService.getServiceLabel(), mService.getServiceIcon(), - saveInfo, valueFinder, mPackageName, this, mPendingSaveUi); + mService.getServicePackageName(), saveInfo, valueFinder, mPackageName, this, + mPendingSaveUi); if (client != null) { try { client.setSaveUiState(id, true); @@ -999,6 +1042,48 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return true; } + @Nullable + private ArrayMap<AutofillId, InternalSanitizer> createSanitizers(@Nullable SaveInfo saveInfo) { + if (saveInfo == null) return null; + + final InternalSanitizer[] sanitizerKeys = saveInfo.getSanitizerKeys(); + if (sanitizerKeys == null) return null; + + final int size = sanitizerKeys.length ; + final ArrayMap<AutofillId, InternalSanitizer> sanitizers = new ArrayMap<>(size); + if (sDebug) Slog.d(TAG, "Service provided " + size + " sanitizers"); + final AutofillId[][] sanitizerValues = saveInfo.getSanitizerValues(); + for (int i = 0; i < size; i++) { + final InternalSanitizer sanitizer = sanitizerKeys[i]; + final AutofillId[] ids = sanitizerValues[i]; + if (sDebug) { + Slog.d(TAG, "sanitizer #" + i + " (" + sanitizer + ") for ids " + + Arrays.toString(ids)); + } + for (AutofillId id : ids) { + sanitizers.put(id, sanitizer); + } + } + return sanitizers; + } + + @NonNull + private AutofillValue getSanitizedValue( + @Nullable ArrayMap<AutofillId, InternalSanitizer> sanitizers, + @NonNull AutofillId id, + @NonNull AutofillValue value) { + if (sanitizers == null) return value; + + final InternalSanitizer sanitizer = sanitizers.get(id); + if (sanitizer == null) { + return value; + } + + final AutofillValue sanitized = sanitizer.sanitize(value); + if (sDebug) Slog.d(TAG, "Value for " + id + "(" + value + ") sanitized to " + sanitized); + return sanitized; + } + /** * Returns whether the session is currently showing the save UI */ @@ -1062,6 +1147,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } + final ArrayMap<AutofillId, InternalSanitizer> sanitizers = + createSanitizers(getSaveInfoLocked()); + final int numContexts = mContexts.size(); for (int contextNum = 0; contextNum < numContexts; contextNum++) { @@ -1088,7 +1176,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } if (sVerbose) Slog.v(TAG, "callSaveLocked(): updating " + id + " to " + value); - node.updateAutofillValue(value); + final AutofillValue sanitizedValue = getSanitizedValue(sanitizers, id, value); + + node.updateAutofillValue(sanitizedValue); } // Sanitize structure before it's sent to service. @@ -1244,6 +1334,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState break; case ACTION_VALUE_CHANGED: if (value != null && !value.equals(viewState.getCurrentValue())) { + if (value.isEmpty() + && viewState.getCurrentValue() != null + && viewState.getCurrentValue().isText() + && viewState.getCurrentValue().getTextValue() != null + && getSaveInfoLocked() != null) { + final int length = viewState.getCurrentValue().getTextValue().length(); + if (sDebug) { + Slog.d(TAG, "updateLocked(" + id + "): resetting value that was " + + length + " chars long"); + } + final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_VALUE_RESET) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_PREVIOUS_LENGTH, length); + mMetricsLogger.write(log); + } + // Always update the internal state. viewState.setCurrentValue(value); @@ -1319,7 +1424,33 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState filterText = value.getTextValue().toString(); } - getUiForShowing().showFillUi(filledId, response, filterText, mPackageName, this); + getUiForShowing().showFillUi(filledId, response, filterText, + mService.getServicePackageName(), mPackageName, this); + + synchronized (mLock) { + if (mUiShownTime == 0) { + // Log first time UI is shown. + mUiShownTime = SystemClock.elapsedRealtime(); + final long duration = mUiShownTime - mStartTime; + if (sDebug) { + final StringBuilder msg = new StringBuilder("1st UI for ") + .append(mActivityToken) + .append(" shown in "); + TimeUtils.formatDuration(duration, msg); + Slog.d(TAG, msg.toString()); + } + final StringBuilder historyLog = new StringBuilder("id=").append(id) + .append(" app=").append(mActivityToken) + .append(" svc=").append(mService.getServicePackageName()) + .append(" latency="); + TimeUtils.formatDuration(duration, historyLog); + mUiLatencyHistory.log(historyLog.toString()); + + final LogMaker metricsLog = newLogMaker(MetricsEvent.AUTOFILL_UI_LATENCY) + .setCounterValue((int) duration); + mMetricsLogger.write(metricsLog); + } + } } boolean isDestroyed() { @@ -1334,11 +1465,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - private void notifyUnavailableToClient() { + private void notifyUnavailableToClient(boolean sessionFinished) { synchronized (mLock) { - if (!mHasCallback || mCurrentViewId == null) return; + if (mCurrentViewId == null) return; try { - mClient.notifyNoFillUi(id, mCurrentViewId); + if (mHasCallback) { + mClient.notifyNoFillUi(id, mCurrentViewId, sessionFinished); + } else if (sessionFinished) { + mClient.setSessionFinished(AutofillManager.STATE_FINISHED); + } } catch (RemoteException e) { Slog.e(TAG, "Error notifying client no fill UI: id=" + mCurrentViewId, e); } @@ -1426,7 +1561,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } mService.resetLastResponse(); // Nothing to be done, but need to notify client. - notifyUnavailableToClient(); + notifyUnavailableToClient(true); removeSelf(); } @@ -1545,6 +1680,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } void autoFill(int requestId, int datasetIndex, Dataset dataset, boolean generateEvent) { + if (sDebug) { + Slog.d(TAG, "autoFill(): requestId=" + requestId + "; datasetIdx=" + datasetIndex + + "; dataset=" + dataset); + } synchronized (mLock) { if (mDestroyed) { Slog.w(TAG, "Call to Session#autoFill() rejected - session: " @@ -1565,10 +1704,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState); setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, false); final Intent fillInIntent = createAuthFillInIntentLocked(requestId, mClientState); - + if (fillInIntent == null) { + forceRemoveSelfLocked(); + return; + } final int authenticationId = AutofillManager.makeAuthenticationId(requestId, datasetIndex); startAuthentication(authenticationId, dataset.getAuthentication(), fillInIntent); + } } @@ -1578,14 +1721,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } + // TODO: this should never be null, but we got at least one occurrence, probably due to a race. + @Nullable private Intent createAuthFillInIntentLocked(int requestId, Bundle extras) { final Intent fillInIntent = new Intent(); final FillContext context = getFillContextByRequestIdLocked(requestId); if (context == null) { - // TODO(b/653742740): this will crash system_server. We need to handle it, but we're - // keeping it crashing for now so we can diagnose when it happens again - Slog.wtf(TAG, "no FillContext for requestId" + requestId + "; mContexts= " + mContexts); + Slog.wtf(TAG, "createAuthFillInIntentLocked(): no FillContext. requestId=" + requestId + + "; mContexts= " + mContexts); + return null; } fillInIntent.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, context.getStructure()); fillInIntent.putExtra(AutofillManager.EXTRA_CLIENT_STATE, extras); @@ -1614,6 +1759,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState pw.print(prefix); pw.print("uid: "); pw.println(uid); pw.print(prefix); pw.print("mPackagename: "); pw.println(mPackageName); pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken); + pw.print(prefix); pw.print("mStartTime: "); pw.println(mStartTime); + pw.print(prefix); pw.print("Time to show UI: "); + if (mUiShownTime == 0) { + pw.println("N/A"); + } else { + TimeUtils.formatDuration(mUiShownTime - mStartTime, pw); + pw.println(); + } pw.print(prefix); pw.print("mResponses: "); if (mResponses == null) { pw.println("null"); @@ -1732,10 +1885,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (mDestroyed) { return null; } - mUi.destroyAll(mPendingSaveUi, this); + mUi.destroyAll(mPendingSaveUi, this, true); mUi.clearCallback(this); mDestroyed = true; - mMetricsLogger.action(MetricsEvent.AUTOFILL_SESSION_FINISHED, mPackageName); + writeLog(MetricsEvent.AUTOFILL_SESSION_FINISHED); return mRemoteFillService; } @@ -1746,9 +1899,17 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState void forceRemoveSelfLocked() { if (sVerbose) Slog.v(TAG, "forceRemoveSelfLocked(): " + mPendingSaveUi); + final boolean isPendingSaveUi = isSaveUiPendingLocked(); mPendingSaveUi = null; removeSelfLocked(); - mUi.destroyAll(mPendingSaveUi, this); + mUi.destroyAll(mPendingSaveUi, this, false); + if (!isPendingSaveUi) { + try { + mClient.setSessionFinished(AutofillManager.STATE_UNKNOWN); + } catch (RemoteException e) { + Slog.e(TAG, "Error notifying client to finish session", e); + } + } } /** @@ -1771,7 +1932,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + id + " destroyed"); return; } - if (isSaveUiPending()) { + if (isSaveUiPendingLocked()) { Slog.i(TAG, "removeSelfLocked() ignored, waiting for pending save ui"); return; } @@ -1792,14 +1953,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * a specific {@code token} created by * {@link PendingUi#PendingUi(IBinder, int, IAutoFillManagerClient)}. */ - boolean isSaveUiPendingForToken(@NonNull IBinder token) { - return isSaveUiPending() && token.equals(mPendingSaveUi.getToken()); + boolean isSaveUiPendingForTokenLocked(@NonNull IBinder token) { + return isSaveUiPendingLocked() && token.equals(mPendingSaveUi.getToken()); } /** * Checks whether this session is hiding the Save UI to handle a custom description link. */ - private boolean isSaveUiPending() { + private boolean isSaveUiPendingLocked() { return mPendingSaveUi != null && mPendingSaveUi.getState() == PendingUi.STATE_PENDING; } @@ -1820,4 +1981,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } return lastResponseIdx; } + + private LogMaker newLogMaker(int category) { + return newLogMaker(category, mService.getServicePackageName()); + } + + private LogMaker newLogMaker(int category, String servicePackageName) { + return Helper.newLogMaker(category, mPackageName, servicePackageName); + } + + private void writeLog(int category) { + mMetricsLogger.write(newLogMaker(category)); + } } diff --git a/com/android/server/autofill/ui/AutoFillUI.java b/com/android/server/autofill/ui/AutoFillUI.java index cac2bff5..36b95fc0 100644 --- a/com/android/server/autofill/ui/AutoFillUI.java +++ b/com/android/server/autofill/ui/AutoFillUI.java @@ -40,8 +40,9 @@ import android.view.autofill.IAutofillWindowPresenter; import android.widget.Toast; import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.server.UiThread; +import com.android.server.autofill.Helper; import java.io.PrintWriter; @@ -158,21 +159,22 @@ public final class AutoFillUI { * @param focusedId the currently focused field * @param response the current fill response * @param filterText text of the view to be filled + * @param servicePackageName package name of the autofill service filling the activity * @param packageName package name of the activity that is filled * @param callback Identifier for the caller */ public void showFillUi(@NonNull AutofillId focusedId, @NonNull FillResponse response, - @Nullable String filterText, @NonNull String packageName, - @NonNull AutoFillUiCallback callback) { + @Nullable String filterText, @Nullable String servicePackageName, + @NonNull String packageName, @NonNull AutoFillUiCallback callback) { if (sDebug) { final int size = filterText == null ? 0 : filterText.length(); Slog.d(TAG, "showFillUi(): id=" + focusedId + ", filter=" + size + " chars"); } - final LogMaker log = (new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_FILL_UI)) - .setPackageName(packageName) - .addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_FILTERTEXT_LEN, + final LogMaker log = + Helper.newLogMaker(MetricsEvent.AUTOFILL_FILL_UI, packageName, servicePackageName) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FILTERTEXT_LEN, filterText == null ? 0 : filterText.length()) - .addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, response.getDatasets() == null ? 0 : response.getDatasets().size()); mHandler.post(() -> { @@ -184,7 +186,7 @@ public final class AutoFillUI { filterText, mOverlayControl, new FillUi.Callback() { @Override public void onResponsePicked(FillResponse response) { - log.setType(MetricsProto.MetricsEvent.TYPE_DETAIL); + log.setType(MetricsEvent.TYPE_DETAIL); hideFillUiUiThread(callback); if (mCallback != null) { mCallback.authenticate(response.getRequestId(), @@ -195,7 +197,7 @@ public final class AutoFillUI { @Override public void onDatasetPicked(Dataset dataset) { - log.setType(MetricsProto.MetricsEvent.TYPE_ACTION); + log.setType(MetricsEvent.TYPE_ACTION); hideFillUiUiThread(callback); if (mCallback != null) { final int datasetIndex = response.getDatasets().indexOf(dataset); @@ -205,14 +207,14 @@ public final class AutoFillUI { @Override public void onCanceled() { - log.setType(MetricsProto.MetricsEvent.TYPE_DISMISS); + log.setType(MetricsEvent.TYPE_DISMISS); hideFillUiUiThread(callback); } @Override public void onDestroy() { - if (log.getType() == MetricsProto.MetricsEvent.TYPE_UNKNOWN) { - log.setType(MetricsProto.MetricsEvent.TYPE_CLOSE); + if (log.getType() == MetricsEvent.TYPE_UNKNOWN) { + log.setType(MetricsEvent.TYPE_CLOSE); } mMetricsLogger.write(log); } @@ -246,37 +248,39 @@ public final class AutoFillUI { * Shows the UI asking the user to save for autofill. */ public void showSaveUi(@NonNull CharSequence serviceLabel, @NonNull Drawable serviceIcon, - @NonNull SaveInfo info,@NonNull ValueFinder valueFinder, @NonNull String packageName, + @Nullable String servicePackageName, @NonNull SaveInfo info, + @NonNull ValueFinder valueFinder, @NonNull String packageName, @NonNull AutoFillUiCallback callback, @NonNull PendingUi pendingSaveUi) { if (sVerbose) Slog.v(TAG, "showSaveUi() for " + packageName + ": " + info); int numIds = 0; numIds += info.getRequiredIds() == null ? 0 : info.getRequiredIds().length; numIds += info.getOptionalIds() == null ? 0 : info.getOptionalIds().length; - LogMaker log = (new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_SAVE_UI)) - .setPackageName(packageName).addTaggedData( - MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_IDS, numIds); + final LogMaker log = + Helper.newLogMaker(MetricsEvent.AUTOFILL_SAVE_UI, packageName, servicePackageName) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_IDS, numIds); mHandler.post(() -> { if (callback != mCallback) { return; } hideAllUiThread(callback); - mSaveUi = new SaveUi(mContext, pendingSaveUi, serviceLabel, serviceIcon, info, - valueFinder, mOverlayControl, new SaveUi.OnSaveListener() { + mSaveUi = new SaveUi(mContext, pendingSaveUi, serviceLabel, serviceIcon, + servicePackageName, packageName, info, valueFinder, mOverlayControl, + new SaveUi.OnSaveListener() { @Override public void onSave() { - log.setType(MetricsProto.MetricsEvent.TYPE_ACTION); + log.setType(MetricsEvent.TYPE_ACTION); hideSaveUiUiThread(mCallback); if (mCallback != null) { mCallback.save(); } - destroySaveUiUiThread(pendingSaveUi); + destroySaveUiUiThread(pendingSaveUi, true); } @Override public void onCancel(IntentSender listener) { - log.setType(MetricsProto.MetricsEvent.TYPE_DISMISS); + log.setType(MetricsEvent.TYPE_DISMISS); hideSaveUiUiThread(mCallback); if (listener != null) { try { @@ -289,13 +293,13 @@ public final class AutoFillUI { if (mCallback != null) { mCallback.cancelSave(); } - destroySaveUiUiThread(pendingSaveUi); + destroySaveUiUiThread(pendingSaveUi, true); } @Override public void onDestroy() { - if (log.getType() == MetricsProto.MetricsEvent.TYPE_UNKNOWN) { - log.setType(MetricsProto.MetricsEvent.TYPE_CLOSE); + if (log.getType() == MetricsEvent.TYPE_UNKNOWN) { + log.setType(MetricsEvent.TYPE_CLOSE); if (mCallback != null) { mCallback.cancelSave(); @@ -331,8 +335,8 @@ public final class AutoFillUI { * Destroy all UI affordances. */ public void destroyAll(@Nullable PendingUi pendingSaveUi, - @Nullable AutoFillUiCallback callback) { - mHandler.post(() -> destroyAllUiThread(pendingSaveUi, callback)); + @Nullable AutoFillUiCallback callback, boolean notifyClient) { + mHandler.post(() -> destroyAllUiThread(pendingSaveUi, callback, notifyClient)); } public void dump(PrintWriter pw) { @@ -375,7 +379,7 @@ public final class AutoFillUI { } @android.annotation.UiThread - private void destroySaveUiUiThread(@Nullable PendingUi pendingSaveUi) { + private void destroySaveUiUiThread(@Nullable PendingUi pendingSaveUi, boolean notifyClient) { if (mSaveUi == null) { // Calling destroySaveUiUiThread() twice is normal - it usually happens when the // first call is made after the SaveUI is hidden and the second when the session is @@ -387,7 +391,7 @@ public final class AutoFillUI { if (sDebug) Slog.d(TAG, "destroySaveUiUiThread(): " + pendingSaveUi); mSaveUi.destroy(); mSaveUi = null; - if (pendingSaveUi != null) { + if (pendingSaveUi != null && notifyClient) { try { if (sDebug) Slog.d(TAG, "destroySaveUiUiThread(): notifying client"); pendingSaveUi.client.setSaveUiState(pendingSaveUi.id, false); @@ -399,9 +403,9 @@ public final class AutoFillUI { @android.annotation.UiThread private void destroyAllUiThread(@Nullable PendingUi pendingSaveUi, - @Nullable AutoFillUiCallback callback) { + @Nullable AutoFillUiCallback callback, boolean notifyClient) { hideFillUiUiThread(callback); - destroySaveUiUiThread(pendingSaveUi); + destroySaveUiUiThread(pendingSaveUi, notifyClient); } @android.annotation.UiThread @@ -413,7 +417,7 @@ public final class AutoFillUI { Slog.d(TAG, "hideAllUiThread(): " + "destroying Save UI because pending restoration is finished"); } - destroySaveUiUiThread(pendingSaveUi); + destroySaveUiUiThread(pendingSaveUi, true); } } } diff --git a/com/android/server/autofill/ui/FillUi.java b/com/android/server/autofill/ui/FillUi.java index 371e74d6..bf442dce 100644 --- a/com/android/server/autofill/ui/FillUi.java +++ b/com/android/server/autofill/ui/FillUi.java @@ -55,6 +55,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.regex.Pattern; final class FillUi { private static final String TAG = "FillUi"; @@ -164,15 +165,18 @@ final class FillUi { Slog.e(TAG, "Error inflating remote views", e); continue; } - final AutofillValue value = dataset.getFieldValues().get(index); + final Pattern filter = dataset.getFilter(index); String valueText = null; - // If the dataset needs auth - don't add its text to allow guessing - // its content based on how filtering behaves. - if (value != null && value.isText() && dataset.getAuthentication() == null) { - valueText = value.getTextValue().toString().toLowerCase(); + if (filter == null) { + final AutofillValue value = dataset.getFieldValues().get(index); + // If the dataset needs auth - don't add its text to allow guessing + // its content based on how filtering behaves. + if (value != null && value.isText() && dataset.getAuthentication() == null) { + valueText = value.getTextValue().toString().toLowerCase(); + } } - items.add(new ViewItem(dataset, valueText, view)); + items.add(new ViewItem(dataset, filter, valueText, view)); } } @@ -331,11 +335,17 @@ final class FillUi { private final String mValue; private final Dataset mDataset; private final View mView; + private final Pattern mFilter; - ViewItem(Dataset dataset, String value, View view) { + ViewItem(Dataset dataset, Pattern filter, String value, View view) { mDataset = dataset; mValue = value; mView = view; + mFilter = filter; + } + + public Pattern getFilter() { + return mFilter; } public View getView() { @@ -349,12 +359,6 @@ final class FillUi { public String getValue() { return mValue; } - - @Override - public String toString() { - // Used for filtering in the adapter - return mValue; - } } private final class AutofillWindowPresenter extends IAutofillWindowPresenter.Stub { @@ -516,10 +520,16 @@ final class FillUi { for (int i = 0; i < itemCount; i++) { final ViewItem item = mAllItems.get(i); final String value = item.getValue(); - // No value, i.e. null, matches any filter - if ((value == null && item.mDataset.getAuthentication() == null) - || (value != null - && value.toLowerCase().startsWith(constraintLowerCase))) { + final Pattern filter = item.getFilter(); + final boolean matches; + if (filter != null) { + matches = filter.matcher(constraintLowerCase).matches(); + } else { + matches = (value == null) + ? (item.mDataset.getAuthentication() == null) + : value.toLowerCase().startsWith(constraintLowerCase); + } + if (matches) { filteredItems.add(item); } } diff --git a/com/android/server/autofill/ui/SaveUi.java b/com/android/server/autofill/ui/SaveUi.java index d0b2e924..d48f23ca 100644 --- a/com/android/server/autofill/ui/SaveUi.java +++ b/com/android/server/autofill/ui/SaveUi.java @@ -20,16 +20,15 @@ import static com.android.server.autofill.Helper.sDebug; import static com.android.server.autofill.Helper.sVerbose; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.Dialog; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.IntentSender; import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.metrics.LogMaker; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; @@ -53,6 +52,8 @@ import android.widget.ScrollView; import android.widget.TextView; import com.android.internal.R; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.server.UiThread; import java.io.PrintWriter; @@ -111,6 +112,7 @@ final class SaveUi { } private final Handler mHandler = UiThread.getHandler(); + private final MetricsLogger mMetricsLogger = new MetricsLogger(); private final @NonNull Dialog mDialog; @@ -121,16 +123,21 @@ final class SaveUi { private final CharSequence mTitle; private final CharSequence mSubTitle; private final PendingUi mPendingUi; + private final String mServicePackageName; + private final String mPackageName; private boolean mDestroyed; SaveUi(@NonNull Context context, @NonNull PendingUi pendingUi, @NonNull CharSequence serviceLabel, @NonNull Drawable serviceIcon, + @Nullable String servicePackageName, @NonNull String packageName, @NonNull SaveInfo info, @NonNull ValueFinder valueFinder, @NonNull OverlayControl overlayControl, @NonNull OnSaveListener listener) { mPendingUi= pendingUi; mListener = new OneTimeListener(listener); mOverlayControl = overlayControl; + mServicePackageName = servicePackageName; + mPackageName = packageName; final LayoutInflater inflater = LayoutInflater.from(context); final View view = inflater.inflate(R.layout.autofill_save, null); @@ -181,6 +188,8 @@ final class SaveUi { ScrollView subtitleContainer = null; final CustomDescription customDescription = info.getCustomDescription(); if (customDescription != null) { + writeLog(MetricsEvent.AUTOFILL_SAVE_CUSTOM_DESCRIPTION, type); + mSubTitle = null; if (sDebug) Slog.d(TAG, "Using custom description"); @@ -190,40 +199,35 @@ final class SaveUi { @Override public boolean onClickHandler(View view, PendingIntent pendingIntent, Intent intent) { + final LogMaker log = + newLogMaker(MetricsEvent.AUTOFILL_SAVE_LINK_TAPPED, type); // We need to hide the Save UI before launching the pending intent, and // restore back it once the activity is finished, and that's achieved by // adding a custom extra in the activity intent. - if (pendingIntent != null) { - if (intent == null) { - Slog.w(TAG, - "remote view on custom description does not have intent"); - return false; - } - if (!pendingIntent.isActivity()) { - Slog.w(TAG, "ignoring custom description pending intent that's not " - + "for an activity: " + pendingIntent); - return false; - } - if (sVerbose) { - Slog.v(TAG, - "Intercepting custom description intent: " + intent); - } - final IBinder token = mPendingUi.getToken(); - intent.putExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN, token); - try { - pendingUi.client.startIntentSender(pendingIntent.getIntentSender(), - intent); - mPendingUi.setState(PendingUi.STATE_PENDING); - if (sDebug) { - Slog.d(TAG, "hiding UI until restored with token " + token); - } - hide(); - } catch (RemoteException e) { - Slog.w(TAG, "error triggering pending intent: " + intent); - return false; - } + final boolean isValid = isValidLink(pendingIntent, intent); + if (!isValid) { + log.setType(MetricsEvent.TYPE_UNKNOWN); + mMetricsLogger.write(log); + return false; + } + if (sVerbose) Slog.v(TAG, "Intercepting custom description intent"); + final IBinder token = mPendingUi.getToken(); + intent.putExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN, token); + try { + pendingUi.client.startIntentSender(pendingIntent.getIntentSender(), + intent); + mPendingUi.setState(PendingUi.STATE_PENDING); + if (sDebug) Slog.d(TAG, "hiding UI until restored with token " + token); + hide(); + log.setType(MetricsEvent.TYPE_OPEN); + mMetricsLogger.write(log); + return true; + } catch (RemoteException e) { + Slog.w(TAG, "error triggering pending intent: " + intent); + log.setType(MetricsEvent.TYPE_FAILURE); + mMetricsLogger.write(log); + return false; } - return true; } }; @@ -241,6 +245,7 @@ final class SaveUi { } else { mSubTitle = info.getDescription(); if (mSubTitle != null) { + writeLog(MetricsEvent.AUTOFILL_SAVE_CUSTOM_SUBTITLE, type); subtitleContainer = view.findViewById(R.id.autofill_save_custom_subtitle); final TextView subtitleView = new TextView(context); subtitleView.setText(mSubTitle); @@ -258,9 +263,7 @@ final class SaveUi { } else { noButton.setText(R.string.autofill_save_no); } - final View.OnClickListener cancelListener = - (v) -> mListener.onCancel(info.getNegativeActionListener()); - noButton.setOnClickListener(cancelListener); + noButton.setOnClickListener((v) -> mListener.onCancel(info.getNegativeActionListener())); final View yesButton = view.findViewById(R.id.autofill_save_yes); yesButton.setOnClickListener((v) -> mListener.onSave()); @@ -268,8 +271,9 @@ final class SaveUi { mDialog = new Dialog(context, R.style.Theme_DeviceDefault_Light_Panel); mDialog.setContentView(view); - // Dialog can be dismissed when touched outside. - mDialog.setOnDismissListener((d) -> mListener.onCancel(info.getNegativeActionListener())); + // Dialog can be dismissed when touched outside, but the negative listener should not be + // notified (hence the null argument). + mDialog.setOnDismissListener((d) -> mListener.onCancel(null)); final Window window = mDialog.getWindow(); window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); @@ -300,7 +304,7 @@ final class SaveUi { if (actualWidth <= maxWidth && actualHeight <= maxHeight) { if (sDebug) { - Slog.d(TAG, "Addingservice icon " + Slog.d(TAG, "Adding service icon " + "(" + actualWidth + "x" + actualHeight + ") as it's less than maximum " + "(" + maxWidth + "x" + maxHeight + ")."); } @@ -309,8 +313,39 @@ final class SaveUi { Slog.w(TAG, "Not adding service icon of size " + "(" + actualWidth + "x" + actualHeight + ") because maximum is " + "(" + maxWidth + "x" + maxHeight + ")."); - iconView.setVisibility(View.INVISIBLE); + ((ViewGroup)iconView.getParent()).removeView(iconView); + } + } + + private static boolean isValidLink(PendingIntent pendingIntent, Intent intent) { + if (pendingIntent == null) { + Slog.w(TAG, "isValidLink(): custom description without pending intent"); + return false; } + if (!pendingIntent.isActivity()) { + Slog.w(TAG, "isValidLink(): pending intent not for activity"); + return false; + } + if (intent == null) { + Slog.w(TAG, "isValidLink(): no intent"); + return false; + } + return true; + } + + private LogMaker newLogMaker(int category, int saveType) { + return newLogMaker(category) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SAVE_TYPE, saveType); + } + + private LogMaker newLogMaker(int category) { + return new LogMaker(category) + .setPackageName(mPackageName) + .addTaggedData(MetricsEvent.FIELD_AUTOFILL_SERVICE, mServicePackageName); + } + + private void writeLog(int category, int saveType) { + mMetricsLogger.write(newLogMaker(category, saveType)); } /** @@ -326,17 +361,25 @@ final class SaveUi { + mPendingUi.getToken()); return; } - switch (operation) { - case AutofillManager.PENDING_UI_OPERATION_RESTORE: - if (sDebug) Slog.d(TAG, "Restoring save dialog for " + token); - show(); - break; - case AutofillManager.PENDING_UI_OPERATION_CANCEL: - if (sDebug) Slog.d(TAG, "Cancelling pending save dialog for " + token); - hide(); - break; - default: - Slog.w(TAG, "restore(): invalid operation " + operation); + final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_PENDING_SAVE_UI_OPERATION); + try { + switch (operation) { + case AutofillManager.PENDING_UI_OPERATION_RESTORE: + if (sDebug) Slog.d(TAG, "Restoring save dialog for " + token); + log.setType(MetricsEvent.TYPE_OPEN); + show(); + break; + case AutofillManager.PENDING_UI_OPERATION_CANCEL: + log.setType(MetricsEvent.TYPE_DISMISS); + if (sDebug) Slog.d(TAG, "Cancelling pending save dialog for " + token); + hide(); + break; + default: + log.setType(MetricsEvent.TYPE_FAILURE); + Slog.w(TAG, "restore(): invalid operation " + operation); + } + } finally { + mMetricsLogger.write(log); } mPendingUi.setState(PendingUi.STATE_FINISHED); } @@ -385,6 +428,8 @@ final class SaveUi { pw.print(prefix); pw.print("title: "); pw.println(mTitle); pw.print(prefix); pw.print("subtitle: "); pw.println(mSubTitle); pw.print(prefix); pw.print("pendingUi: "); pw.println(mPendingUi); + pw.print(prefix); pw.print("service: "); pw.println(mServicePackageName); + pw.print(prefix); pw.print("app: "); pw.println(mPackageName); final View view = mDialog.getWindow().getDecorView(); final int[] loc = view.getLocationOnScreen(); diff --git a/com/android/server/backup/BackupManagerConstants.java b/com/android/server/backup/BackupManagerConstants.java index cd601826..245241cb 100644 --- a/com/android/server/backup/BackupManagerConstants.java +++ b/com/android/server/backup/BackupManagerConstants.java @@ -123,7 +123,7 @@ class BackupManagerConstants extends ContentObserver { // group the calls of these methods in a block syncrhonized on // a reference of this object. public synchronized long getKeyValueBackupIntervalMilliseconds() { - if (BackupManagerService.DEBUG_SCHEDULING) { + if (RefactoredBackupManagerService.DEBUG_SCHEDULING) { Slog.v(TAG, "getKeyValueBackupIntervalMilliseconds(...) returns " + mKeyValueBackupIntervalMilliseconds); } @@ -131,7 +131,7 @@ class BackupManagerConstants extends ContentObserver { } public synchronized long getKeyValueBackupFuzzMilliseconds() { - if (BackupManagerService.DEBUG_SCHEDULING) { + if (RefactoredBackupManagerService.DEBUG_SCHEDULING) { Slog.v(TAG, "getKeyValueBackupFuzzMilliseconds(...) returns " + mKeyValueBackupFuzzMilliseconds); } @@ -139,7 +139,7 @@ class BackupManagerConstants extends ContentObserver { } public synchronized boolean getKeyValueBackupRequireCharging() { - if (BackupManagerService.DEBUG_SCHEDULING) { + if (RefactoredBackupManagerService.DEBUG_SCHEDULING) { Slog.v(TAG, "getKeyValueBackupRequireCharging(...) returns " + mKeyValueBackupRequireCharging); } @@ -147,7 +147,7 @@ class BackupManagerConstants extends ContentObserver { } public synchronized int getKeyValueBackupRequiredNetworkType() { - if (BackupManagerService.DEBUG_SCHEDULING) { + if (RefactoredBackupManagerService.DEBUG_SCHEDULING) { Slog.v(TAG, "getKeyValueBackupRequiredNetworkType(...) returns " + mKeyValueBackupRequiredNetworkType); } @@ -155,7 +155,7 @@ class BackupManagerConstants extends ContentObserver { } public synchronized long getFullBackupIntervalMilliseconds() { - if (BackupManagerService.DEBUG_SCHEDULING) { + if (RefactoredBackupManagerService.DEBUG_SCHEDULING) { Slog.v(TAG, "getFullBackupIntervalMilliseconds(...) returns " + mFullBackupIntervalMilliseconds); } @@ -163,7 +163,7 @@ class BackupManagerConstants extends ContentObserver { } public synchronized boolean getFullBackupRequireCharging() { - if (BackupManagerService.DEBUG_SCHEDULING) { + if (RefactoredBackupManagerService.DEBUG_SCHEDULING) { Slog.v(TAG, "getFullBackupRequireCharging(...) returns " + mFullBackupRequireCharging); } return mFullBackupRequireCharging; @@ -171,7 +171,7 @@ class BackupManagerConstants extends ContentObserver { } public synchronized int getFullBackupRequiredNetworkType() { - if (BackupManagerService.DEBUG_SCHEDULING) { + if (RefactoredBackupManagerService.DEBUG_SCHEDULING) { Slog.v(TAG, "getFullBackupRequiredNetworkType(...) returns " + mFullBackupRequiredNetworkType); } diff --git a/com/android/server/backup/BackupManagerService.java b/com/android/server/backup/BackupManagerService.java index f5257978..eabe21fe 100644 --- a/com/android/server/backup/BackupManagerService.java +++ b/com/android/server/backup/BackupManagerService.java @@ -192,6 +192,10 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; +/** + * @Deprecated Use RefactoredBackupManagerService instead. This class is only + * kept for fallback and archeology reasons and will be removed soon. + */ public class BackupManagerService implements BackupManagerServiceInterface { private static final String TAG = "BackupManagerService"; @@ -397,43 +401,51 @@ public class BackupManagerService implements BackupManagerServiceInterface { @Override public void onUnlockUser(int userId) { if (userId == UserHandle.USER_SYSTEM) { - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init"); - sInstance.initialize(userId); - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + sInstance.unlockSystemUser(); + } + } + } + + // Called through the trampoline from onUnlockUser(), then we buck the work + // off to the background thread to keep the unlock time down. + public void unlockSystemUser() { + mBackupHandler.post(() -> { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init"); + sInstance.initialize(UserHandle.USER_SYSTEM); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - // Migrate legacy setting - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate"); - if (!backupSettingMigrated(userId)) { + // Migrate legacy setting + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate"); + if (!backupSettingMigrated(UserHandle.USER_SYSTEM)) { + if (DEBUG) { + Slog.i(TAG, "Backup enable apparently not migrated"); + } + final ContentResolver r = sInstance.mContext.getContentResolver(); + final int enableState = Settings.Secure.getIntForUser(r, + Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM); + if (enableState >= 0) { if (DEBUG) { - Slog.i(TAG, "Backup enable apparently not migrated"); + Slog.i(TAG, "Migrating enable state " + (enableState != 0)); } - final ContentResolver r = sInstance.mContext.getContentResolver(); - final int enableState = Settings.Secure.getIntForUser(r, - Settings.Secure.BACKUP_ENABLED, -1, userId); - if (enableState >= 0) { - if (DEBUG) { - Slog.i(TAG, "Migrating enable state " + (enableState != 0)); - } - writeBackupEnableState(enableState != 0, userId); - Settings.Secure.putStringForUser(r, - Settings.Secure.BACKUP_ENABLED, null, userId); - } else { - if (DEBUG) { - Slog.i(TAG, "Backup not yet configured; retaining null enable state"); - } + writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM); + Settings.Secure.putStringForUser(r, + Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM); + } else { + if (DEBUG) { + Slog.i(TAG, "Backup not yet configured; retaining null enable state"); } } - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable"); - try { - sInstance.setBackupEnabled(readBackupEnableState(userId)); - } catch (RemoteException e) { - // can't happen; it's a local object - } - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable"); + try { + sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM)); + } catch (RemoteException e) { + // can't happen; it's a local object } - } + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + }); } class ProvisionedObserver extends ContentObserver { @@ -1983,7 +1995,7 @@ public class BackupManagerService implements BackupManagerServiceInterface { if (uri == null) { return; } - String pkgName = uri.getSchemeSpecificPart(); + final String pkgName = uri.getSchemeSpecificPart(); if (pkgName != null) { pkgList = new String[] { pkgName }; } @@ -1991,7 +2003,7 @@ public class BackupManagerService implements BackupManagerServiceInterface { // At package-changed we only care about looking at new transport states if (changed) { - String[] components = + final String[] components = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST); if (MORE_DEBUG) { @@ -2001,7 +2013,8 @@ public class BackupManagerService implements BackupManagerServiceInterface { } } - mTransportManager.onPackageChanged(pkgName, components); + mBackupHandler.post( + () -> mTransportManager.onPackageChanged(pkgName, components)); return; // nothing more to do in the PACKAGE_CHANGED case } @@ -2033,7 +2046,7 @@ public class BackupManagerService implements BackupManagerServiceInterface { } // If they're full-backup candidates, add them there instead final long now = System.currentTimeMillis(); - for (String packageName : pkgList) { + for (final String packageName : pkgList) { try { PackageInfo app = mPackageManager.getPackageInfo(packageName, 0); if (appGetsFullBackup(app) @@ -2050,7 +2063,8 @@ public class BackupManagerService implements BackupManagerServiceInterface { writeFullBackupScheduleAsync(); } - mTransportManager.onPackageAdded(packageName); + mBackupHandler.post( + () -> mTransportManager.onPackageAdded(packageName)); } catch (NameNotFoundException e) { // doesn't really exist; ignore it @@ -2074,8 +2088,9 @@ public class BackupManagerService implements BackupManagerServiceInterface { removePackageParticipantsLocked(pkgList, uid); } } - for (String pkgName : pkgList) { - mTransportManager.onPackageRemoved(pkgName); + for (final String pkgName : pkgList) { + mBackupHandler.post( + () -> mTransportManager.onPackageRemoved(pkgName)); } } } diff --git a/com/android/server/backup/BackupManagerServiceInterface.java b/com/android/server/backup/BackupManagerServiceInterface.java index 5dfa6302..041f9ed5 100644 --- a/com/android/server/backup/BackupManagerServiceInterface.java +++ b/com/android/server/backup/BackupManagerServiceInterface.java @@ -39,6 +39,8 @@ import java.io.PrintWriter; */ public interface BackupManagerServiceInterface { + void unlockSystemUser(); + // Utility: build a new random integer token int generateRandomIntegerToken(); diff --git a/com/android/server/backup/FullBackupJob.java b/com/android/server/backup/FullBackupJob.java index 82638b4e..b81a54d3 100644 --- a/com/android/server/backup/FullBackupJob.java +++ b/com/android/server/backup/FullBackupJob.java @@ -61,7 +61,7 @@ public class FullBackupJob extends JobService { @Override public boolean onStartJob(JobParameters params) { mParams = params; - Trampoline service = BackupManagerService.getInstance(); + Trampoline service = RefactoredBackupManagerService.getInstance(); return service.beginFullBackup(this); } @@ -69,7 +69,7 @@ public class FullBackupJob extends JobService { public boolean onStopJob(JobParameters params) { if (mParams != null) { mParams = null; - Trampoline service = BackupManagerService.getInstance(); + Trampoline service = RefactoredBackupManagerService.getInstance(); service.endFullBackup(); } return false; diff --git a/com/android/server/backup/KeyValueAdbBackupEngine.java b/com/android/server/backup/KeyValueAdbBackupEngine.java index 279c8284..b38b25a3 100644 --- a/com/android/server/backup/KeyValueAdbBackupEngine.java +++ b/com/android/server/backup/KeyValueAdbBackupEngine.java @@ -4,8 +4,8 @@ import static android.os.ParcelFileDescriptor.MODE_CREATE; import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; import static android.os.ParcelFileDescriptor.MODE_TRUNCATE; -import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT; -import static com.android.server.backup.BackupManagerService.TIMEOUT_BACKUP_INTERVAL; +import static com.android.server.backup.RefactoredBackupManagerService.OP_TYPE_BACKUP_WAIT; +import static com.android.server.backup.RefactoredBackupManagerService.TIMEOUT_BACKUP_INTERVAL; import android.app.ApplicationThreadConstants; import android.app.IBackupAgent; @@ -19,6 +19,8 @@ import android.os.RemoteException; import android.os.SELinux; import android.util.Slog; +import com.android.server.backup.utils.FullBackupUtils; + import libcore.io.IoUtils; import java.io.File; @@ -78,7 +80,7 @@ public class KeyValueAdbBackupEngine { mNewStateName = new File(mStateDir, pkg + BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX); - mManifestFile = new File(mDataDir, BackupManagerService.BACKUP_MANIFEST_FILENAME); + mManifestFile = new File(mDataDir, RefactoredBackupManagerService.BACKUP_MANIFEST_FILENAME); } public void backupOnePackage() throws IOException { @@ -188,7 +190,7 @@ public class KeyValueAdbBackupEngine { if (DEBUG) { Slog.d(TAG, "Writing manifest for " + mPackage.packageName); } - BackupManagerService.writeAppManifest( + FullBackupUtils.writeAppManifest( mPackage, mPackageManager, mManifestFile, false, false); FullBackup.backupToTar(mPackage.packageName, FullBackup.KEY_VALUE_DATA_TOKEN, null, mDataDir.getAbsolutePath(), @@ -251,7 +253,7 @@ public class KeyValueAdbBackupEngine { t.start(); // Now pull data from the app and stuff it into the output - BackupManagerService.routeSocketDataToOutput(pipes[0], mOutput); + FullBackupUtils.routeSocketDataToOutput(pipes[0], mOutput); if (!mBackupManagerService.waitUntilOperationComplete(token)) { Slog.e(TAG, "Full backup failed on package " + mCurrentPackage.packageName); diff --git a/com/android/server/backup/KeyValueAdbRestoreEngine.java b/com/android/server/backup/KeyValueAdbRestoreEngine.java index b62bb5c9..a2de8e73 100644 --- a/com/android/server/backup/KeyValueAdbRestoreEngine.java +++ b/com/android/server/backup/KeyValueAdbRestoreEngine.java @@ -13,6 +13,8 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Slog; +import com.android.server.backup.restore.PerformAdbRestoreTask; + import libcore.io.IoUtils; import java.io.File; @@ -41,7 +43,7 @@ public class KeyValueAdbRestoreEngine implements Runnable { private final File mDataDir; FileMetadata mInfo; - BackupManagerService.PerformAdbRestoreTask mRestoreTask; + PerformAdbRestoreTask mRestoreTask; ParcelFileDescriptor mInFD; IBackupAgent mAgent; int mToken; diff --git a/com/android/server/backup/KeyValueBackupJob.java b/com/android/server/backup/KeyValueBackupJob.java index d8411e2a..5dfb0bce 100644 --- a/com/android/server/backup/KeyValueBackupJob.java +++ b/com/android/server/backup/KeyValueBackupJob.java @@ -71,7 +71,7 @@ public class KeyValueBackupJob extends JobService { if (delay <= 0) { delay = interval + new Random().nextInt((int) fuzz); } - if (BackupManagerService.DEBUG_SCHEDULING) { + if (RefactoredBackupManagerService.DEBUG_SCHEDULING) { Slog.v(TAG, "Scheduling k/v pass in " + (delay / 1000 / 60) + " minutes"); } JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sKeyValueJobService) @@ -110,7 +110,7 @@ public class KeyValueBackupJob extends JobService { } // Time to run a key/value backup! - Trampoline service = BackupManagerService.getInstance(); + Trampoline service = RefactoredBackupManagerService.getInstance(); try { service.backupNow(); } catch (RemoteException e) {} diff --git a/com/android/server/backup/PackageManagerBackupAgent.java b/com/android/server/backup/PackageManagerBackupAgent.java index 8d91e0dd..f658f22d 100644 --- a/com/android/server/backup/PackageManagerBackupAgent.java +++ b/com/android/server/backup/PackageManagerBackupAgent.java @@ -30,6 +30,8 @@ import android.os.Build; import android.os.ParcelFileDescriptor; import android.util.Slog; +import com.android.server.backup.utils.AppBackupUtils; + import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; @@ -140,7 +142,7 @@ public class PackageManagerBackupAgent extends BackupAgent { int N = pkgs.size(); for (int a = N-1; a >= 0; a--) { PackageInfo pkg = pkgs.get(a); - if (!BackupManagerService.appIsEligibleForBackup(pkg.applicationInfo, pm)) { + if (!AppBackupUtils.appIsEligibleForBackup(pkg.applicationInfo, pm)) { pkgs.remove(a); } } diff --git a/com/android/server/backup/ProcessedPackagesJournal.java b/com/android/server/backup/ProcessedPackagesJournal.java index 187d5d93..e29b7d58 100644 --- a/com/android/server/backup/ProcessedPackagesJournal.java +++ b/com/android/server/backup/ProcessedPackagesJournal.java @@ -21,7 +21,10 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.server.backup.RefactoredBackupManagerService; +import java.io.BufferedInputStream; +import java.io.DataInputStream; import java.io.EOFException; +import java.io.FileInputStream; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; @@ -130,7 +133,8 @@ final class ProcessedPackagesJournal { return; } - try (RandomAccessFile oldJournal = new RandomAccessFile(journalFile, "r")) { + try (DataInputStream oldJournal = new DataInputStream( + new BufferedInputStream(new FileInputStream(journalFile)))) { while (true) { String packageName = oldJournal.readUTF(); if (DEBUG) { diff --git a/com/android/server/backup/RefactoredBackupManagerService.java b/com/android/server/backup/RefactoredBackupManagerService.java index 141f9207..f2980659 100644 --- a/com/android/server/backup/RefactoredBackupManagerService.java +++ b/com/android/server/backup/RefactoredBackupManagerService.java @@ -77,6 +77,7 @@ import android.os.RemoteException; import android.os.SELinux; import android.os.ServiceManager; import android.os.SystemClock; +import android.os.Trace; import android.os.UserHandle; import android.os.storage.IStorageManager; import android.os.storage.StorageManager; @@ -149,6 +150,7 @@ import java.util.Queue; import java.util.Random; import java.util.Set; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicInteger; public class RefactoredBackupManagerService implements BackupManagerServiceInterface { @@ -546,37 +548,51 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter @Override public void onUnlockUser(int userId) { if (userId == UserHandle.USER_SYSTEM) { - sInstance.initialize(userId); + sInstance.unlockSystemUser(); + } + } + } + + // Called through the trampoline from onUnlockUser(), then we buck the work + // off to the background thread to keep the unlock time down. + public void unlockSystemUser() { + mBackupHandler.post(() -> { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init"); + sInstance.initialize(UserHandle.USER_SYSTEM); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - // Migrate legacy setting - if (!backupSettingMigrated(userId)) { + // Migrate legacy setting + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate"); + if (!backupSettingMigrated(UserHandle.USER_SYSTEM)) { + if (DEBUG) { + Slog.i(TAG, "Backup enable apparently not migrated"); + } + final ContentResolver r = sInstance.mContext.getContentResolver(); + final int enableState = Settings.Secure.getIntForUser(r, + Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM); + if (enableState >= 0) { if (DEBUG) { - Slog.i(TAG, "Backup enable apparently not migrated"); + Slog.i(TAG, "Migrating enable state " + (enableState != 0)); } - final ContentResolver r = sInstance.mContext.getContentResolver(); - final int enableState = Settings.Secure.getIntForUser(r, - Settings.Secure.BACKUP_ENABLED, -1, userId); - if (enableState >= 0) { - if (DEBUG) { - Slog.i(TAG, "Migrating enable state " + (enableState != 0)); - } - writeBackupEnableState(enableState != 0, userId); - Settings.Secure.putStringForUser(r, - Settings.Secure.BACKUP_ENABLED, null, userId); - } else { - if (DEBUG) { - Slog.i(TAG, "Backup not yet configured; retaining null enable state"); - } + writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM); + Settings.Secure.putStringForUser(r, + Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM); + } else { + if (DEBUG) { + Slog.i(TAG, "Backup not yet configured; retaining null enable state"); } } + } + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); - try { - sInstance.setBackupEnabled(readBackupEnableState(userId)); - } catch (RemoteException e) { - // can't happen; it's a local object - } + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable"); + try { + sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM)); + } catch (RemoteException e) { + // can't happen; it's a local object } - } + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + }); } // Bookkeeping of in-flight operations for timeout etc. purposes. The operation @@ -619,6 +635,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter private final SparseArray<Operation> mCurrentOperations = new SparseArray<>(); private final Object mCurrentOpLock = new Object(); private final Random mTokenGenerator = new Random(); + final AtomicInteger mNextToken = new AtomicInteger(); private final SparseArray<AdbParams> mAdbBackupRestoreConfirmations = new SparseArray<>(); @@ -644,7 +661,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter // Persistently track the need to do a full init private static final String INIT_SENTINEL_FILE_NAME = "_need_init_"; - private ArraySet<String> mPendingInits = new ArraySet<>(); // transport names + private final ArraySet<String> mPendingInits = new ArraySet<>(); // transport names // Round-robin queue for scheduling full backup passes private static final int SCHEDULE_FILE_VERSION = 1; // current version of the schedule file @@ -658,18 +675,41 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter @GuardedBy("mQueueLock") private ArrayList<FullBackupEntry> mFullBackupQueue; - // Utility: build a new random integer token + // Utility: build a new random integer token. The low bits are the ordinal of the + // operation for near-time uniqueness, and the upper bits are random for app- + // side unpredictability. @Override public int generateRandomIntegerToken() { - int token; - do { - synchronized (mTokenGenerator) { - token = mTokenGenerator.nextInt(); - } - } while (token < 0); + int token = mTokenGenerator.nextInt(); + if (token < 0) token = -token; + token &= ~0xFF; + token |= (mNextToken.incrementAndGet() & 0xFF); return token; } + /* + * Construct a backup agent instance for the metadata pseudopackage. This is a + * process-local non-lifecycle agent instance, so we manually set up the context + * topology for it. + */ + public PackageManagerBackupAgent makeMetadataAgent() { + PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(mPackageManager); + pmAgent.attach(mContext); + pmAgent.onCreate(); + return pmAgent; + } + + /* + * Same as above but with the explicit package-set configuration. + */ + public PackageManagerBackupAgent makeMetadataAgent(List<PackageInfo> packages) { + PackageManagerBackupAgent pmAgent = + new PackageManagerBackupAgent(mPackageManager, packages); + pmAgent.attach(mContext); + pmAgent.onCreate(); + return pmAgent; + } + // ----- Debug-only backup operation trace ----- public void addBackupTrace(String s) { if (DEBUG_BACKUP_TRACE) { @@ -800,17 +840,18 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter // Remember our ancestral dataset mTokenFile = new File(mBaseStateDir, "ancestral"); - try (RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r")) { - int version = tf.readInt(); + try (DataInputStream tokenStream = new DataInputStream(new BufferedInputStream( + new FileInputStream(mTokenFile)))) { + int version = tokenStream.readInt(); if (version == CURRENT_ANCESTRAL_RECORD_VERSION) { - mAncestralToken = tf.readLong(); - mCurrentToken = tf.readLong(); + mAncestralToken = tokenStream.readLong(); + mCurrentToken = tokenStream.readLong(); - int numPackages = tf.readInt(); + int numPackages = tokenStream.readInt(); if (numPackages >= 0) { mAncestralPackages = new HashSet<>(); for (int i = 0; i < numPackages; i++) { - String pkgName = tf.readUTF(); + String pkgName = tokenStream.readUTF(); mAncestralPackages.add(pkgName); } } @@ -878,7 +919,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter PackageInfo pkg = mPackageManager.getPackageInfo(pkgName, 0); if (AppBackupUtils.appGetsFullBackup(pkg) && AppBackupUtils.appIsEligibleForBackup( - pkg.applicationInfo)) { + pkg.applicationInfo, mPackageManager)) { schedule.add(new FullBackupEntry(pkgName, lastBackup)); } else { if (DEBUG) { @@ -899,7 +940,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter for (PackageInfo app : apps) { if (AppBackupUtils.appGetsFullBackup(app) && AppBackupUtils.appIsEligibleForBackup( - app.applicationInfo)) { + app.applicationInfo, mPackageManager)) { if (!foundApps.contains(app.packageName)) { if (MORE_DEBUG) { Slog.i(TAG, "New full backup app " + app.packageName + " found"); @@ -925,7 +966,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter schedule = new ArrayList<>(apps.size()); for (PackageInfo info : apps) { if (AppBackupUtils.appGetsFullBackup(info) && AppBackupUtils.appIsEligibleForBackup( - info.applicationInfo)) { + info.applicationInfo, mPackageManager)) { schedule.add(new FullBackupEntry(info.packageName, 0)); } } @@ -1155,7 +1196,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter if (uri == null) { return; } - String pkgName = uri.getSchemeSpecificPart(); + final String pkgName = uri.getSchemeSpecificPart(); if (pkgName != null) { pkgList = new String[]{pkgName}; } @@ -1163,7 +1204,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter // At package-changed we only care about looking at new transport states if (changed) { - String[] components = + final String[] components = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST); if (MORE_DEBUG) { @@ -1173,7 +1214,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } } - mTransportManager.onPackageChanged(pkgName, components); + mBackupHandler.post( + () -> mTransportManager.onPackageChanged(pkgName, components)); return; // nothing more to do in the PACKAGE_CHANGED case } @@ -1205,12 +1247,12 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } // If they're full-backup candidates, add them there instead final long now = System.currentTimeMillis(); - for (String packageName : pkgList) { + for (final String packageName : pkgList) { try { PackageInfo app = mPackageManager.getPackageInfo(packageName, 0); if (AppBackupUtils.appGetsFullBackup(app) && AppBackupUtils.appIsEligibleForBackup( - app.applicationInfo)) { + app.applicationInfo, mPackageManager)) { enqueueFullBackup(packageName, now); scheduleNextFullBackupJob(0); } else { @@ -1223,7 +1265,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter writeFullBackupScheduleAsync(); } - mTransportManager.onPackageAdded(packageName); + mBackupHandler.post( + () -> mTransportManager.onPackageAdded(packageName)); } catch (NameNotFoundException e) { // doesn't really exist; ignore it @@ -1247,8 +1290,9 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter removePackageParticipantsLocked(pkgList, uid); } } - for (String pkgName : pkgList) { - mTransportManager.onPackageRemoved(pkgName); + for (final String pkgName : pkgList) { + mBackupHandler.post( + () -> mTransportManager.onPackageRemoved(pkgName)); } } } @@ -1507,7 +1551,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter long token = mAncestralToken; synchronized (mQueueLock) { - if (mProcessedPackagesJournal.hasBeenProcessed(packageName)) { + if (mCurrentToken != 0 && mProcessedPackagesJournal.hasBeenProcessed(packageName)) { if (MORE_DEBUG) { Slog.i(TAG, "App in ever-stored, so using current token"); } @@ -1568,7 +1612,8 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter try { PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); - if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo)) { + if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo, + mPackageManager)) { BackupObserverUtils.sendBackupOnPackageResult(observer, packageName, BackupManager.ERROR_BACKUP_NOT_ALLOWED); continue; @@ -1759,8 +1804,12 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter // Can't delete op from mCurrentOperations here. waitUntilOperationComplete may be // called after we receive cancel here. We need this op's state there. - // Remove all pending timeout messages for this operation type. - mBackupHandler.removeMessages(getMessageIdForOperationType(op.type)); + // Remove all pending timeout messages of types OP_TYPE_BACKUP_WAIT and + // OP_TYPE_RESTORE_WAIT. On the other hand, OP_TYPE_BACKUP cannot time out and + // doesn't require cancellation. + if (op.type == OP_TYPE_BACKUP_WAIT || op.type == OP_TYPE_RESTORE_WAIT) { + mBackupHandler.removeMessages(getMessageIdForOperationType(op.type)); + } } mCurrentOpLock.notifyAll(); } @@ -2108,14 +2157,26 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter // so tear down any ongoing backup task right away. @Override public void endFullBackup() { - synchronized (mQueueLock) { - if (mRunningFullBackupTask != null) { - if (DEBUG_SCHEDULING) { - Slog.i(TAG, "Telling running backup to stop"); + // offload the mRunningFullBackupTask.handleCancel() call to another thread, + // as we might have to wait for mCancelLock + Runnable endFullBackupRunnable = new Runnable() { + @Override + public void run() { + PerformFullTransportBackupTask pftbt = null; + synchronized (mQueueLock) { + if (mRunningFullBackupTask != null) { + pftbt = mRunningFullBackupTask; + } + } + if (pftbt != null) { + if (DEBUG_SCHEDULING) { + Slog.i(TAG, "Telling running backup to stop"); + } + pftbt.handleCancel(true); } - mRunningFullBackupTask.handleCancel(true); } - } + }; + new Thread(endFullBackupRunnable, "end-full-backup").start(); } // Used by both incremental and full restore @@ -2800,8 +2861,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter final long oldId = Binder.clearCallingIdentity(); try { String prevTransport = mTransportManager.selectTransport(transport); - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.BACKUP_TRANSPORT, transport); + updateStateForTransport(transport); Slog.v(TAG, "selectBackupTransport() set " + mTransportManager.getCurrentTransportName() + " returning " + prevTransport); return prevTransport; @@ -2826,9 +2886,7 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter @Override public void onSuccess(String transportName) { mTransportManager.selectTransport(transportName); - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.BACKUP_TRANSPORT, - mTransportManager.getCurrentTransportName()); + updateStateForTransport(mTransportManager.getCurrentTransportName()); Slog.v(TAG, "Transport successfully selected: " + transport.flattenToShortString()); try { @@ -2853,6 +2911,28 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter Binder.restoreCallingIdentity(oldId); } + private void updateStateForTransport(String newTransportName) { + // Publish the name change + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.BACKUP_TRANSPORT, newTransportName); + + // And update our current-dataset bookkeeping + IBackupTransport transport = mTransportManager.getTransportBinder(newTransportName); + if (transport != null) { + try { + mCurrentToken = transport.getCurrentRestoreSet(); + } catch (Exception e) { + // Oops. We can't know the current dataset token, so reset and figure it out + // when we do the next k/v backup operation on this transport. + mCurrentToken = 0; + } + } else { + // The named transport isn't bound at this particular moment, so we can't + // know yet what its current dataset token is. Reset as above. + mCurrentToken = 0; + } + } + // Supply the configuration Intent for the given transport. If the name is not one // of the available transports, or if the transport does not supply any configuration // UI, the method returns null. @@ -3162,19 +3242,6 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter } } - // We also avoid backups of 'disabled' apps - private static boolean appIsDisabled(ApplicationInfo app, PackageManager pm) { - switch (pm.getApplicationEnabledSetting(app.packageName)) { - case PackageManager.COMPONENT_ENABLED_STATE_DISABLED: - case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER: - case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED: - return true; - - default: - return false; - } - } - @Override public boolean isAppEligibleForBackup(String packageName) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, @@ -3182,9 +3249,10 @@ public class RefactoredBackupManagerService implements BackupManagerServiceInter try { PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); - if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo) || + if (!AppBackupUtils.appIsEligibleForBackup(packageInfo.applicationInfo, + mPackageManager) || AppBackupUtils.appIsStopped(packageInfo.applicationInfo) || - appIsDisabled(packageInfo.applicationInfo, mPackageManager)) { + AppBackupUtils.appIsDisabled(packageInfo.applicationInfo, mPackageManager)) { return false; } IBackupTransport transport = mTransportManager.getCurrentTransportBinder(); diff --git a/com/android/server/backup/Trampoline.java b/com/android/server/backup/Trampoline.java index fcd929a7..9739e380 100644 --- a/com/android/server/backup/Trampoline.java +++ b/com/android/server/backup/Trampoline.java @@ -98,7 +98,7 @@ public class Trampoline extends IBackupManager.Stub { protected boolean isRefactoredServiceEnabled() { return Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.BACKUP_REFACTORED_SERVICE_DISABLED, 1) == 0; + Settings.Global.BACKUP_REFACTORED_SERVICE_DISABLED, 0) == 0; } protected int binderGetCallingUid() { @@ -139,6 +139,13 @@ public class Trampoline extends IBackupManager.Stub { } } + void unlockSystemUser() { + BackupManagerServiceInterface svc = mService; + if (svc != null) { + svc.unlockSystemUser(); + } + } + public void setBackupServiceActive(final int userHandle, boolean makeActive) { // Only the DPM should be changing the active state of backup final int caller = binderGetCallingUid(); diff --git a/com/android/server/backup/TransportManager.java b/com/android/server/backup/TransportManager.java index 9aae3841..7a0173f6 100644 --- a/com/android/server/backup/TransportManager.java +++ b/com/android/server/backup/TransportManager.java @@ -341,9 +341,9 @@ public class TransportManager { private class TransportConnection implements ServiceConnection { // Hold mTransportsLock to access these fields so as to provide a consistent view of them. - private IBackupTransport mBinder; + private volatile IBackupTransport mBinder; private final List<TransportReadyCallback> mListeners = new ArrayList<>(); - private String mTransportName; + private volatile String mTransportName; private final ComponentName mTransportComponent; @@ -426,25 +426,24 @@ public class TransportManager { + rebindTimeout + "ms"); } + // Intentionally not synchronized -- the variable is volatile and changes to its value + // are inside synchronized blocks, providing a memory sync barrier; and this method + // does not touch any other state protected by that lock. private IBackupTransport getBinder() { - synchronized (mTransportLock) { - return mBinder; - } + return mBinder; } + // Intentionally not synchronized; same as getBinder() private String getName() { - synchronized (mTransportLock) { - return mTransportName; - } + return mTransportName; } + // Intentionally not synchronized; same as getBinder() private void bindIfUnbound() { - synchronized (mTransportLock) { - if (mBinder == null) { - Slog.d(TAG, - "Rebinding to transport " + mTransportComponent.flattenToShortString()); - bindToTransport(mTransportComponent, this); - } + if (mBinder == null) { + Slog.d(TAG, + "Rebinding to transport " + mTransportComponent.flattenToShortString()); + bindToTransport(mTransportComponent, this); } } diff --git a/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/com/android/server/backup/fullbackup/PerformAdbBackupTask.java index 4085f63a..f0b3e4a0 100644 --- a/com/android/server/backup/fullbackup/PerformAdbBackupTask.java +++ b/com/android/server/backup/fullbackup/PerformAdbBackupTask.java @@ -236,12 +236,11 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor obbConnection.establish(); // we'll want this later sendStartBackup(); + PackageManager pm = backupManagerService.getPackageManager(); // doAllApps supersedes the package set if any if (mAllApps) { - List<PackageInfo> allPackages = - backupManagerService.getPackageManager().getInstalledPackages( - PackageManager.GET_SIGNATURES); + List<PackageInfo> allPackages = pm.getInstalledPackages(PackageManager.GET_SIGNATURES); for (int i = 0; i < allPackages.size(); i++) { PackageInfo pkg = allPackages.get(i); // Exclude system apps if we've been asked to do so @@ -288,7 +287,7 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator(); while (iter.hasNext()) { PackageInfo pkg = iter.next().getValue(); - if (!AppBackupUtils.appIsEligibleForBackup(pkg.applicationInfo) + if (!AppBackupUtils.appIsEligibleForBackup(pkg.applicationInfo, pm) || AppBackupUtils.appIsStopped(pkg.applicationInfo)) { iter.remove(); if (DEBUG) { diff --git a/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java index bc7c1174..90134e1a 100644 --- a/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java +++ b/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java @@ -140,10 +140,10 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba for (String pkg : whichPackages) { try { - PackageInfo info = backupManagerService.getPackageManager().getPackageInfo(pkg, - PackageManager.GET_SIGNATURES); + PackageManager pm = backupManagerService.getPackageManager(); + PackageInfo info = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES); mCurrentPackage = info; - if (!AppBackupUtils.appIsEligibleForBackup(info.applicationInfo)) { + if (!AppBackupUtils.appIsEligibleForBackup(info.applicationInfo, pm)) { // Cull any packages that have indicated that backups are not permitted, // that run as system-domain uids but do not define their own backup agents, // as well as any explicit mention of the 'special' shared-storage agent @@ -306,6 +306,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba final int N = mPackages.size(); final byte[] buffer = new byte[8192]; for (int i = 0; i < N; i++) { + mBackupRunner = null; PackageInfo currentPackage = mPackages.get(i); String packageName = currentPackage.packageName; if (DEBUG) { @@ -491,7 +492,13 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba } EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE, packageName, "transport rejected"); - // Do nothing, clean up, and continue looping. + // This failure state can come either a-priori from the transport, or + // from the preflight pass. If we got as far as preflight, we now need + // to tear down the target process. + if (mBackupRunner != null) { + backupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo); + } + // ... and continue looping. } else if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) { BackupObserverUtils .sendBackupOnPackageResult(mBackupObserver, packageName, @@ -501,6 +508,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba EventLog.writeEvent(EventLogTags.FULL_BACKUP_QUOTA_EXCEEDED, packageName); } + backupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo); // Do nothing, clean up, and continue looping. } else if (backupPackageStatus == BackupTransport.AGENT_ERROR) { BackupObserverUtils @@ -527,6 +535,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE); // Abort entire backup pass. backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED; + backupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo); return; } else { // Success! diff --git a/com/android/server/backup/internal/PerformBackupTask.java b/com/android/server/backup/internal/PerformBackupTask.java index ce4f906e..7a8a920e 100644 --- a/com/android/server/backup/internal/PerformBackupTask.java +++ b/com/android/server/backup/internal/PerformBackupTask.java @@ -227,9 +227,8 @@ public class PerformBackupTask implements BackupRestoreTask { if (!mFinished) { finalizeBackup(); } else { - Slog.e(TAG, "Duplicate finish"); + Slog.e(TAG, "Duplicate finish of K/V pass"); } - mFinished = true; break; } } @@ -322,8 +321,7 @@ public class PerformBackupTask implements BackupRestoreTask { // because it's cheap and this way we guarantee that we don't get out of // step even if we're selecting among various transports at run time. if (mStatus == BackupTransport.TRANSPORT_OK) { - PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( - backupManagerService.getPackageManager()); + PackageManagerBackupAgent pmAgent = backupManagerService.makeMetadataAgent(); mStatus = invokeAgentForBackup( PACKAGE_MANAGER_SENTINEL, IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport); @@ -391,11 +389,9 @@ public class PerformBackupTask implements BackupRestoreTask { // to sanity-check here. This also gives us the classname of the // package's backup agent. try { - mCurrentPackage = backupManagerService.getPackageManager().getPackageInfo( - request.packageName, - PackageManager.GET_SIGNATURES); - if (!AppBackupUtils.appIsEligibleForBackup( - mCurrentPackage.applicationInfo)) { + PackageManager pm = backupManagerService.getPackageManager(); + mCurrentPackage = pm.getPackageInfo(request.packageName, PackageManager.GET_SIGNATURES); + if (!AppBackupUtils.appIsEligibleForBackup(mCurrentPackage.applicationInfo, pm)) { // The manifest has changed but we had a stale backup request pending. // This won't happen again because the app won't be requesting further // backups. @@ -609,6 +605,7 @@ public class PerformBackupTask implements BackupRestoreTask { break; } } + mFinished = true; Slog.i(TAG, "K/V backup pass finished."); // Only once we're entirely finished do we release the wakelock for k/v backup. backupManagerService.getWakelock().release(); diff --git a/com/android/server/backup/internal/RunInitializeReceiver.java b/com/android/server/backup/internal/RunInitializeReceiver.java index a6897d0e..1df0bf0c 100644 --- a/com/android/server/backup/internal/RunInitializeReceiver.java +++ b/com/android/server/backup/internal/RunInitializeReceiver.java @@ -23,6 +23,7 @@ import static com.android.server.backup.RefactoredBackupManagerService.TAG; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.util.ArraySet; import android.util.Slog; import com.android.server.backup.RefactoredBackupManagerService; @@ -38,19 +39,22 @@ public class RunInitializeReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) { synchronized (backupManagerService.getQueueLock()) { + final ArraySet<String> pendingInits = backupManagerService.getPendingInits(); if (DEBUG) { - Slog.v(TAG, "Running a device init"); + Slog.v(TAG, "Running a device init; " + pendingInits.size() + " pending"); } - String[] pendingInits = (String[]) backupManagerService.getPendingInits().toArray(); - backupManagerService.clearPendingInits(); - PerformInitializeTask initTask = new PerformInitializeTask(backupManagerService, - pendingInits, null); + if (pendingInits.size() > 0) { + final String[] transports = pendingInits.toArray(new String[pendingInits.size()]); + PerformInitializeTask initTask = new PerformInitializeTask(backupManagerService, + transports, null); - // Acquire the wakelock and pass it to the init thread. it will - // be released once init concludes. - backupManagerService.getWakelock().acquire(); - backupManagerService.getBackupHandler().post(initTask); + // Acquire the wakelock and pass it to the init thread. it will + // be released once init concludes. + backupManagerService.clearPendingInits(); + backupManagerService.getWakelock().acquire(); + backupManagerService.getBackupHandler().post(initTask); + } } } } diff --git a/com/android/server/backup/restore/PerformAdbRestoreTask.java b/com/android/server/backup/restore/PerformAdbRestoreTask.java index 62ae065b..22691bb6 100644 --- a/com/android/server/backup/restore/PerformAdbRestoreTask.java +++ b/com/android/server/backup/restore/PerformAdbRestoreTask.java @@ -150,8 +150,7 @@ public class PerformAdbRestoreTask implements Runnable { mObserver = observer; mLatchObject = latch; mAgent = null; - mPackageManagerBackupAgent = new PackageManagerBackupAgent( - backupManagerService.getPackageManager()); + mPackageManagerBackupAgent = backupManagerService.makeMetadataAgent(); mAgentPackage = null; mTargetApp = null; mObbConnection = new FullBackupObbConnection(backupManagerService); diff --git a/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/com/android/server/backup/restore/PerformUnifiedRestoreTask.java index 21d5dc21..b538c6d4 100644 --- a/com/android/server/backup/restore/PerformUnifiedRestoreTask.java +++ b/com/android/server/backup/restore/PerformUnifiedRestoreTask.java @@ -198,8 +198,8 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { boolean hasSettings = false; for (int i = 0; i < filterSet.length; i++) { try { - PackageInfo info = backupManagerService.getPackageManager().getPackageInfo( - filterSet[i], 0); + PackageManager pm = backupManagerService.getPackageManager(); + PackageInfo info = pm.getPackageInfo(filterSet[i], 0); if ("android".equals(info.packageName)) { hasSystem = true; continue; @@ -209,8 +209,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { continue; } - if (AppBackupUtils.appIsEligibleForBackup( - info.applicationInfo)) { + if (AppBackupUtils.appIsEligibleForBackup(info.applicationInfo, pm)) { mAcceptSet.add(info); } } catch (NameNotFoundException e) { @@ -387,8 +386,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // Pull the Package Manager metadata from the restore set first mCurrentPackage = new PackageInfo(); mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL; - mPmAgent = new PackageManagerBackupAgent(backupManagerService.getPackageManager(), - null); + mPmAgent = backupManagerService.makeMetadataAgent(null); mAgent = IBackupAgent.Stub.asInterface(mPmAgent.onBind()); if (MORE_DEBUG) { Slog.v(TAG, "initiating restore for PMBA"); @@ -779,6 +777,9 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // state RESTORE_FINISHED : provide the "no more data" signpost callback at the end private void restoreFinished() { + if (DEBUG) { + Slog.d(TAG, "restoreFinished packageName=" + mCurrentPackage.packageName); + } try { backupManagerService .prepareOperationTimeout(mEphemeralOpToken, diff --git a/com/android/server/backup/utils/AppBackupUtils.java b/com/android/server/backup/utils/AppBackupUtils.java index 4abf18ad..d7cac777 100644 --- a/com/android/server/backup/utils/AppBackupUtils.java +++ b/com/android/server/backup/utils/AppBackupUtils.java @@ -22,6 +22,7 @@ import static com.android.server.backup.RefactoredBackupManagerService.TAG; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.content.pm.Signature; import android.os.Process; import android.util.Slog; @@ -44,7 +45,7 @@ public class AppBackupUtils { * <li>it is the special shared-storage backup package used for 'adb backup' * </ol> */ - public static boolean appIsEligibleForBackup(ApplicationInfo app) { + public static boolean appIsEligibleForBackup(ApplicationInfo app, PackageManager pm) { // 1. their manifest states android:allowBackup="false" if ((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) { return false; @@ -60,11 +61,33 @@ public class AppBackupUtils { return false; } - return true; + // 4. it is an "instant" app + if (app.isInstantApp()) { + return false; + } + + // Everything else checks out; the only remaining roadblock would be if the + // package were disabled + return !appIsDisabled(app, pm); + } + + /** Avoid backups of 'disabled' apps. */ + public static boolean appIsDisabled(ApplicationInfo app, PackageManager pm) { + switch (pm.getApplicationEnabledSetting(app.packageName)) { + case PackageManager.COMPONENT_ENABLED_STATE_DISABLED: + case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER: + case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED: + return true; + + default: + return false; + } } /** - * Checks if the app is in a stopped state, that means it won't receive broadcasts. + * Checks if the app is in a stopped state. This is not part of the general "eligible for + * backup?" check because we *do* still need to restore data to apps in this state (e.g. + * newly-installing ones) */ public static boolean appIsStopped(ApplicationInfo app) { return ((app.flags & ApplicationInfo.FLAG_STOPPED) != 0); diff --git a/com/android/server/connectivity/IpConnectivityEventBuilder.java b/com/android/server/connectivity/IpConnectivityEventBuilder.java index 22330e66..67e72167 100644 --- a/com/android/server/connectivity/IpConnectivityEventBuilder.java +++ b/com/android/server/connectivity/IpConnectivityEventBuilder.java @@ -126,7 +126,7 @@ final public class IpConnectivityEventBuilder { wakeupStats.systemWakeups = in.systemWakeups; wakeupStats.nonApplicationWakeups = in.nonApplicationWakeups; wakeupStats.applicationWakeups = in.applicationWakeups; - wakeupStats.unroutedWakeups = in.unroutedWakeups; + wakeupStats.noUidWakeups = in.noUidWakeups; final IpConnectivityEvent out = buildEvent(0, 0, in.iface); out.setWakeupStats(wakeupStats); return out; diff --git a/com/android/server/connectivity/IpConnectivityMetrics.java b/com/android/server/connectivity/IpConnectivityMetrics.java index 475d786a..f2445fa3 100644 --- a/com/android/server/connectivity/IpConnectivityMetrics.java +++ b/com/android/server/connectivity/IpConnectivityMetrics.java @@ -34,6 +34,7 @@ import android.util.Base64; import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.RingBuffer; import com.android.internal.util.TokenBucket; import com.android.server.SystemService; import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent; @@ -44,7 +45,11 @@ import java.util.ArrayList; import java.util.List; import java.util.function.ToIntFunction; -/** {@hide} */ +/** + * Event buffering service for core networking and connectivity metrics. + * + * {@hide} + */ final public class IpConnectivityMetrics extends SystemService { private static final String TAG = IpConnectivityMetrics.class.getSimpleName(); private static final boolean DBG = false; @@ -58,7 +63,10 @@ final public class IpConnectivityMetrics extends SystemService { private static final String SERVICE_NAME = IpConnectivityLog.SERVICE_NAME; - // Default size of the event buffer. Once the buffer is full, incoming events are dropped. + // Default size of the event rolling log for bug report dumps. + private static final int DEFAULT_LOG_SIZE = 500; + // Default size of the event buffer for metrics reporting. + // Once the buffer is full, incoming events are dropped. private static final int DEFAULT_BUFFER_SIZE = 2000; // Maximum size of the event buffer. private static final int MAXIMUM_BUFFER_SIZE = DEFAULT_BUFFER_SIZE * 10; @@ -67,24 +75,38 @@ final public class IpConnectivityMetrics extends SystemService { private static final int ERROR_RATE_LIMITED = -1; - // Lock ensuring that concurrent manipulations of the event buffer are correct. + // Lock ensuring that concurrent manipulations of the event buffers are correct. // There are three concurrent operations to synchronize: // - appending events to the buffer. // - iterating throught the buffer. // - flushing the buffer content and replacing it by a new buffer. private final Object mLock = new Object(); + // Implementation instance of IIpConnectivityMetrics.aidl. @VisibleForTesting public final Impl impl = new Impl(); + // Subservice listening to Netd events via INetdEventListener.aidl. @VisibleForTesting NetdEventListenerService mNetdListener; + // Rolling log of the most recent events. This log is used for dumping + // connectivity events in bug reports. + @GuardedBy("mLock") + private final RingBuffer<ConnectivityMetricsEvent> mEventLog = + new RingBuffer(ConnectivityMetricsEvent.class, DEFAULT_LOG_SIZE); + // Buffer of connectivity events used for metrics reporting. This buffer + // does not rotate automatically and instead saturates when it becomes full. + // It is flushed at metrics reporting. @GuardedBy("mLock") private ArrayList<ConnectivityMetricsEvent> mBuffer; + // Total number of events dropped from mBuffer since last metrics reporting. @GuardedBy("mLock") private int mDropped; + // Capacity of mBuffer @GuardedBy("mLock") private int mCapacity; + // A list of rate limiting counters keyed by connectivity event types for + // metrics reporting mBuffer. @GuardedBy("mLock") private final ArrayMap<Class<?>, TokenBucket> mBuckets = makeRateLimitingBuckets(); @@ -132,6 +154,7 @@ final public class IpConnectivityMetrics extends SystemService { private int append(ConnectivityMetricsEvent event) { if (DBG) Log.d(TAG, "logEvent: " + event); synchronized (mLock) { + mEventLog.append(event); final int left = mCapacity - mBuffer.size(); if (event == null) { return left; @@ -216,6 +239,23 @@ final public class IpConnectivityMetrics extends SystemService { } } + /** + * Prints for bug reports the content of the rolling event log and the + * content of Netd event listener. + */ + private void cmdDumpsys(FileDescriptor fd, PrintWriter pw, String[] args) { + final ConnectivityMetricsEvent[] events; + synchronized (mLock) { + events = mEventLog.toArray(); + } + for (ConnectivityMetricsEvent ev : events) { + pw.println(ev.toString()); + } + if (mNetdListener != null) { + mNetdListener.list(pw); + } + } + private void cmdStats(FileDescriptor fd, PrintWriter pw, String[] args) { synchronized (mLock) { pw.println("Buffered events: " + mBuffer.size()); @@ -258,7 +298,8 @@ final public class IpConnectivityMetrics extends SystemService { cmdFlush(fd, pw, args); return; case CMD_DUMPSYS: - // Fallthrough to CMD_LIST when dumpsys.cpp dumps services states (bug reports) + cmdDumpsys(fd, pw, args); + return; case CMD_LIST: cmdList(fd, pw, args); return; diff --git a/com/android/server/connectivity/Nat464Xlat.java b/com/android/server/connectivity/Nat464Xlat.java index e6585ad1..fceacba4 100644 --- a/com/android/server/connectivity/Nat464Xlat.java +++ b/com/android/server/connectivity/Nat464Xlat.java @@ -20,6 +20,7 @@ import android.net.InterfaceConfiguration; import android.net.ConnectivityManager; import android.net.LinkAddress; import android.net.LinkProperties; +import android.net.NetworkInfo; import android.net.RouteInfo; import android.os.INetworkManagementService; import android.os.RemoteException; @@ -44,12 +45,18 @@ public class Nat464Xlat extends BaseNetworkObserver { // This must match the interface prefix in clatd.c. private static final String CLAT_PREFIX = "v4-"; - // The network types we will start clatd on, + // The network types on which we will start clatd, // allowing clat only on networks for which we can support IPv6-only. private static final int[] NETWORK_TYPES = { - ConnectivityManager.TYPE_MOBILE, - ConnectivityManager.TYPE_WIFI, - ConnectivityManager.TYPE_ETHERNET, + ConnectivityManager.TYPE_MOBILE, + ConnectivityManager.TYPE_WIFI, + ConnectivityManager.TYPE_ETHERNET, + }; + + // The network states in which running clatd is supported. + private static final NetworkInfo.State[] NETWORK_STATES = { + NetworkInfo.State.CONNECTED, + NetworkInfo.State.SUSPENDED, }; private final INetworkManagementService mNMService; @@ -81,11 +88,8 @@ public class Nat464Xlat extends BaseNetworkObserver { */ public static boolean requiresClat(NetworkAgentInfo nai) { // TODO: migrate to NetworkCapabilities.TRANSPORT_*. - final int netType = nai.networkInfo.getType(); final boolean supported = ArrayUtils.contains(NETWORK_TYPES, nai.networkInfo.getType()); - // TODO: this should also consider if the network is in SUSPENDED state to avoid stopping - // clatd in SUSPENDED state. - final boolean connected = nai.networkInfo.isConnected(); + final boolean connected = ArrayUtils.contains(NETWORK_STATES, nai.networkInfo.getState()); // We only run clat on networks that don't have a native IPv4 address. final boolean hasIPv4Address = (nai.linkProperties != null) && nai.linkProperties.hasIPv4Address(); @@ -148,7 +152,6 @@ public class Nat464Xlat extends BaseNetworkObserver { * turn ND offload off if on WiFi. */ private void enterRunningState() { - maybeSetIpv6NdOffload(mBaseIface, false); mState = State.RUNNING; } @@ -156,10 +159,6 @@ public class Nat464Xlat extends BaseNetworkObserver { * Stop clatd, and turn ND offload on if it had been turned off. */ private void enterStoppingState() { - if (isRunning()) { - maybeSetIpv6NdOffload(mBaseIface, true); - } - try { mNMService.stopClatd(mBaseIface); } catch(RemoteException|IllegalStateException e) { @@ -275,19 +274,6 @@ public class Nat464Xlat extends BaseNetworkObserver { } } - private void maybeSetIpv6NdOffload(String iface, boolean on) { - // TODO: migrate to NetworkCapabilities.TRANSPORT_*. - if (mNetwork.networkInfo.getType() != ConnectivityManager.TYPE_WIFI) { - return; - } - try { - Slog.d(TAG, (on ? "En" : "Dis") + "abling ND offload on " + iface); - mNMService.setInterfaceIpv6NdOffload(iface, on); - } catch(RemoteException|IllegalStateException e) { - Slog.w(TAG, "Changing IPv6 ND offload on " + iface + "failed: " + e); - } - } - /** * Adds stacked link on base link and transitions to RUNNING state. */ diff --git a/com/android/server/connectivity/NetdEventListenerService.java b/com/android/server/connectivity/NetdEventListenerService.java index 6f7ace2f..6206dfcd 100644 --- a/com/android/server/connectivity/NetdEventListenerService.java +++ b/com/android/server/connectivity/NetdEventListenerService.java @@ -38,6 +38,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.BitUtils; import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.util.RingBuffer; import com.android.internal.util.TokenBucket; import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent; import java.io.PrintWriter; @@ -82,9 +83,8 @@ public class NetdEventListenerService extends INetdEventListener.Stub { private final ArrayMap<String, WakeupStats> mWakeupStats = new ArrayMap<>(); // Ring buffer array for storing packet wake up events sent by Netd. @GuardedBy("this") - private final WakeupEvent[] mWakeupEvents = new WakeupEvent[WAKEUP_EVENT_BUFFER_LENGTH]; - @GuardedBy("this") - private long mWakeupEventCursor = 0; + private final RingBuffer<WakeupEvent> mWakeupEvents = + new RingBuffer(WakeupEvent.class, WAKEUP_EVENT_BUFFER_LENGTH); private final ConnectivityManager mCm; @@ -170,18 +170,16 @@ public class NetdEventListenerService extends INetdEventListener.Stub { timestampMs = System.currentTimeMillis(); } - addWakupEvent(iface, timestampMs, uid); + addWakeupEvent(iface, timestampMs, uid); } @GuardedBy("this") - private void addWakupEvent(String iface, long timestampMs, int uid) { - int index = wakeupEventIndex(mWakeupEventCursor); - mWakeupEventCursor++; + private void addWakeupEvent(String iface, long timestampMs, int uid) { WakeupEvent event = new WakeupEvent(); event.iface = iface; event.timestampMs = timestampMs; event.uid = uid; - mWakeupEvents[index] = event; + mWakeupEvents.append(event); WakeupStats stats = mWakeupStats.get(iface); if (stats == null) { stats = new WakeupStats(iface); @@ -190,23 +188,6 @@ public class NetdEventListenerService extends INetdEventListener.Stub { stats.countEvent(event); } - @GuardedBy("this") - private WakeupEvent[] getWakeupEvents() { - int length = (int) Math.min(mWakeupEventCursor, (long) mWakeupEvents.length); - WakeupEvent[] out = new WakeupEvent[length]; - // Reverse iteration from youngest event to oldest event. - long inCursor = mWakeupEventCursor - 1; - int outIdx = out.length - 1; - while (outIdx >= 0) { - out[outIdx--] = mWakeupEvents[wakeupEventIndex(inCursor--)]; - } - return out; - } - - private static int wakeupEventIndex(long cursor) { - return (int) Math.abs(cursor % WAKEUP_EVENT_BUFFER_LENGTH); - } - public synchronized void flushStatistics(List<IpConnectivityEvent> events) { flushProtos(events, mConnectEvents, IpConnectivityEventBuilder::toProto); flushProtos(events, mDnsEvents, IpConnectivityEventBuilder::toProto); @@ -230,7 +211,7 @@ public class NetdEventListenerService extends INetdEventListener.Stub { for (int i = 0; i < mWakeupStats.size(); i++) { pw.println(mWakeupStats.valueAt(i)); } - for (WakeupEvent wakeup : getWakeupEvents()) { + for (WakeupEvent wakeup : mWakeupEvents.toArray()) { pw.println(wakeup); } } diff --git a/com/android/server/connectivity/tethering/OffloadController.java b/com/android/server/connectivity/tethering/OffloadController.java index 5eafe5f9..cff216c7 100644 --- a/com/android/server/connectivity/tethering/OffloadController.java +++ b/com/android/server/connectivity/tethering/OffloadController.java @@ -52,6 +52,7 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -73,6 +74,8 @@ public class OffloadController { private static final String ANYIP = "0.0.0.0"; private static final ForwardedStats EMPTY_STATS = new ForwardedStats(); + private static enum UpdateType { IF_NEEDED, FORCE }; + private final Handler mHandler; private final OffloadHardwareInterface mHwInterface; private final ContentResolver mContentResolver; @@ -185,8 +188,8 @@ public class OffloadController { updateStatsForAllUpstreams(); forceTetherStatsPoll(); // [2] (Re)Push all state. - // TODO: computeAndPushLocalPrefixes() - // TODO: push all downstream state. + computeAndPushLocalPrefixes(UpdateType.FORCE); + pushAllDownstreamState(); pushUpstreamParameters(null); } @@ -319,7 +322,7 @@ public class OffloadController { } private boolean maybeUpdateDataLimit(String iface) { - // setDataLimit may only be called while offload is occuring on this upstream. + // setDataLimit may only be called while offload is occurring on this upstream. if (!started() || !TextUtils.equals(iface, currentUpstreamInterface())) { return true; } @@ -368,15 +371,15 @@ public class OffloadController { // upstream parameters fails (probably just wait for a subsequent // onOffloadEvent() callback to tell us offload is available again and // then reapply all state). - computeAndPushLocalPrefixes(); + computeAndPushLocalPrefixes(UpdateType.IF_NEEDED); pushUpstreamParameters(prevUpstream); } public void setLocalPrefixes(Set<IpPrefix> localPrefixes) { - if (!started()) return; - mExemptPrefixes = localPrefixes; - computeAndPushLocalPrefixes(); + + if (!started()) return; + computeAndPushLocalPrefixes(UpdateType.IF_NEEDED); } public void notifyDownstreamLinkProperties(LinkProperties lp) { @@ -385,27 +388,38 @@ public class OffloadController { if (Objects.equals(oldLp, lp)) return; if (!started()) return; + pushDownstreamState(oldLp, lp); + } - final List<RouteInfo> oldRoutes = (oldLp != null) ? oldLp.getRoutes() : new ArrayList<>(); - final List<RouteInfo> newRoutes = lp.getRoutes(); + private void pushDownstreamState(LinkProperties oldLp, LinkProperties newLp) { + final String ifname = newLp.getInterfaceName(); + final List<RouteInfo> oldRoutes = + (oldLp != null) ? oldLp.getRoutes() : Collections.EMPTY_LIST; + final List<RouteInfo> newRoutes = newLp.getRoutes(); // For each old route, if not in new routes: remove. - for (RouteInfo oldRoute : oldRoutes) { - if (shouldIgnoreDownstreamRoute(oldRoute)) continue; - if (!newRoutes.contains(oldRoute)) { - mHwInterface.removeDownstreamPrefix(ifname, oldRoute.getDestination().toString()); + for (RouteInfo ri : oldRoutes) { + if (shouldIgnoreDownstreamRoute(ri)) continue; + if (!newRoutes.contains(ri)) { + mHwInterface.removeDownstreamPrefix(ifname, ri.getDestination().toString()); } } // For each new route, if not in old routes: add. - for (RouteInfo newRoute : newRoutes) { - if (shouldIgnoreDownstreamRoute(newRoute)) continue; - if (!oldRoutes.contains(newRoute)) { - mHwInterface.addDownstreamPrefix(ifname, newRoute.getDestination().toString()); + for (RouteInfo ri : newRoutes) { + if (shouldIgnoreDownstreamRoute(ri)) continue; + if (!oldRoutes.contains(ri)) { + mHwInterface.addDownstreamPrefix(ifname, ri.getDestination().toString()); } } } + private void pushAllDownstreamState() { + for (LinkProperties lp : mDownstreams.values()) { + pushDownstreamState(null, lp); + } + } + public void removeDownstreamInterface(String ifname) { final LinkProperties lp = mDownstreams.remove(ifname); if (lp == null) return; @@ -484,10 +498,11 @@ public class OffloadController { return success; } - private boolean computeAndPushLocalPrefixes() { + private boolean computeAndPushLocalPrefixes(UpdateType how) { + final boolean force = (how == UpdateType.FORCE); final Set<String> localPrefixStrs = computeLocalPrefixStrings( mExemptPrefixes, mUpstreamLinkProperties); - if (mLastLocalPrefixStrs.equals(localPrefixStrs)) return true; + if (!force && mLastLocalPrefixStrs.equals(localPrefixStrs)) return true; mLastLocalPrefixStrs = localPrefixStrs; return mHwInterface.setLocalPrefixes(new ArrayList<>(localPrefixStrs)); @@ -581,9 +596,10 @@ public class OffloadController { } mNatUpdateCallbacksReceived++; + final String natDescription = String.format("%s (%s, %s) -> (%s, %s)", + protoName, srcAddr, srcPort, dstAddr, dstPort); if (DBG) { - mLog.log(String.format("NAT timeout update: %s (%s, %s) -> (%s, %s)", - protoName, srcAddr, srcPort, dstAddr, dstPort)); + mLog.log("NAT timeout update: " + natDescription); } final int timeoutSec = connectionTimeoutUpdateSecondsFor(proto); @@ -594,7 +610,7 @@ public class OffloadController { NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_NETFILTER, msg); } catch (ErrnoException e) { mNatUpdateNetlinkErrors++; - mLog.e("Error updating NAT conntrack entry: " + e + mLog.e("Error updating NAT conntrack entry >" + natDescription + "<: " + e + ", msg: " + NetlinkConstants.hexify(msg)); mLog.log("NAT timeout update callbacks received: " + mNatUpdateCallbacksReceived); mLog.log("NAT timeout update netlink errors: " + mNatUpdateNetlinkErrors); diff --git a/com/android/server/content/SyncManager.java b/com/android/server/content/SyncManager.java index 2f3b5596..9cd52d77 100644 --- a/com/android/server/content/SyncManager.java +++ b/com/android/server/content/SyncManager.java @@ -46,8 +46,8 @@ import android.content.SyncStatusInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ProviderInfo; import android.content.pm.RegisteredServicesCache; import android.content.pm.RegisteredServicesCacheListener; @@ -143,6 +143,7 @@ public class SyncManager { private static final boolean DEBUG_ACCOUNT_ACCESS = false; + // Only do the check on a debuggable build. private static final boolean ENABLE_SUSPICIOUS_CHECK = Build.IS_DEBUGGABLE; /** Delay a sync due to local changes this long. In milliseconds */ @@ -537,9 +538,11 @@ public class SyncManager { * @return whether the device most likely has some periodic syncs. */ private boolean likelyHasPeriodicSyncs() { - // STOPSHIP Remove the google specific string. try { - return AccountManager.get(mContext).getAccountsByType("com.google").length > 0; + // Each sync adapter has a daily periodic sync by default, but sync adapters can remove + // them by themselves. So here, we use an arbitrary threshold. If there are more than + // this many sync endpoints, surely one of them should have a periodic sync... + return mSyncStorageEngine.getAuthorityCount() >= 6; } catch (Throwable th) { // Just in case. } @@ -3775,48 +3778,10 @@ public class SyncManager { } if (op.isPeriodic) { mLogger.log("Removing periodic sync ", op, " for ", why); - - if (ENABLE_SUSPICIOUS_CHECK && isSuspiciousPeriodicSyncRemoval(op)) { - wtfWithLog("Suspicious removal of " + op + " for " + why); - } } getJobScheduler().cancel(op.jobId); } - private boolean isSuspiciousPeriodicSyncRemoval(SyncOperation op) { - // STOPSHIP Remove the google specific string. - if (!op.isPeriodic){ - return false; - } - boolean found = false; - for (UserInfo user : UserManager.get(mContext).getUsers(/*excludeDying=*/ true)) { - if (op.target.userId == user.id) { - found = true; - break; - } - } - if (!found) { - return false; // User is being removed, okay. - } - switch (op.target.provider) { - case "gmail-ls": - case "com.android.contacts.metadata": - break; - default: - return false; - } - final Account account = op.target.account; - final Account[] accounts = AccountManager.get(mContext) - .getAccountsByTypeAsUser(account.type, UserHandle.of(op.target.userId)); - for (Account a : accounts) { - if (a.equals(account)) { - return true; // Account still exists. Suspicious! - } - } - // Account no longer exists. Makes sense... - return false; - } - private void wtfWithLog(String message) { Slog.wtf(TAG, message); mLogger.log("WTF: ", message); diff --git a/com/android/server/content/SyncStorageEngine.java b/com/android/server/content/SyncStorageEngine.java index 7b277c06..3591871f 100644 --- a/com/android/server/content/SyncStorageEngine.java +++ b/com/android/server/content/SyncStorageEngine.java @@ -911,6 +911,12 @@ public class SyncStorageEngine extends Handler { } } + public int getAuthorityCount() { + synchronized (mAuthorities) { + return mAuthorities.size(); + } + } + public AuthorityInfo getAuthority(int authorityId) { synchronized (mAuthorities) { return mAuthorities.get(authorityId); diff --git a/com/android/server/devicepolicy/DevicePolicyManagerService.java b/com/android/server/devicepolicy/DevicePolicyManagerService.java index 6c859f76..c59f44e7 100644 --- a/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -97,8 +97,8 @@ import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ParceledListSlice; import android.content.pm.PermissionInfo; import android.content.pm.ResolveInfo; @@ -5350,7 +5350,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - private void forceWipeUser(int userId) { + private void forceWipeUser(int userId, String wipeReasonForUser) { try { IActivityManager am = mInjector.getIActivityManager(); if (am.getCurrentUser().id == userId) { @@ -5361,7 +5361,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!userRemoved) { Slog.w(LOG_TAG, "Couldn't remove user " + userId); } else if (isManagedProfile(userId)) { - sendWipeProfileNotification(); + sendWipeProfileNotification(wipeReasonForUser); } } catch (RemoteException re) { // Shouldn't happen @@ -5369,23 +5369,26 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override - public void wipeData(int flags) { + public void wipeDataWithReason(int flags, String wipeReasonForUser) { if (!mHasFeature) { return; } + Preconditions.checkStringNotEmpty(wipeReasonForUser, "wipeReasonForUser is null or empty"); enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId()); final ActiveAdmin admin; synchronized (this) { admin = getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_WIPE_DATA); } - String reason = "DevicePolicyManager.wipeData() from " + String internalReason = "DevicePolicyManager.wipeDataWithReason() from " + admin.info.getComponent().flattenToShortString(); wipeDataNoLock( - admin.info.getComponent(), flags, reason, admin.getUserHandle().getIdentifier()); + admin.info.getComponent(), flags, internalReason, wipeReasonForUser, + admin.getUserHandle().getIdentifier()); } - private void wipeDataNoLock(ComponentName admin, int flags, String reason, int userId) { + private void wipeDataNoLock(ComponentName admin, int flags, String internalReason, + String wipeReasonForUser, int userId) { wtfIfInLock(); long ident = mInjector.binderClearCallingIdentity(); @@ -5420,25 +5423,26 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // (rather than system), we should probably trigger factory reset. Current code just // removes that user (but still clears FRP...) if (userId == UserHandle.USER_SYSTEM) { - forceWipeDeviceNoLock(/*wipeExtRequested=*/ (flags & WIPE_EXTERNAL_STORAGE) != 0, - reason, /*wipeEuicc=*/ (flags & WIPE_EUICC) != 0); + forceWipeDeviceNoLock(/*wipeExtRequested=*/ ( + flags & WIPE_EXTERNAL_STORAGE) != 0, + internalReason, + /*wipeEuicc=*/ (flags & WIPE_EUICC) != 0); } else { - forceWipeUser(userId); + forceWipeUser(userId, wipeReasonForUser); } } finally { mInjector.binderRestoreCallingIdentity(ident); } } - private void sendWipeProfileNotification() { - String contentText = mContext.getString(R.string.work_profile_deleted_description_dpm_wipe); + private void sendWipeProfileNotification(String wipeReasonForUser) { Notification notification = new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN) .setSmallIcon(android.R.drawable.stat_sys_warning) .setContentTitle(mContext.getString(R.string.work_profile_deleted)) - .setContentText(contentText) + .setContentText(wipeReasonForUser) .setColor(mContext.getColor(R.color.system_notification_accent_color)) - .setStyle(new Notification.BigTextStyle().bigText(contentText)) + .setStyle(new Notification.BigTextStyle().bigText(wipeReasonForUser)) .build(); mInjector.getNotificationManager().notify(SystemMessage.NOTE_PROFILE_WIPED, notification); } @@ -5610,9 +5614,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // able to do so). // IMPORTANT: Call without holding the lock to prevent deadlock. try { + String wipeReasonForUser = mContext.getString( + R.string.work_profile_deleted_reason_maximum_password_failure); wipeDataNoLock(strictestAdmin.info.getComponent(), /*flags=*/ 0, /*reason=*/ "reportFailedPasswordAttempt()", + wipeReasonForUser, userId); } catch (SecurityException e) { Slog.w(LOG_TAG, "Failed to wipe user " + userId @@ -5621,7 +5628,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } if (mInjector.securityLogIsLoggingEnabled()) { - SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0, + SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, + /*result*/ 0, /*method strength*/ 1); } } diff --git a/com/android/server/display/DisplayDeviceInfo.java b/com/android/server/display/DisplayDeviceInfo.java index ef6de4c1..fddb81ba 100644 --- a/com/android/server/display/DisplayDeviceInfo.java +++ b/com/android/server/display/DisplayDeviceInfo.java @@ -98,6 +98,12 @@ final class DisplayDeviceInfo { public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 1 << 9; /** + * Flag: This display will destroy its content on removal. + * @hide + */ + public static final int FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 10; + + /** * Touch attachment: Display does not receive touch. */ public static final int TOUCH_NONE = 0; diff --git a/com/android/server/display/LocalDisplayAdapter.java b/com/android/server/display/LocalDisplayAdapter.java index 87564846..d61a418c 100644 --- a/com/android/server/display/LocalDisplayAdapter.java +++ b/com/android/server/display/LocalDisplayAdapter.java @@ -473,17 +473,18 @@ final class LocalDisplayAdapter extends DisplayAdapter { } // If the state change was from or to VR, then we need to tell the light - // so that it can apply appropriate VR brightness settings. This should - // happen prior to changing the brightness but also if there is no - // brightness change at all. + // so that it can apply appropriate VR brightness settings. Also, update the + // brightness so the state is propogated to light. + boolean vrModeChange = false; if ((state == Display.STATE_VR || currentState == Display.STATE_VR) && currentState != state) { setVrMode(state == Display.STATE_VR); + vrModeChange = true; } // Apply brightness changes given that we are in a non-suspended state. - if (brightnessChanged) { + if (brightnessChanged || vrModeChange) { setDisplayBrightness(brightness); } diff --git a/com/android/server/display/LogicalDisplay.java b/com/android/server/display/LogicalDisplay.java index addad0b4..78a54079 100644 --- a/com/android/server/display/LogicalDisplay.java +++ b/com/android/server/display/LogicalDisplay.java @@ -238,6 +238,9 @@ final class LogicalDisplay { // For private displays by default content is destroyed on removal. mBaseDisplayInfo.removeMode = Display.REMOVE_MODE_DESTROY_CONTENT; } + if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL) != 0) { + mBaseDisplayInfo.removeMode = Display.REMOVE_MODE_DESTROY_CONTENT; + } if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_PRESENTATION) != 0) { mBaseDisplayInfo.flags |= Display.FLAG_PRESENTATION; } diff --git a/com/android/server/display/NightDisplayService.java b/com/android/server/display/NightDisplayService.java index aafc6317..9cf13672 100644 --- a/com/android/server/display/NightDisplayService.java +++ b/com/android/server/display/NightDisplayService.java @@ -48,8 +48,10 @@ import com.android.server.twilight.TwilightListener; import com.android.server.twilight.TwilightManager; import com.android.server.twilight.TwilightState; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.Calendar; import java.util.TimeZone; import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY; @@ -306,7 +308,7 @@ public final class NightDisplayService extends SystemService } @Override - public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) { + public void onCustomStartTimeChanged(LocalTime startTime) { Slog.d(TAG, "onCustomStartTimeChanged: startTime=" + startTime); if (mAutoMode != null) { @@ -315,7 +317,7 @@ public final class NightDisplayService extends SystemService } @Override - public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) { + public void onCustomEndTimeChanged(LocalTime endTime) { Slog.d(TAG, "onCustomEndTimeChanged: endTime=" + endTime); if (mAutoMode != null) { @@ -414,6 +416,36 @@ public final class NightDisplayService extends SystemService outTemp[10] = blue; } + /** + * Returns the first date time corresponding to the local time that occurs before the + * provided date time. + * + * @param compareTime the LocalDateTime to compare against + * @return the prior LocalDateTime corresponding to this local time + */ + public static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) { + final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(), + compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute()); + + // Check if the local time has passed, if so return the same time yesterday. + return ldt.isAfter(compareTime) ? ldt.minusDays(1) : ldt; + } + + /** + * Returns the first date time corresponding to this local time that occurs after the + * provided date time. + * + * @param compareTime the LocalDateTime to compare against + * @return the next LocalDateTime corresponding to this local time + */ + public static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) { + final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(), + compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute()); + + // Check if the local time has passed, if so return the same time tomorrow. + return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt; + } + private abstract class AutoMode implements NightDisplayController.Callback { public abstract void onStart(); @@ -425,10 +457,10 @@ public final class NightDisplayService extends SystemService private final AlarmManager mAlarmManager; private final BroadcastReceiver mTimeChangedReceiver; - private NightDisplayController.LocalTime mStartTime; - private NightDisplayController.LocalTime mEndTime; + private LocalTime mStartTime; + private LocalTime mEndTime; - private Calendar mLastActivatedTime; + private LocalDateTime mLastActivatedTime; CustomAutoMode() { mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE); @@ -441,31 +473,15 @@ public final class NightDisplayService extends SystemService } private void updateActivated() { - final Calendar now = Calendar.getInstance(); - final Calendar startTime = mStartTime.getDateTimeBefore(now); - final Calendar endTime = mEndTime.getDateTimeAfter(startTime); + final LocalDateTime now = LocalDateTime.now(); + final LocalDateTime start = getDateTimeBefore(mStartTime, now); + final LocalDateTime end = getDateTimeAfter(mEndTime, start); + boolean activate = now.isBefore(end); - boolean activate = now.before(endTime); if (mLastActivatedTime != null) { - // Convert mLastActivatedTime to the current timezone if needed. - final TimeZone currentTimeZone = now.getTimeZone(); - if (!currentTimeZone.equals(mLastActivatedTime.getTimeZone())) { - final int year = mLastActivatedTime.get(Calendar.YEAR); - final int dayOfYear = mLastActivatedTime.get(Calendar.DAY_OF_YEAR); - final int hourOfDay = mLastActivatedTime.get(Calendar.HOUR_OF_DAY); - final int minute = mLastActivatedTime.get(Calendar.MINUTE); - - mLastActivatedTime.setTimeZone(currentTimeZone); - mLastActivatedTime.set(Calendar.YEAR, year); - mLastActivatedTime.set(Calendar.DAY_OF_YEAR, dayOfYear); - mLastActivatedTime.set(Calendar.HOUR_OF_DAY, hourOfDay); - mLastActivatedTime.set(Calendar.MINUTE, minute); - } - // Maintain the existing activated state if within the current period. - if (mLastActivatedTime.before(now) - && mLastActivatedTime.after(startTime) - && (mLastActivatedTime.after(endTime) || now.before(endTime))) { + if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start) + && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) { activate = mController.isActivated(); } } @@ -473,14 +489,16 @@ public final class NightDisplayService extends SystemService if (mIsActivated == null || mIsActivated != activate) { mController.setActivated(activate); } + updateNextAlarm(mIsActivated, now); } - private void updateNextAlarm(@Nullable Boolean activated, @NonNull Calendar now) { + private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) { if (activated != null) { - final Calendar next = activated ? mEndTime.getDateTimeAfter(now) - : mStartTime.getDateTimeAfter(now); - mAlarmManager.setExact(AlarmManager.RTC, next.getTimeInMillis(), TAG, this, null); + final LocalDateTime next = activated ? getDateTimeAfter(mEndTime, now) + : getDateTimeAfter(mStartTime, now); + final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, this, null); } } @@ -510,18 +528,18 @@ public final class NightDisplayService extends SystemService @Override public void onActivated(boolean activated) { mLastActivatedTime = mController.getLastActivatedTime(); - updateNextAlarm(activated, Calendar.getInstance()); + updateNextAlarm(activated, LocalDateTime.now()); } @Override - public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) { + public void onCustomStartTimeChanged(LocalTime startTime) { mStartTime = startTime; mLastActivatedTime = null; updateActivated(); } @Override - public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) { + public void onCustomEndTimeChanged(LocalTime endTime) { mEndTime = endTime; mLastActivatedTime = null; updateActivated(); @@ -550,15 +568,14 @@ public final class NightDisplayService extends SystemService } boolean activate = state.isNight(); - final Calendar lastActivatedTime = mController.getLastActivatedTime(); + final LocalDateTime lastActivatedTime = mController.getLastActivatedTime(); if (lastActivatedTime != null) { - final Calendar now = Calendar.getInstance(); - final Calendar sunrise = state.sunrise(); - final Calendar sunset = state.sunset(); - + final LocalDateTime now = LocalDateTime.now(); + final LocalDateTime sunrise = state.sunrise(); + final LocalDateTime sunset = state.sunset(); // Maintain the existing activated state if within the current period. - if (lastActivatedTime.before(now) - && (lastActivatedTime.after(sunrise) ^ lastActivatedTime.after(sunset))) { + if (lastActivatedTime.isBefore(now) && (lastActivatedTime.isBefore(sunrise) + ^ lastActivatedTime.isBefore(sunset))) { activate = mController.isActivated(); } } diff --git a/com/android/server/display/VirtualDisplayAdapter.java b/com/android/server/display/VirtualDisplayAdapter.java index d6ab8881..f86d5763 100644 --- a/com/android/server/display/VirtualDisplayAdapter.java +++ b/com/android/server/display/VirtualDisplayAdapter.java @@ -24,6 +24,8 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLI import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT; +import static android.hardware.display.DisplayManager + .VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL; import android.content.Context; import android.hardware.display.IVirtualDisplayCallback; @@ -363,6 +365,9 @@ public class VirtualDisplayAdapter extends DisplayAdapter { if ((mFlags & VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT) != 0) { mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; } + if ((mFlags & VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL) != 0) { + mInfo.flags |= DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL; + } mInfo.type = Display.TYPE_VIRTUAL; mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ? diff --git a/com/android/server/fingerprint/FingerprintService.java b/com/android/server/fingerprint/FingerprintService.java index b1c165ef..1df9c861 100644 --- a/com/android/server/fingerprint/FingerprintService.java +++ b/com/android/server/fingerprint/FingerprintService.java @@ -63,6 +63,8 @@ import android.service.fingerprint.FingerprintActionStatsProto; import android.service.fingerprint.FingerprintServiceDumpProto; import android.service.fingerprint.FingerprintUserStatsProto; import android.util.Slog; +import android.util.SparseBooleanArray; +import android.util.SparseIntArray; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; @@ -81,7 +83,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; @@ -89,18 +90,19 @@ import java.util.concurrent.CopyOnWriteArrayList; /** * A service to manage multiple clients that want to access the fingerprint HAL API. * The service is responsible for maintaining a list of clients and dispatching all - * fingerprint -related events. + * fingerprint-related events. * * @hide */ public class FingerprintService extends SystemService implements IHwBinder.DeathRecipient { static final String TAG = "FingerprintService"; static final boolean DEBUG = true; - private static final boolean CLEANUP_UNUSED_FP = false; + private static final boolean CLEANUP_UNUSED_FP = true; private static final String FP_DATA_DIR = "fpdata"; private static final int MSG_USER_SWITCHING = 10; private static final String ACTION_LOCKOUT_RESET = "com.android.server.fingerprint.ACTION_LOCKOUT_RESET"; + private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user"; private class PerformanceStats { int accept; // number of accepted fingerprints @@ -128,8 +130,8 @@ public class FingerprintService extends SystemService implements IHwBinder.Death private final FingerprintUtils mFingerprintUtils = FingerprintUtils.getInstance(); private Context mContext; private long mHalDeviceId; - private boolean mTimedLockoutCleared; - private int mFailedAttempts; + private SparseBooleanArray mTimedLockoutCleared; + private SparseIntArray mFailedAttempts; @GuardedBy("this") private IBiometricsFingerprint mDaemon; private final PowerManager mPowerManager; @@ -139,10 +141,8 @@ public class FingerprintService extends SystemService implements IHwBinder.Death private ClientMonitor mPendingClient; private PerformanceStats mPerformanceStats; - private IBinder mToken = new Binder(); // used for internal FingerprintService enumeration - private LinkedList<Integer> mEnumeratingUserIds = new LinkedList<>(); - private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw finterprints + private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw fingerprints private class UserFingerprint { Fingerprint f; @@ -177,15 +177,17 @@ public class FingerprintService extends SystemService implements IHwBinder.Death @Override public void onReceive(Context context, Intent intent) { if (ACTION_LOCKOUT_RESET.equals(intent.getAction())) { - resetFailedAttempts(false /* clearAttemptCounter */); + final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0); + resetFailedAttemptsForUser(false /* clearAttemptCounter */, user); } } }; - private final Runnable mResetFailedAttemptsRunnable = new Runnable() { + private final Runnable mResetFailedAttemptsForCurrentUserRunnable = new Runnable() { @Override public void run() { - resetFailedAttempts(true /* clearAttemptCounter */); + resetFailedAttemptsForUser(true /* clearAttemptCounter */, + ActivityManager.getCurrentUser()); } }; @@ -221,6 +223,8 @@ public class FingerprintService extends SystemService implements IHwBinder.Death mContext.registerReceiver(mLockoutReceiver, new IntentFilter(ACTION_LOCKOUT_RESET), RESET_FINGERPRINT_LOCKOUT, null /* handler */); mUserManager = UserManager.get(mContext); + mTimedLockoutCleared = new SparseBooleanArray(); + mFailedAttempts = new SparseIntArray(); } @Override @@ -233,7 +237,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death public synchronized IBiometricsFingerprint getFingerprintDaemon() { if (mDaemon == null) { - Slog.v(TAG, "mDeamon was null, reconnect to fingerprint"); + Slog.v(TAG, "mDaemon was null, reconnect to fingerprint"); try { mDaemon = IBiometricsFingerprint.getService(); } catch (java.util.NoSuchElementException e) { @@ -259,7 +263,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death if (mHalDeviceId != 0) { loadAuthenticatorIds(); updateActiveGroup(ActivityManager.getCurrentUser(), null); - doFingerprintCleanup(ActivityManager.getCurrentUser()); + doFingerprintCleanupForUser(ActivityManager.getCurrentUser()); } else { Slog.w(TAG, "Failed to open Fingerprint HAL!"); MetricsLogger.count(mContext, "fingerprintd_openhal_error", 1); @@ -288,52 +292,41 @@ public class FingerprintService extends SystemService implements IHwBinder.Death } } - private void doFingerprintCleanup(int userId) { + /** + * This method should be called upon connection to the daemon, and when user switches. + * @param userId + */ + private void doFingerprintCleanupForUser(int userId) { if (CLEANUP_UNUSED_FP) { - resetEnumerateState(); - mEnumeratingUserIds.push(userId); - enumerateNextUser(); + enumerateUser(userId); } } - private void resetEnumerateState() { - if (DEBUG) Slog.v(TAG, "Enumerate cleaning up"); - mEnumeratingUserIds.clear(); + private void clearEnumerateState() { + if (DEBUG) Slog.v(TAG, "clearEnumerateState()"); mUnknownFingerprints.clear(); } - private void enumerateNextUser() { - int nextUser = mEnumeratingUserIds.getFirst(); - updateActiveGroup(nextUser, null); + private void enumerateUser(int userId) { + if (DEBUG) Slog.v(TAG, "Enumerating user(" + userId + ")"); boolean restricted = !hasPermission(MANAGE_FINGERPRINT); - - if (DEBUG) Slog.v(TAG, "Enumerating user id " + nextUser + " of " - + mEnumeratingUserIds.size() + " remaining users"); - - startEnumerate(mToken, nextUser, null, restricted, true /* internal */); + startEnumerate(mToken, userId, null, restricted, true /* internal */); } // Remove unknown fingerprints from hardware private void cleanupUnknownFingerprints() { if (!mUnknownFingerprints.isEmpty()) { - Slog.w(TAG, "unknown fingerprint size: " + mUnknownFingerprints.size()); UserFingerprint uf = mUnknownFingerprints.get(0); mUnknownFingerprints.remove(uf); boolean restricted = !hasPermission(MANAGE_FINGERPRINT); - updateActiveGroup(uf.userId, null); startRemove(mToken, uf.f.getFingerId(), uf.f.getGroupId(), uf.userId, null, restricted, true /* internal */); } else { - resetEnumerateState(); + clearEnumerateState(); } } protected void handleEnumerate(long deviceId, int fingerId, int groupId, int remaining) { - if (DEBUG) Slog.w(TAG, "Enumerate: fid=" + fingerId - + ", gid=" + groupId - + ", dev=" + deviceId - + ", rem=" + remaining); - ClientMonitor client = mCurrentClient; if ( !(client instanceof InternalRemovalClient) && !(client instanceof EnumerateClient) ) { @@ -343,24 +336,21 @@ public class FingerprintService extends SystemService implements IHwBinder.Death // All fingerprints in hardware for this user were enumerated if (remaining == 0) { - mEnumeratingUserIds.poll(); - if (client instanceof InternalEnumerateClient) { - List<Fingerprint> enrolled = ((InternalEnumerateClient) client).getEnumeratedList(); - Slog.w(TAG, "Added " + enrolled.size() + " enumerated fingerprints for deletion"); - for (Fingerprint f : enrolled) { + List<Fingerprint> unknownFingerprints = + ((InternalEnumerateClient) client).getUnknownFingerprints(); + + if (!unknownFingerprints.isEmpty()) { + Slog.w(TAG, "Adding " + unknownFingerprints.size() + + " fingerprints for deletion"); + } + for (Fingerprint f : unknownFingerprints) { mUnknownFingerprints.add(new UserFingerprint(f, client.getTargetUserId())); } - } - - removeClient(client); - - if (!mEnumeratingUserIds.isEmpty()) { - enumerateNextUser(); - } else if (client instanceof InternalEnumerateClient) { - if (DEBUG) Slog.v(TAG, "Finished enumerating all users"); - // This will start a chain of InternalRemovalClients + removeClient(client); cleanupUnknownFingerprints(); + } else { + removeClient(client); } } } @@ -368,7 +358,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death protected void handleError(long deviceId, int error, int vendorCode) { ClientMonitor client = mCurrentClient; if (client instanceof InternalRemovalClient || client instanceof InternalEnumerateClient) { - resetEnumerateState(); + clearEnumerateState(); } if (client != null && client.onError(error, vendorCode)) { removeClient(client); @@ -412,7 +402,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death if (client instanceof InternalRemovalClient && !mUnknownFingerprints.isEmpty()) { cleanupUnknownFingerprints(); } else if (client instanceof InternalRemovalClient){ - resetEnumerateState(); + clearEnumerateState(); } } @@ -466,8 +456,14 @@ public class FingerprintService extends SystemService implements IHwBinder.Death } void handleUserSwitching(int userId) { + if (mCurrentClient instanceof InternalRemovalClient + || mCurrentClient instanceof InternalEnumerateClient) { + Slog.w(TAG, "User switched while performing cleanup"); + removeClient(mCurrentClient); + clearEnumerateState(); + } updateActiveGroup(userId, null); - doFingerprintCleanup(userId); + doFingerprintCleanupForUser(userId); } private void removeClient(ClientMonitor client) { @@ -488,27 +484,32 @@ public class FingerprintService extends SystemService implements IHwBinder.Death } private int getLockoutMode() { - if (mFailedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) { + final int currentUser = ActivityManager.getCurrentUser(); + final int failedAttempts = mFailedAttempts.get(currentUser, 0); + if (failedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) { return AuthenticationClient.LOCKOUT_PERMANENT; - } else if (mFailedAttempts > 0 && mTimedLockoutCleared == false && - (mFailedAttempts % MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED == 0)) { + } else if (failedAttempts > 0 && + mTimedLockoutCleared.get(currentUser, false) == false + && (failedAttempts % MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED == 0)) { return AuthenticationClient.LOCKOUT_TIMED; } return AuthenticationClient.LOCKOUT_NONE; } - private void scheduleLockoutReset() { - mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS, getLockoutResetIntent()); + private void scheduleLockoutResetForUser(int userId) { + mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS, + getLockoutResetIntentForUser(userId)); } - private void cancelLockoutReset() { - mAlarmManager.cancel(getLockoutResetIntent()); + private void cancelLockoutResetForUser(int userId) { + mAlarmManager.cancel(getLockoutResetIntentForUser(userId)); } - private PendingIntent getLockoutResetIntent() { - return PendingIntent.getBroadcast(mContext, 0, - new Intent(ACTION_LOCKOUT_RESET), PendingIntent.FLAG_UPDATE_CURRENT); + private PendingIntent getLockoutResetIntentForUser(int userId) { + return PendingIntent.getBroadcast(mContext, userId, + new Intent(ACTION_LOCKOUT_RESET).putExtra(KEY_LOCKOUT_RESET_USER, userId), + PendingIntent.FLAG_UPDATE_CURRENT); } public long startPreEnroll(IBinder token) { @@ -555,6 +556,12 @@ public class FingerprintService extends SystemService implements IHwBinder.Death // This condition means we're currently running internal diagnostics to // remove extra fingerprints in the hardware and/or the software // TODO: design an escape hatch in case client never finishes + if (newClient != null) { + Slog.w(TAG, "Internal cleanup in progress but trying to start client " + + newClient.getClass().getSuperclass().getSimpleName() + + "(" + newClient.getOwnerString() + ")" + + ", initiatedByClient = " + initiatedByClient); + } } else { currentClient.stop(initiatedByClient); @@ -567,7 +574,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death if (DEBUG) Slog.v(TAG, "starting client " + newClient.getClass().getSuperclass().getSimpleName() + "(" + newClient.getOwnerString() + ")" - + ", initiatedByClient = " + initiatedByClient + ")"); + + ", initiatedByClient = " + initiatedByClient); notifyClientActiveCallbacks(true); newClient.start(); @@ -813,8 +820,9 @@ public class FingerprintService extends SystemService implements IHwBinder.Death receiver, mCurrentUserId, groupId, opId, restricted, opPackageName) { @Override public int handleFailedAttempt() { - mFailedAttempts++; - mTimedLockoutCleared = false; + final int currentUser = ActivityManager.getCurrentUser(); + mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1); + mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false); final int lockoutMode = getLockoutMode(); if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) { mPerformanceStats.permanentLockout++; @@ -824,7 +832,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death // Failing multiple times will continue to push out the lockout time if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) { - scheduleLockoutReset(); + scheduleLockoutResetForUser(currentUser); return lockoutMode; } return AuthenticationClient.LOCKOUT_NONE; @@ -832,7 +840,8 @@ public class FingerprintService extends SystemService implements IHwBinder.Death @Override public void resetFailedAttempts() { - FingerprintService.this.resetFailedAttempts(true /* clearAttemptCounter */); + FingerprintService.this.resetFailedAttemptsForUser(true /* clearAttemptCounter */, + ActivityManager.getCurrentUser()); } @Override @@ -886,17 +895,17 @@ public class FingerprintService extends SystemService implements IHwBinder.Death // attempt counter should only be cleared when Keyguard goes away or when // a fingerprint is successfully authenticated - protected void resetFailedAttempts(boolean clearAttemptCounter) { + protected void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) { if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) { Slog.v(TAG, "Reset fingerprint lockout, clearAttemptCounter=" + clearAttemptCounter); } if (clearAttemptCounter) { - mFailedAttempts = 0; + mFailedAttempts.put(userId, 0); } - mTimedLockoutCleared = true; + mTimedLockoutCleared.put(userId, true); // If we're asked to reset failed attempts externally (i.e. from Keyguard), // the alarm might still be pending; remove it. - cancelLockoutReset(); + cancelLockoutResetForUser(userId); notifyLockoutResetMonitors(); } @@ -1277,7 +1286,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death public void resetTimeout(byte [] token) { checkPermission(RESET_FINGERPRINT_LOCKOUT); // TODO: confirm security token when we move timeout management into the HAL layer. - mHandler.post(mResetFailedAttemptsRunnable); + mHandler.post(mResetFailedAttemptsForCurrentUserRunnable); } @Override @@ -1338,6 +1347,8 @@ public class FingerprintService extends SystemService implements IHwBinder.Death set.put("rejectCrypto", (cryptoStats != null) ? cryptoStats.reject : 0); set.put("acquireCrypto", (cryptoStats != null) ? cryptoStats.acquire : 0); set.put("lockoutCrypto", (cryptoStats != null) ? cryptoStats.lockout : 0); + set.put("permanentLockoutCrypto", + (cryptoStats != null) ? cryptoStats.permanentLockout : 0); sets.put(set); } @@ -1367,7 +1378,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death proto.write(FingerprintActionStatsProto.REJECT, normal.reject); proto.write(FingerprintActionStatsProto.ACQUIRE, normal.acquire); proto.write(FingerprintActionStatsProto.LOCKOUT, normal.lockout); - proto.write(FingerprintActionStatsProto.LOCKOUT_PERMANENT, normal.lockout); + proto.write(FingerprintActionStatsProto.LOCKOUT_PERMANENT, normal.permanentLockout); proto.end(countsToken); } @@ -1380,7 +1391,7 @@ public class FingerprintService extends SystemService implements IHwBinder.Death proto.write(FingerprintActionStatsProto.REJECT, crypto.reject); proto.write(FingerprintActionStatsProto.ACQUIRE, crypto.acquire); proto.write(FingerprintActionStatsProto.LOCKOUT, crypto.lockout); - proto.write(FingerprintActionStatsProto.LOCKOUT_PERMANENT, crypto.lockout); + proto.write(FingerprintActionStatsProto.LOCKOUT_PERMANENT, crypto.permanentLockout); proto.end(countsToken); } diff --git a/com/android/server/fingerprint/InternalEnumerateClient.java b/com/android/server/fingerprint/InternalEnumerateClient.java index 88d9ef43..434db98a 100644 --- a/com/android/server/fingerprint/InternalEnumerateClient.java +++ b/com/android/server/fingerprint/InternalEnumerateClient.java @@ -30,7 +30,7 @@ import java.util.List; public abstract class InternalEnumerateClient extends EnumerateClient { private List<Fingerprint> mEnrolledList; - private List<Fingerprint> mEnumeratedList = new ArrayList<>(); // list of fp to delete + private List<Fingerprint> mUnknownFingerprints = new ArrayList<>(); // list of fp to delete public InternalEnumerateClient(Context context, long halDeviceId, IBinder token, IFingerprintServiceReceiver receiver, int groupId, int userId, @@ -47,7 +47,6 @@ public abstract class InternalEnumerateClient extends EnumerateClient { if (mEnrolledList.get(i).getFingerId() == fingerId) { mEnrolledList.remove(i); matched = true; - Slog.e(TAG, "Matched fingerprint fid=" + fingerId); break; } } @@ -55,7 +54,7 @@ public abstract class InternalEnumerateClient extends EnumerateClient { // fingerId 0 means no fingerprints are in hardware if (!matched && fingerId != 0) { Fingerprint fingerprint = new Fingerprint("", groupId, fingerId, getHalDeviceId()); - mEnumeratedList.add(fingerprint); + mUnknownFingerprints.add(fingerprint); } } @@ -76,8 +75,8 @@ public abstract class InternalEnumerateClient extends EnumerateClient { mEnrolledList.clear(); } - public List<Fingerprint> getEnumeratedList() { - return mEnumeratedList; + public List<Fingerprint> getUnknownFingerprints() { + return mUnknownFingerprints; } @Override diff --git a/com/android/server/job/JobSchedulerService.java b/com/android/server/job/JobSchedulerService.java index ac807941..78aa2f94 100644 --- a/com/android/server/job/JobSchedulerService.java +++ b/com/android/server/job/JobSchedulerService.java @@ -795,18 +795,22 @@ public final class JobSchedulerService extends com.android.server.SystemService * @param uid Uid to check against for removal of a job. * */ - public void cancelJobsForUid(int uid, String reason) { + public boolean cancelJobsForUid(int uid, String reason) { if (uid == Process.SYSTEM_UID) { Slog.wtfStack(TAG, "Can't cancel all jobs for system uid"); - return; + return false; } + + boolean jobsCanceled = false; synchronized (mLock) { final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid); for (int i=0; i<jobsForUid.size(); i++) { JobStatus toRemove = jobsForUid.get(i); cancelJobImplLocked(toRemove, null, reason); + jobsCanceled = true; } } + return jobsCanceled; } /** @@ -816,13 +820,14 @@ public final class JobSchedulerService extends com.android.server.SystemService * @param uid Uid of the calling client. * @param jobId Id of the job, provided at schedule-time. */ - public void cancelJob(int uid, int jobId) { + public boolean cancelJob(int uid, int jobId) { JobStatus toCancel; synchronized (mLock) { toCancel = mJobs.getJobByUidAndJobId(uid, jobId); if (toCancel != null) { cancelJobImplLocked(toCancel, null, "cancel() called by app"); } + return (toCancel != null); } } @@ -2147,6 +2152,39 @@ public final class JobSchedulerService extends com.android.server.SystemService return 0; } + // Shell command infrastructure: cancel a scheduled job + int executeCancelCommand(PrintWriter pw, String pkgName, int userId, + boolean hasJobId, int jobId) { + if (DEBUG) { + Slog.v(TAG, "executeCancelCommand(): " + pkgName + "/" + userId + " " + jobId); + } + + int pkgUid = -1; + try { + IPackageManager pm = AppGlobals.getPackageManager(); + pkgUid = pm.getPackageUid(pkgName, 0, userId); + } catch (RemoteException e) { /* can't happen */ } + + if (pkgUid < 0) { + pw.println("Package " + pkgName + " not found."); + return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE; + } + + if (!hasJobId) { + pw.println("Canceling all jobs for " + pkgName + " in user " + userId); + if (!cancelJobsForUid(pkgUid, "cancel shell command for package")) { + pw.println("No matching jobs found."); + } + } else { + pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId); + if (!cancelJob(pkgUid, jobId)) { + pw.println("No matching job found."); + } + } + + return 0; + } + void setMonitorBattery(boolean enabled) { synchronized (mLock) { if (mBatteryController != null) { diff --git a/com/android/server/job/JobSchedulerShellCommand.java b/com/android/server/job/JobSchedulerShellCommand.java index a53c0885..d630aab6 100644 --- a/com/android/server/job/JobSchedulerShellCommand.java +++ b/com/android/server/job/JobSchedulerShellCommand.java @@ -48,6 +48,8 @@ public final class JobSchedulerShellCommand extends ShellCommand { return runJob(pw); case "timeout": return timeout(pw); + case "cancel": + return cancelJob(pw); case "monitor-battery": return monitorBattery(pw); case "get-battery-seq": @@ -205,6 +207,42 @@ public final class JobSchedulerShellCommand extends ShellCommand { } } + private int cancelJob(PrintWriter pw) throws Exception { + checkPermission("cancel jobs"); + + int userId = UserHandle.USER_SYSTEM; + + String opt; + while ((opt = getNextOption()) != null) { + switch (opt) { + case "-u": + case "--user": + userId = UserHandle.parseUserArg(getNextArgRequired()); + break; + + default: + pw.println("Error: unknown option '" + opt + "'"); + return -1; + } + } + + if (userId < 0) { + pw.println("Error: must specify a concrete user ID"); + return -1; + } + + final String pkgName = getNextArg(); + final String jobIdStr = getNextArg(); + final int jobId = jobIdStr != null ? Integer.parseInt(jobIdStr) : -1; + + final long ident = Binder.clearCallingIdentity(); + try { + return mInternal.executeCancelCommand(pw, pkgName, userId, jobIdStr != null, jobId); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + private int monitorBattery(PrintWriter pw) throws Exception { checkPermission("change battery monitoring"); String opt = getNextArgRequired(); @@ -315,6 +353,12 @@ public final class JobSchedulerShellCommand extends ShellCommand { pw.println(" Options:"); pw.println(" -u or --user: specify which user's job is to be run; the default is"); pw.println(" all users"); + pw.println(" cancel [-u | --user USER_ID] PACKAGE [JOB_ID]"); + pw.println(" Cancel a scheduled job. If a job ID is not supplied, all jobs scheduled"); + pw.println(" by that package will be canceled. USE WITH CAUTION."); + pw.println(" Options:"); + pw.println(" -u or --user: specify which user's job is to be run; the default is"); + pw.println(" the primary or system user"); pw.println(" monitor-battery [on|off]"); pw.println(" Control monitoring of all battery changes. Off by default. Turning"); pw.println(" on makes get-battery-seq useful."); diff --git a/com/android/server/locksettings/LockSettingsService.java b/com/android/server/locksettings/LockSettingsService.java index 11043bd1..a1a01061 100644 --- a/com/android/server/locksettings/LockSettingsService.java +++ b/com/android/server/locksettings/LockSettingsService.java @@ -376,7 +376,7 @@ public class LockSettingsService extends ILockSettings.Stub { } public SyntheticPasswordManager getSyntheticPasswordManager(LockSettingsStorage storage) { - return new SyntheticPasswordManager(storage, getUserManager()); + return new SyntheticPasswordManager(getContext(), storage, getUserManager()); } public int binderGetCallingUid() { @@ -763,7 +763,8 @@ public class LockSettingsService extends ILockSettings.Stub { private void migrateOldDataAfterSystemReady() { try { // Migrate the FRP credential to the persistent data block - if (LockPatternUtils.frpCredentialEnabled() && !getBoolean("migrated_frp", false, 0)) { + if (LockPatternUtils.frpCredentialEnabled(mContext) + && !getBoolean("migrated_frp", false, 0)) { migrateFrpCredential(); setBoolean("migrated_frp", true, 0); Slog.i(TAG, "Migrated migrated_frp."); @@ -784,7 +785,7 @@ public class LockSettingsService extends ILockSettings.Stub { return; } for (UserInfo userInfo : mUserManager.getUsers()) { - if (userOwnsFrpCredential(userInfo) && isUserSecure(userInfo.id)) { + if (userOwnsFrpCredential(mContext, userInfo) && isUserSecure(userInfo.id)) { synchronized (mSpManager) { if (isSyntheticPasswordBasedCredentialLocked(userInfo.id)) { int actualQuality = (int) getLong(LockPatternUtils.PASSWORD_TYPE_KEY, @@ -2366,6 +2367,13 @@ public class LockSettingsService extends ILockSettings.Stub { Slog.w(TAG, "Invalid escrow token supplied"); return false; } + if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) { + // Most likely, an untrusted credential reset happened in the past which + // changed the synthetic password + Slog.e(TAG, "Obsolete token: synthetic password derived but it fails GK " + + "verification."); + return false; + } // Update PASSWORD_TYPE_KEY since it's needed by notifyActivePasswordMetricsAvailable() // called by setLockCredentialWithAuthTokenLocked(). // TODO: refactor usage of PASSWORD_TYPE_KEY b/65239740 @@ -2497,7 +2505,7 @@ public class LockSettingsService extends ILockSettings.Stub { } public void onSystemReady() { - if (frpCredentialEnabled()) { + if (frpCredentialEnabled(mContext)) { updateRegistration(); } else { // If we don't intend to use frpCredentials and we're not provisioned yet, send @@ -2526,7 +2534,7 @@ public class LockSettingsService extends ILockSettings.Stub { private void clearFrpCredentialIfOwnerNotSecure() { List<UserInfo> users = mUserManager.getUsers(); for (UserInfo user : users) { - if (userOwnsFrpCredential(user)) { + if (userOwnsFrpCredential(mContext, user)) { if (!isUserSecure(user.id)) { mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, user.id, 0, null); diff --git a/com/android/server/locksettings/LockSettingsStrongAuth.java b/com/android/server/locksettings/LockSettingsStrongAuth.java index 542b929d..c9c93293 100644 --- a/com/android/server/locksettings/LockSettingsStrongAuth.java +++ b/com/android/server/locksettings/LockSettingsStrongAuth.java @@ -27,6 +27,7 @@ import android.app.AlarmManager.OnAlarmListener; import android.app.admin.DevicePolicyManager; import android.app.trust.IStrongAuthTracker; import android.content.Context; +import android.content.pm.PackageManager; import android.hardware.fingerprint.FingerprintManager; import android.os.Binder; import android.os.DeadObjectException; @@ -74,7 +75,10 @@ public class LockSettingsStrongAuth { } public void systemReady() { - mFingerprintManager = mContext.getSystemService(FingerprintManager.class); + final PackageManager pm = mContext.getPackageManager(); + if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) { + mFingerprintManager = mContext.getSystemService(FingerprintManager.class); + } } private void handleAddStrongAuthTracker(IStrongAuthTracker tracker) { diff --git a/com/android/server/locksettings/SyntheticPasswordManager.java b/com/android/server/locksettings/SyntheticPasswordManager.java index 33a9a995..9440f171 100644 --- a/com/android/server/locksettings/SyntheticPasswordManager.java +++ b/com/android/server/locksettings/SyntheticPasswordManager.java @@ -19,6 +19,7 @@ package com.android.server.locksettings; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.admin.DevicePolicyManager; +import android.content.Context; import android.content.pm.UserInfo; import android.hardware.weaver.V1_0.IWeaver; import android.hardware.weaver.V1_0.WeaverConfig; @@ -255,13 +256,16 @@ public class SyntheticPasswordManager { byte[] aggregatedSecret; } + private final Context mContext; private LockSettingsStorage mStorage; private IWeaver mWeaver; private WeaverConfig mWeaverConfig; private final UserManager mUserManager; - public SyntheticPasswordManager(LockSettingsStorage storage, UserManager userManager) { + public SyntheticPasswordManager(Context context, LockSettingsStorage storage, + UserManager userManager) { + mContext = context; mStorage = storage; mUserManager = userManager; } @@ -645,7 +649,7 @@ public class SyntheticPasswordManager { public void migrateFrpPasswordLocked(long handle, UserInfo userInfo, int requestedQuality) { if (mStorage.getPersistentDataBlock() != null - && LockPatternUtils.userOwnsFrpCredential(userInfo)) { + && LockPatternUtils.userOwnsFrpCredential(mContext, userInfo)) { PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userInfo.id)); if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) { @@ -662,7 +666,8 @@ public class SyntheticPasswordManager { private void synchronizeFrpPassword(PasswordData pwd, int requestedQuality, int userId) { if (mStorage.getPersistentDataBlock() != null - && LockPatternUtils.userOwnsFrpCredential(mUserManager.getUserInfo(userId))) { + && LockPatternUtils.userOwnsFrpCredential(mContext, + mUserManager.getUserInfo(userId))) { if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) { mStorage.writePersistentDataBlock(PersistentData.TYPE_SP, userId, requestedQuality, pwd.toBytes()); @@ -675,7 +680,8 @@ public class SyntheticPasswordManager { private void synchronizeWeaverFrpPassword(PasswordData pwd, int requestedQuality, int userId, int weaverSlot) { if (mStorage.getPersistentDataBlock() != null - && LockPatternUtils.userOwnsFrpCredential(mUserManager.getUserInfo(userId))) { + && LockPatternUtils.userOwnsFrpCredential(mContext, + mUserManager.getUserInfo(userId))) { if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) { mStorage.writePersistentDataBlock(PersistentData.TYPE_SP_WEAVER, weaverSlot, requestedQuality, pwd.toBytes()); diff --git a/com/android/server/media/MediaRouterService.java b/com/android/server/media/MediaRouterService.java index 3795b7f3..1cfd5f02 100644 --- a/com/android/server/media/MediaRouterService.java +++ b/com/android/server/media/MediaRouterService.java @@ -271,14 +271,6 @@ public final class MediaRouterService extends IMediaRouterService.Stub // Binder call @Override - public boolean isGlobalBluetoothA2doOn() { - synchronized (mLock) { - return mGlobalBluetoothA2dpOn; - } - } - - // Binder call - @Override public void setDiscoveryRequest(IMediaRouterClient client, int routeTypes, boolean activeScan) { if (client == null) { @@ -383,7 +375,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub synchronized (mLock) { a2dpOn = mGlobalBluetoothA2dpOn; } - Slog.v(TAG, "restoreBluetoothA2dp( " + a2dpOn + ")"); + Slog.v(TAG, "restoreBluetoothA2dp(" + a2dpOn + ")"); mAudioService.setBluetoothA2dpOn(a2dpOn); } catch (RemoteException e) { Slog.w(TAG, "RemoteException while calling setBluetoothA2dpOn."); diff --git a/com/android/server/media/MediaSessionRecord.java b/com/android/server/media/MediaSessionRecord.java index 89e10503..0b11479a 100644 --- a/com/android/server/media/MediaSessionRecord.java +++ b/com/android/server/media/MediaSessionRecord.java @@ -462,18 +462,25 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { mHandler.post(new Runnable() { @Override public void run() { - if (useSuggested) { - if (AudioSystem.isStreamActive(stream, 0)) { - mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream, direction, - flags, packageName, uid); + try { + if (useSuggested) { + if (AudioSystem.isStreamActive(stream, 0)) { + mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream, + direction, flags, packageName, uid); + } else { + mAudioManagerInternal.adjustSuggestedStreamVolumeForUid( + AudioManager.USE_DEFAULT_STREAM_TYPE, direction, + flags | previousFlagPlaySound, packageName, uid); + } } else { - mAudioManagerInternal.adjustSuggestedStreamVolumeForUid( - AudioManager.USE_DEFAULT_STREAM_TYPE, direction, - flags | previousFlagPlaySound, packageName, uid); + mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags, + packageName, uid); } - } else { - mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags, - packageName, uid); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Cannot adjust volume: direction=" + direction + ", stream=" + + stream + ", flags=" + flags + ", packageName=" + packageName + + ", uid=" + uid + ", useSuggested=" + useSuggested + + ", previousFlagPlaySound=" + previousFlagPlaySound, e); } } }); diff --git a/com/android/server/media/MediaSessionService.java b/com/android/server/media/MediaSessionService.java index b77ed913..b9a2d184 100644 --- a/com/android/server/media/MediaSessionService.java +++ b/com/android/server/media/MediaSessionService.java @@ -1363,6 +1363,10 @@ public class MediaSessionService extends SystemService implements Monitor { flags, packageName, TAG); } catch (RemoteException e) { Log.e(TAG, "Error adjusting default volume.", e); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Cannot adjust volume: direction=" + direction + + ", suggestedStream=" + suggestedStream + ", flags=" + flags, + e); } } }); diff --git a/com/android/server/net/NetworkPolicyManagerService.java b/com/android/server/net/NetworkPolicyManagerService.java index 90dab2c3..b4056b33 100644 --- a/com/android/server/net/NetworkPolicyManagerService.java +++ b/com/android/server/net/NetworkPolicyManagerService.java @@ -331,7 +331,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final int MSG_UPDATE_INTERFACE_QUOTA = 10; private static final int MSG_REMOVE_INTERFACE_QUOTA = 11; private static final int MSG_POLICIES_CHANGED = 13; - private static final int MSG_SET_FIREWALL_RULES = 14; private static final int MSG_RESET_FIREWALL_RULES_BY_UID = 15; private static final int UID_MSG_STATE_CHANGED = 100; @@ -3138,9 +3137,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { uidRules.put(mUidState.keyAt(i), FIREWALL_RULE_ALLOW); } } - setUidFirewallRulesAsync(chain, uidRules, CHAIN_TOGGLE_ENABLE); + setUidFirewallRulesUL(chain, uidRules, CHAIN_TOGGLE_ENABLE); } else { - setUidFirewallRulesAsync(chain, null, CHAIN_TOGGLE_DISABLE); + setUidFirewallRulesUL(chain, null, CHAIN_TOGGLE_DISABLE); } } @@ -3207,7 +3206,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - setUidFirewallRulesAsync(FIREWALL_CHAIN_STANDBY, uidRules, CHAIN_TOGGLE_NONE); + setUidFirewallRulesUL(FIREWALL_CHAIN_STANDBY, uidRules, CHAIN_TOGGLE_NONE); } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); } @@ -3906,18 +3905,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { removeInterfaceQuota((String) msg.obj); return true; } - case MSG_SET_FIREWALL_RULES: { - final int chain = msg.arg1; - final int toggle = msg.arg2; - final SparseIntArray uidRules = (SparseIntArray) msg.obj; - if (uidRules != null) { - setUidFirewallRules(chain, uidRules); - } - if (toggle != CHAIN_TOGGLE_NONE) { - enableFirewallChainUL(chain, toggle == CHAIN_TOGGLE_ENABLE); - } - return true; - } case MSG_RESET_FIREWALL_RULES_BY_UID: { resetUidFirewallRules(msg.arg1); return true; @@ -4063,15 +4050,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** * Calls {@link #setUidFirewallRules(int, SparseIntArray)} and - * {@link #enableFirewallChainUL(int, boolean)} asynchronously. + * {@link #enableFirewallChainUL(int, boolean)} synchronously. * * @param chain firewall chain. * @param uidRules new UID rules; if {@code null}, only toggles chain state. * @param toggle whether the chain should be enabled, disabled, or not changed. */ - private void setUidFirewallRulesAsync(int chain, @Nullable SparseIntArray uidRules, + private void setUidFirewallRulesUL(int chain, @Nullable SparseIntArray uidRules, @ChainToggleType int toggle) { - mHandler.obtainMessage(MSG_SET_FIREWALL_RULES, chain, toggle, uidRules).sendToTarget(); + if (uidRules != null) { + setUidFirewallRulesUL(chain, uidRules); + } + if (toggle != CHAIN_TOGGLE_NONE) { + enableFirewallChainUL(chain, toggle == CHAIN_TOGGLE_ENABLE); + } } /** @@ -4079,7 +4071,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * here to netd. It will clean up dead rules and make sure the target chain only contains rules * specified here. */ - private void setUidFirewallRules(int chain, SparseIntArray uidRules) { + private void setUidFirewallRulesUL(int chain, SparseIntArray uidRules) { try { int size = uidRules.size(); int[] uids = new int[size]; diff --git a/com/android/server/notification/ConditionProviders.java b/com/android/server/notification/ConditionProviders.java index 3444ef3e..c0fbfbb2 100644 --- a/com/android/server/notification/ConditionProviders.java +++ b/com/android/server/notification/ConditionProviders.java @@ -186,6 +186,11 @@ public class ConditionProviders extends ManagedServices { super.onPackagesChanged(removingPackage, pkgList, uid); } + @Override + protected boolean isValidEntry(String packageOrComponent, int userId) { + return true; + } + public ManagedServiceInfo checkServiceToken(IConditionProvider provider) { synchronized(mMutex) { return checkServiceTokenLocked(provider); diff --git a/com/android/server/notification/ImportanceExtractor.java b/com/android/server/notification/ImportanceExtractor.java index 46ec92b1..452121c0 100644 --- a/com/android/server/notification/ImportanceExtractor.java +++ b/com/android/server/notification/ImportanceExtractor.java @@ -22,7 +22,7 @@ import android.util.Slog; * Determines the importance of the given notification. */ public class ImportanceExtractor implements NotificationSignalExtractor { - private static final String TAG = "ImportantTopicExtractor"; + private static final String TAG = "ImportanceExtractor"; private static final boolean DBG = false; private RankingConfig mConfig; diff --git a/com/android/server/notification/ManagedServices.java b/com/android/server/notification/ManagedServices.java index add4184f..019c7c2f 100644 --- a/com/android/server/notification/ManagedServices.java +++ b/com/android/server/notification/ManagedServices.java @@ -45,12 +45,16 @@ import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.service.notification.ManagedServiceInfoProto; +import android.service.notification.ManagedServicesProto; +import android.service.notification.ManagedServicesProto.ServiceProto; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import android.util.proto.ProtoOutputStream; import com.android.internal.util.XmlUtils; import com.android.server.notification.NotificationManagerService.DumpFilter; @@ -214,6 +218,53 @@ abstract public class ManagedServices { } } + public void dump(ProtoOutputStream proto, DumpFilter filter) { + proto.write(ManagedServicesProto.CAPTION, getCaption()); + final int N = mApproved.size(); + for (int i = 0 ; i < N; i++) { + final int userId = mApproved.keyAt(i); + final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i); + if (approvedByType != null) { + final int M = approvedByType.size(); + for (int j = 0; j < M; j++) { + final boolean isPrimary = approvedByType.keyAt(j); + final ArraySet<String> approved = approvedByType.valueAt(j); + if (approvedByType != null && approvedByType.size() > 0) { + final long sToken = proto.start(ManagedServicesProto.APPROVED); + for (String s : approved) { + proto.write(ServiceProto.NAME, s); + } + proto.write(ServiceProto.USER_ID, userId); + proto.write(ServiceProto.IS_PRIMARY, isPrimary); + proto.end(sToken); + } + } + } + } + + for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) { + if (filter != null && !filter.matches(cmpt)) continue; + + final long cToken = proto.start(ManagedServicesProto.ENABLED); + cmpt.toProto(proto); + proto.end(cToken); + } + + for (ManagedServiceInfo info : mServices) { + if (filter != null && !filter.matches(info.component)) continue; + + final long lToken = proto.start(ManagedServicesProto.LIVE_SERVICES); + info.toProto(proto, this); + proto.end(lToken); + } + + for (ComponentName name : mSnoozingForCurrentProfiles) { + final long cToken = proto.start(ManagedServicesProto.SNOOZED); + name.toProto(proto); + proto.end(cToken); + } + } + protected void onSettingRestored(String element, String value, int backupSdkInt, int userId) { if (!mUseXml) { Slog.d(TAG, "Restored managed service setting: " + element); @@ -294,6 +345,7 @@ abstract public class ManagedServices { } if (type == XmlPullParser.START_TAG) { if (TAG_MANAGED_SERVICES.equals(tag)) { + Slog.i(TAG, "Read " + mConfig.caption + " permissions from xml"); final String approved = XmlUtils.readStringAttribute(parser, ATT_APPROVED_LIST); final int userId = XmlUtils.readIntAttribute(parser, ATT_USER_ID, 0); final boolean isPrimary = @@ -353,6 +405,8 @@ abstract public class ManagedServices { protected void setPackageOrComponentEnabled(String pkgOrComponent, int userId, boolean isPrimary, boolean enabled) { + Slog.i(TAG, + (enabled ? " Allowing " : "Disallowing ") + mConfig.caption + " " + pkgOrComponent); ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.get(userId); if (allowedByType == null) { allowedByType = new ArrayMap<>(); @@ -460,6 +514,7 @@ abstract public class ManagedServices { } public void onUserRemoved(int user) { + Slog.i(TAG, "Removing approved services for removed user " + user); mApproved.remove(user); rebindServices(true); } @@ -491,6 +546,17 @@ abstract public class ManagedServices { return null; } + protected boolean isServiceTokenValidLocked(IInterface service) { + if (service == null) { + return false; + } + ManagedServiceInfo info = getServiceFromTokenLocked(service); + if (info != null) { + return true; + } + return false; + } + protected ManagedServiceInfo checkServiceTokenLocked(IInterface service) { checkNotNull(service); ManagedServiceInfo info = getServiceFromTokenLocked(service); @@ -543,10 +609,8 @@ abstract public class ManagedServices { } // State changed - if (DEBUG) { - Slog.d(TAG, ((enabled) ? "Enabling " : "Disabling ") + "component " + - component.flattenToShortString()); - } + Slog.d(TAG, ((enabled) ? "Enabling " : "Disabling ") + "component " + + component.flattenToShortString()); synchronized (mMutex) { final int[] userIds = mUserProfiles.getCurrentProfileIds(); @@ -628,12 +692,10 @@ abstract public class ManagedServices { int P = approved.size(); for (int k = P - 1; k >= 0; k--) { final String approvedPackageOrComponent = approved.valueAt(k); - if (!hasMatchingServices(approvedPackageOrComponent, userId)){ + if (!isValidEntry(approvedPackageOrComponent, userId)){ approved.removeAt(k); - if (DEBUG) { - Slog.v(TAG, "Removing " + approvedPackageOrComponent - + " from approved list; no matching services found"); - } + Slog.v(TAG, "Removing " + approvedPackageOrComponent + + " from approved list; no matching services found"); } else { if (DEBUG) { Slog.v(TAG, "Keeping " + approvedPackageOrComponent @@ -678,6 +740,10 @@ abstract public class ManagedServices { } } + protected boolean isValidEntry(String packageOrComponent, int userId) { + return hasMatchingServices(packageOrComponent, userId); + } + private boolean hasMatchingServices(String packageOrComponent, int userId) { if (!TextUtils.isEmpty(packageOrComponent)) { final String packageName = getPackageName(packageOrComponent); @@ -774,7 +840,12 @@ abstract public class ManagedServices { ServiceInfo info = mPm.getServiceInfo(component, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userIds[i]); - if (info == null || !mConfig.bindPermission.equals(info.permission)) { + if (info == null) { + Slog.w(TAG, "Not binding " + getCaption() + " service " + component + + ": service not found"); + continue; + } + if (!mConfig.bindPermission.equals(info.permission)) { Slog.w(TAG, "Not binding " + getCaption() + " service " + component + ": it does not require the permission " + mConfig.bindPermission); continue; @@ -830,8 +901,7 @@ abstract public class ManagedServices { if (name.equals(info.component) && info.userid == userid) { // cut old connections - if (DEBUG) Slog.v(TAG, " disconnecting old " + getCaption() + ": " - + info.service); + Slog.v(TAG, " disconnecting old " + getCaption() + ": " + info.service); removeServiceLocked(i); if (info.connection != null) { mContext.unbindService(info.connection); @@ -859,7 +929,7 @@ abstract public class ManagedServices { appInfo != null ? appInfo.targetSdkVersion : Build.VERSION_CODES.BASE; try { - if (DEBUG) Slog.v(TAG, "binding: " + intent); + Slog.v(TAG, "binding: " + intent); ServiceConnection serviceConnection = new ServiceConnection() { IInterface mService; @@ -917,8 +987,7 @@ abstract public class ManagedServices { final int N = mServices.size(); for (int i = N - 1; i >= 0; i--) { final ManagedServiceInfo info = mServices.get(i); - if (name.equals(info.component) - && info.userid == userid) { + if (name.equals(info.component) && info.userid == userid) { removeServiceLocked(i); if (info.connection != null) { try { @@ -945,9 +1014,8 @@ abstract public class ManagedServices { final int N = mServices.size(); for (int i = N - 1; i >= 0; i--) { final ManagedServiceInfo info = mServices.get(i); - if (info.service.asBinder() == service.asBinder() - && info.userid == userid) { - if (DEBUG) Slog.d(TAG, "Removing active service " + info.component); + if (info.service.asBinder() == service.asBinder() && info.userid == userid) { + Slog.d(TAG, "Removing active service " + info.component); serviceInfo = removeServiceLocked(i); } } @@ -1035,6 +1103,16 @@ abstract public class ManagedServices { .append(']').toString(); } + public void toProto(ProtoOutputStream proto, ManagedServices host) { + final long cToken = proto.start(ManagedServiceInfoProto.COMPONENT); + component.toProto(proto); + proto.end(cToken); + proto.write(ManagedServiceInfoProto.USER_ID, userid); + proto.write(ManagedServiceInfoProto.SERVICE, service.getClass().getName()); + proto.write(ManagedServiceInfoProto.IS_SYSTEM, isSystem); + proto.write(ManagedServiceInfoProto.IS_GUEST, isGuest(host)); + } + public boolean enabledAndUserMatches(int nid) { if (!isEnabledForCurrentProfiles()) { return false; diff --git a/com/android/server/notification/NotificationAdjustmentExtractor.java b/com/android/server/notification/NotificationAdjustmentExtractor.java index 7c828458..3bfd93fd 100644 --- a/com/android/server/notification/NotificationAdjustmentExtractor.java +++ b/com/android/server/notification/NotificationAdjustmentExtractor.java @@ -22,7 +22,7 @@ import android.util.Slog; * Applies adjustments from the group helper and notification assistant */ public class NotificationAdjustmentExtractor implements NotificationSignalExtractor { - private static final String TAG = "BadgeExtractor"; + private static final String TAG = "AdjustmentExtractor"; private static final boolean DBG = false; @@ -35,7 +35,6 @@ public class NotificationAdjustmentExtractor implements NotificationSignalExtrac if (DBG) Slog.d(TAG, "skipping empty notification"); return null; } - record.applyAdjustments(); return null; diff --git a/com/android/server/notification/NotificationChannelExtractor.java b/com/android/server/notification/NotificationChannelExtractor.java index 46ab556f..11c7ab75 100644 --- a/com/android/server/notification/NotificationChannelExtractor.java +++ b/com/android/server/notification/NotificationChannelExtractor.java @@ -22,7 +22,7 @@ import android.util.Slog; * Stores the latest notification channel information for this notification */ public class NotificationChannelExtractor implements NotificationSignalExtractor { - private static final String TAG = "BadgeExtractor"; + private static final String TAG = "ChannelExtractor"; private static final boolean DBG = false; private RankingConfig mConfig; diff --git a/com/android/server/notification/NotificationDelegate.java b/com/android/server/notification/NotificationDelegate.java index 6a1401c2..36bc0962 100644 --- a/com/android/server/notification/NotificationDelegate.java +++ b/com/android/server/notification/NotificationDelegate.java @@ -16,6 +16,8 @@ package com.android.server.notification; +import android.service.notification.NotificationStats; + import com.android.internal.statusbar.NotificationVisibility; public interface NotificationDelegate { @@ -24,7 +26,8 @@ public interface NotificationDelegate { void onNotificationClick(int callingUid, int callingPid, String key); void onNotificationActionClick(int callingUid, int callingPid, String key, int actionIndex); void onNotificationClear(int callingUid, int callingPid, - String pkg, String tag, int id, int userId); + String pkg, String tag, int id, int userId, String key, + @NotificationStats.DismissalSurface int dismissalSurface); void onNotificationError(int callingUid, int callingPid, String pkg, String tag, int id, int uid, int initialPid, String message, int userId); @@ -35,4 +38,6 @@ public interface NotificationDelegate { NotificationVisibility[] newlyVisibleKeys, NotificationVisibility[] noLongerVisibleKeys); void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded); + void onNotificationDirectReplied(String key); + void onNotificationSettingsViewed(String key); } diff --git a/com/android/server/notification/NotificationManagerInternal.java b/com/android/server/notification/NotificationManagerInternal.java index 4923b06e..f1476b34 100644 --- a/com/android/server/notification/NotificationManagerInternal.java +++ b/com/android/server/notification/NotificationManagerInternal.java @@ -17,8 +17,10 @@ package com.android.server.notification; import android.app.Notification; +import android.app.NotificationChannel; public interface NotificationManagerInternal { + NotificationChannel getNotificationChannel(String pkg, int uid, String channelId); void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid, String tag, int id, Notification notification, int userId); diff --git a/com/android/server/notification/NotificationManagerService.java b/com/android/server/notification/NotificationManagerService.java index fe39fccb..14cd0555 100644 --- a/com/android/server/notification/NotificationManagerService.java +++ b/com/android/server/notification/NotificationManagerService.java @@ -16,8 +16,10 @@ package com.android.server.notification; +import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.app.NotificationManager.IMPORTANCE_NONE; +import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import static android.content.pm.PackageManager.FEATURE_LEANBACK; import static android.content.pm.PackageManager.FEATURE_TELEVISION; import static android.content.pm.PackageManager.PERMISSION_GRANTED; @@ -125,12 +127,14 @@ import android.service.notification.Condition; import android.service.notification.IConditionProvider; import android.service.notification.INotificationListener; import android.service.notification.IStatusBarNotificationHolder; +import android.service.notification.ListenersDisablingEffectsProto; import android.service.notification.NotificationAssistantService; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationRankingUpdate; import android.service.notification.NotificationRecordProto; import android.service.notification.NotificationServiceDumpProto; import android.service.notification.NotificationServiceProto; +import android.service.notification.NotificationStats; import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; import android.service.notification.ZenModeConfig; @@ -675,7 +679,14 @@ public class NotificationManagerService extends SystemService { @Override public void onNotificationClear(int callingUid, int callingPid, - String pkg, String tag, int id, int userId) { + String pkg, String tag, int id, int userId, String key, + @NotificationStats.DismissalSurface int dismissalSurface) { + synchronized (mNotificationLock) { + NotificationRecord r = mNotificationsByKey.get(key); + if (r != null) { + r.recordDismissalSurface(dismissalSurface); + } + } cancelNotification(callingUid, callingPid, pkg, tag, id, 0, Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, true, userId, REASON_CANCEL, null); @@ -761,12 +772,35 @@ public class NotificationManagerService extends SystemService { .setType(expanded ? MetricsEvent.TYPE_DETAIL : MetricsEvent.TYPE_COLLAPSE)); } + if (expanded) { + r.recordExpanded(); + } EventLogTags.writeNotificationExpansion(key, userAction ? 1 : 0, expanded ? 1 : 0, r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now)); } } } + + @Override + public void onNotificationDirectReplied(String key) { + synchronized (mNotificationLock) { + NotificationRecord r = mNotificationsByKey.get(key); + if (r != null) { + r.recordDirectReplied(); + } + } + } + + @Override + public void onNotificationSettingsViewed(String key) { + synchronized (mNotificationLock) { + NotificationRecord r = mNotificationsByKey.get(key); + if (r != null) { + r.recordViewedSettings(); + } + } + } }; @GuardedBy("mNotificationLock") @@ -1142,6 +1176,12 @@ public class NotificationManagerService extends SystemService { } @VisibleForTesting + NotificationRecord getNotificationRecord(String key) { + return mNotificationsByKey.get(key); + } + + + @VisibleForTesting void setSystemReady(boolean systemReady) { mSystemReady = systemReady; } @@ -1216,7 +1256,7 @@ public class NotificationManagerService extends SystemService { mUsageStats = usageStats; mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper()); mRankingHelper = new RankingHelper(getContext(), - getContext().getPackageManager(), + mPackageManagerClient, mRankingHandler, mUsageStats, extractorNames); @@ -1269,13 +1309,11 @@ public class NotificationManagerService extends SystemService { R.array.config_notificationFallbackVibePattern, VIBRATE_PATTERN_MAXLEN, DEFAULT_VIBRATE_PATTERN); - mInCallNotificationUri = Uri.parse("file://" + resources.getString(R.string.config_inCallNotificationSound)); mInCallNotificationAudioAttributes = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) - .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED) .build(); mInCallNotificationVolume = resources.getFloat(R.dimen.config_inCallNotificationVolume); @@ -1476,7 +1514,7 @@ public class NotificationManagerService extends SystemService { } } } - mRankingHelper.updateNotificationChannel(pkg, uid, channel); + mRankingHelper.updateNotificationChannel(pkg, uid, channel, true); if (!fromListener) { final NotificationChannel modifiedChannel = @@ -3239,14 +3277,51 @@ public class NotificationManagerService extends SystemService { } } proto.end(records); - } - long zenLog = proto.start(NotificationServiceDumpProto.ZEN); - mZenModeHelper.dump(proto); - for (ComponentName suppressor : mEffectsSuppressors) { - proto.write(ZenModeProto.SUPPRESSORS, suppressor.toString()); + long zenLog = proto.start(NotificationServiceDumpProto.ZEN); + mZenModeHelper.dump(proto); + for (ComponentName suppressor : mEffectsSuppressors) { + proto.write(ZenModeProto.SUPPRESSORS, suppressor.toString()); + } + proto.end(zenLog); + + long listenersToken = proto.start(NotificationServiceDumpProto.NOTIFICATION_LISTENERS); + mListeners.dump(proto, filter); + proto.end(listenersToken); + + proto.write(NotificationServiceDumpProto.LISTENER_HINTS, mListenerHints); + + for (int i = 0; i < mListenersDisablingEffects.size(); ++i) { + long effectsToken = proto.start( + NotificationServiceDumpProto.LISTENERS_DISABLING_EFFECTS); + + proto.write( + ListenersDisablingEffectsProto.HINT, mListenersDisablingEffects.keyAt(i)); + final ArraySet<ManagedServiceInfo> listeners = + mListenersDisablingEffects.valueAt(i); + for (int j = 0; j < listeners.size(); j++) { + final ManagedServiceInfo listener = listeners.valueAt(i); + listenersToken = proto.start(ListenersDisablingEffectsProto.LISTENERS); + listener.toProto(proto, null); + proto.end(listenersToken); + } + + proto.end(effectsToken); + } + + long assistantsToken = proto.start( + NotificationServiceDumpProto.NOTIFICATION_ASSISTANTS); + mAssistants.dump(proto, filter); + proto.end(assistantsToken); + + long conditionsToken = proto.start(NotificationServiceDumpProto.CONDITION_PROVIDERS); + mConditionProviders.dump(proto, filter); + proto.end(conditionsToken); + + long rankingToken = proto.start(NotificationServiceDumpProto.RANKING_CONFIG); + mRankingHelper.dump(proto, filter); + proto.end(rankingToken); } - proto.end(zenLog); proto.flush(); } @@ -3401,6 +3476,12 @@ public class NotificationManagerService extends SystemService { */ private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() { @Override + public NotificationChannel getNotificationChannel(String pkg, int uid, String + channelId) { + return mRankingHelper.getNotificationChannel(pkg, uid, channelId, false); + } + + @Override public void enqueueNotification(String pkg, String opPkg, int callingUid, int callingPid, String tag, int id, Notification notification, int userId) { enqueueNotificationInternal(pkg, opPkg, callingUid, callingPid, tag, id, notification, @@ -3519,6 +3600,21 @@ public class NotificationManagerService extends SystemService { user, null, System.currentTimeMillis()); final NotificationRecord r = new NotificationRecord(getContext(), n, channel); + if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0 + && (channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0 + && (r.getImportance() == IMPORTANCE_MIN || r.getImportance() == IMPORTANCE_NONE)) { + // Increase the importance of foreground service notifications unless the user had an + // opinion otherwise + if (TextUtils.isEmpty(channelId) + || NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) { + r.setImportance(IMPORTANCE_LOW, "Bumped for foreground service"); + } else { + channel.setImportance(IMPORTANCE_LOW); + mRankingHelper.updateNotificationChannel(pkg, notificationUid, channel, false); + r.updateNotificationChannel(channel); + } + } + if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r, r.sbn.getOverrideGroupKey() != null)) { return; @@ -3752,6 +3848,8 @@ public class NotificationManagerService extends SystemService { MetricsLogger.action(r.getLogMaker() .setCategory(MetricsEvent.NOTIFICATION_SNOOZED) .setType(MetricsEvent.TYPE_CLOSE) + .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_DURATION_MS, + mDuration) .addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA, mSnoozeCriterionId == null ? 0 : 1)); boolean wasPosted = removeFromNotificationListsLocked(r); @@ -3763,6 +3861,7 @@ public class NotificationManagerService extends SystemService { } else { mSnoozeHelper.snooze(r, mDuration); } + r.recordSnoozed(); savePolicyFile(); } } @@ -3902,7 +4001,7 @@ public class NotificationManagerService extends SystemService { Slog.e(TAG, "Not posting notification without small icon: " + notification); if (old != null && !old.isCanceled) { mListeners.notifyRemovedLocked(n, - NotificationListenerService.REASON_ERROR); + NotificationListenerService.REASON_ERROR, null); mHandler.post(new Runnable() { @Override public void run() { @@ -4028,19 +4127,19 @@ public class NotificationManagerService extends SystemService { if (mSystemReady && mAudioManager != null) { Uri soundUri = record.getSound(); hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri); - long[] vibration = record.getVibration(); // Demote sound to vibration if vibration missing & phone in vibration mode. if (vibration == null && hasValidSound && (mAudioManager.getRingerModeInternal() - == AudioManager.RINGER_MODE_VIBRATE)) { + == AudioManager.RINGER_MODE_VIBRATE) + && mAudioManager.getStreamVolume( + AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) == 0) { vibration = mFallbackVibrationPattern; } hasValidVibrate = vibration != null; boolean hasAudibleAlert = hasValidSound || hasValidVibrate; - if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) { if (DBG) Slog.v(TAG, "Interrupting!"); if (hasValidSound) { @@ -4137,8 +4236,9 @@ public class NotificationManagerService extends SystemService { boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0; // do not play notifications if there is a user of exclusive audio focus // or the device is in vibrate mode - if (!mAudioManager.isAudioFocusExclusive() && mAudioManager.getRingerModeInternal() - != AudioManager.RINGER_MODE_VIBRATE) { + if (!mAudioManager.isAudioFocusExclusive() && (mAudioManager.getRingerModeInternal() + != AudioManager.RINGER_MODE_VIBRATE || mAudioManager.getStreamVolume( + AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) != 0)) { final long identity = Binder.clearCallingIdentity(); try { final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); @@ -4394,6 +4494,7 @@ public class NotificationManagerService extends SystemService { ArrayList<String> groupKeyBefore = new ArrayList<>(N); ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N); ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N); + ArrayList<Integer> userSentimentBefore = new ArrayList<>(N); for (int i = 0; i < N; i++) { final NotificationRecord r = mNotificationList.get(i); orderBefore.add(r.getKey()); @@ -4403,6 +4504,7 @@ public class NotificationManagerService extends SystemService { groupKeyBefore.add(r.getGroupKey()); overridePeopleBefore.add(r.getPeopleOverride()); snoozeCriteriaBefore.add(r.getSnoozeCriteria()); + userSentimentBefore.add(r.getUserSentiment()); mRankingHelper.extractSignals(r); } mRankingHelper.sort(mNotificationList); @@ -4414,7 +4516,8 @@ public class NotificationManagerService extends SystemService { || !Objects.equals(channelBefore.get(i), r.getChannel()) || !Objects.equals(groupKeyBefore.get(i), r.getGroupKey()) || !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride()) - || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria())) { + || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria()) + || !Objects.equals(userSentimentBefore.get(i), r.getUserSentiment())) { mHandler.scheduleSendRankingUpdate(); return; } @@ -4607,6 +4710,10 @@ public class NotificationManagerService extends SystemService { // Record caller. recordCallerLocked(r); + if (r.getStats().getDismissalSurface() == NotificationStats.DISMISSAL_NOT_DISMISSED) { + r.recordDismissalSurface(NotificationStats.DISMISSAL_OTHER); + } + // tell the app if (sendDelete) { if (r.getNotification().deleteIntent != null) { @@ -4627,7 +4734,7 @@ public class NotificationManagerService extends SystemService { if (reason != REASON_SNOOZED) { r.isCanceled = true; } - mListeners.notifyRemovedLocked(r.sbn, reason); + mListeners.notifyRemovedLocked(r.sbn, reason, r.getStats()); mHandler.post(new Runnable() { @Override public void run() { @@ -5221,6 +5328,7 @@ public class NotificationManagerService extends SystemService { Bundle overridePeople = new Bundle(); Bundle snoozeCriteria = new Bundle(); Bundle showBadge = new Bundle(); + Bundle userSentiment = new Bundle(); for (int i = 0; i < N; i++) { NotificationRecord record = mNotificationList.get(i); if (!isVisibleToListener(record.sbn, info)) { @@ -5246,6 +5354,7 @@ public class NotificationManagerService extends SystemService { overridePeople.putStringArrayList(key, record.getPeopleOverride()); snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria()); showBadge.putBoolean(key, record.canShowBadge()); + userSentiment.putInt(key, record.getUserSentiment()); } final int M = keys.size(); String[] keysAr = keys.toArray(new String[M]); @@ -5256,7 +5365,7 @@ public class NotificationManagerService extends SystemService { } return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides, suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys, - channels, overridePeople, snoozeCriteria, showBadge); + channels, overridePeople, snoozeCriteria, showBadge, userSentiment); } boolean hasCompanionDevice(ManagedServiceInfo info) { @@ -5345,7 +5454,7 @@ public class NotificationManagerService extends SystemService { @Override protected Config getConfig() { Config c = new Config(); - c.caption = "notification assistant service"; + c.caption = "notification assistant"; c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE; c.xmlTag = TAG_ENABLED_NOTIFICATION_ASSISTANTS; c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT; @@ -5388,8 +5497,6 @@ public class NotificationManagerService extends SystemService { continue; } - final int importance = r.getImportance(); - final boolean fromUser = r.isImportanceFromUser(); final StatusBarNotification sbnToPost = trimCache.ForListener(info); mHandler.post(new Runnable() { @Override @@ -5420,6 +5527,10 @@ public class NotificationManagerService extends SystemService { final String snoozeCriterionId) { TrimCache trimCache = new TrimCache(sbn); for (final ManagedServiceInfo info : getServices()) { + boolean sbnVisible = isVisibleToListener(sbn, info); + if (!sbnVisible) { + continue; + } final StatusBarNotification sbnToPost = trimCache.ForListener(info); mHandler.post(new Runnable() { @Override @@ -5541,7 +5652,8 @@ public class NotificationManagerService extends SystemService { mHandler.post(new Runnable() { @Override public void run() { - notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED); + notifyRemoved( + info, oldSbnLightClone, update, null, REASON_USER_STOPPED); } }); continue; @@ -5561,7 +5673,8 @@ public class NotificationManagerService extends SystemService { * asynchronously notify all listeners about a removed notification */ @GuardedBy("mNotificationLock") - public void notifyRemovedLocked(StatusBarNotification sbn, int reason) { + public void notifyRemovedLocked(StatusBarNotification sbn, int reason, + NotificationStats notificationStats) { // make a copy in case changes are made to the underlying Notification object // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the // notification @@ -5570,11 +5683,14 @@ public class NotificationManagerService extends SystemService { if (!isVisibleToListener(sbn, info)) { continue; } + // Only assistants can get stats + final NotificationStats stats = mAssistants.isServiceTokenValidLocked(info.service) + ? notificationStats : null; final NotificationRankingUpdate update = makeRankingUpdateLocked(info); mHandler.post(new Runnable() { @Override public void run() { - notifyRemoved(info, sbnLight, update, reason); + notifyRemoved(info, sbnLight, update, stats, reason); } }); } @@ -5679,14 +5795,14 @@ public class NotificationManagerService extends SystemService { } private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn, - NotificationRankingUpdate rankingUpdate, int reason) { + NotificationRankingUpdate rankingUpdate, NotificationStats stats, int reason) { if (!info.enabledAndUserMatches(sbn.getUserId())) { return; } final INotificationListener listener = (INotificationListener) info.service; StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn); try { - listener.onNotificationRemoved(sbnHolder, rankingUpdate, reason); + listener.onNotificationRemoved(sbnHolder, rankingUpdate, stats, reason); } catch (RemoteException ex) { Log.e(TAG, "unable to notify listener (removed): " + listener, ex); } @@ -5772,10 +5888,9 @@ public class NotificationManagerService extends SystemService { final DumpFilter filter = new DumpFilter(); for (int ai = 0; ai < args.length; ai++) { final String a = args[ai]; - if ("--proto".equals(args[0])) { + if ("--proto".equals(a)) { filter.proto = true; - } - if ("--noredact".equals(a) || "--reveal".equals(a)) { + } else if ("--noredact".equals(a) || "--reveal".equals(a)) { filter.redact = false; } else if ("p".equals(a) || "pkg".equals(a) || "--package".equals(a)) { if (ai < args.length-1) { @@ -5848,8 +5963,8 @@ public class NotificationManagerService extends SystemService { private class ShellCmd extends ShellCommand { public static final String USAGE = "help\n" - + "allow_listener COMPONENT\n" - + "disallow_listener COMPONENT\n" + + "allow_listener COMPONENT [user_id]\n" + + "disallow_listener COMPONENT [user_id]\n" + "set_assistant COMPONENT\n" + "remove_assistant COMPONENT\n" + "allow_dnd PACKAGE\n" @@ -5880,7 +5995,13 @@ public class NotificationManagerService extends SystemService { pw.println("Invalid listener - must be a ComponentName"); return -1; } - getBinderService().setNotificationListenerAccessGranted(cn, true); + String userId = getNextArg(); + if (userId == null) { + getBinderService().setNotificationListenerAccessGranted(cn, true); + } else { + getBinderService().setNotificationListenerAccessGrantedForUser( + cn, Integer.parseInt(userId), true); + } } break; case "disallow_listener": { @@ -5889,7 +6010,13 @@ public class NotificationManagerService extends SystemService { pw.println("Invalid listener - must be a ComponentName"); return -1; } - getBinderService().setNotificationListenerAccessGranted(cn, false); + String userId = getNextArg(); + if (userId == null) { + getBinderService().setNotificationListenerAccessGranted(cn, false); + } else { + getBinderService().setNotificationListenerAccessGrantedForUser( + cn, Integer.parseInt(userId), false); + } } break; case "allow_assistant": { diff --git a/com/android/server/notification/NotificationRecord.java b/com/android/server/notification/NotificationRecord.java index 77bf9e30..faa300f2 100644 --- a/com/android/server/notification/NotificationRecord.java +++ b/com/android/server/notification/NotificationRecord.java @@ -20,6 +20,8 @@ import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; +import static android.service.notification.NotificationListenerService.Ranking + .USER_SENTIMENT_NEUTRAL; import android.app.Notification; import android.app.NotificationChannel; @@ -41,6 +43,7 @@ import android.provider.Settings; import android.service.notification.Adjustment; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationRecordProto; +import android.service.notification.NotificationStats; import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; import android.text.TextUtils; @@ -84,8 +87,6 @@ public final class NotificationRecord { NotificationUsageStats.SingleNotificationStats stats; boolean isCanceled; - /** Whether the notification was seen by the user via one of the notification listeners. */ - boolean mIsSeen; // These members are used by NotificationSignalExtractors // to communicate with the ranking module. @@ -136,6 +137,8 @@ public final class NotificationRecord { private String mChannelIdLogTag; private final List<Adjustment> mAdjustments; + private final NotificationStats mStats; + private int mUserSentiment; @VisibleForTesting public NotificationRecord(Context context, StatusBarNotification sbn, @@ -156,6 +159,7 @@ public final class NotificationRecord { mImportance = calculateImportance(); mLight = calculateLights(); mAdjustments = new ArrayList<>(); + mStats = new NotificationStats(); } private boolean isPreChannelsNotification() { @@ -395,7 +399,7 @@ public final class NotificationRecord { pw.println(prefix + "flags=0x" + Integer.toHexString(notification.flags)); pw.println(prefix + "pri=" + notification.priority); pw.println(prefix + "key=" + sbn.getKey()); - pw.println(prefix + "seen=" + mIsSeen); + pw.println(prefix + "seen=" + mStats.hasSeen()); pw.println(prefix + "groupKey=" + getGroupKey()); pw.println(prefix + "fullscreenIntent=" + notification.fullScreenIntent); pw.println(prefix + "contentIntent=" + notification.contentIntent); @@ -572,6 +576,10 @@ public final class NotificationRecord { adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY); setOverrideGroupKey(groupOverrideKey); } + if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) { + setUserSentiment(adjustment.getSignals().getInt( + Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL)); + } } } } @@ -740,6 +748,7 @@ public final class NotificationRecord { .setType(visible ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE) .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, rank)); if (visible) { + setSeen(); MetricsLogger.histogram(mContext, "note_freshness", getFreshnessMs(now)); } EventLogTags.writeNotificationVisibility(getKey(), visible ? 1 : 0, @@ -777,12 +786,12 @@ public final class NotificationRecord { /** Check if any of the listeners have marked this notification as seen by the user. */ public boolean isSeen() { - return mIsSeen; + return mStats.hasSeen(); } /** Mark the notification as seen by the user. */ public void setSeen() { - mIsSeen = true; + mStats.setSeen(); } public void setAuthoritativeRank(int authoritativeRank) { @@ -883,6 +892,38 @@ public final class NotificationRecord { mSnoozeCriteria = snoozeCriteria; } + private void setUserSentiment(int userSentiment) { + mUserSentiment = userSentiment; + } + + public int getUserSentiment() { + return mUserSentiment; + } + + public NotificationStats getStats() { + return mStats; + } + + public void recordExpanded() { + mStats.setExpanded(); + } + + public void recordDirectReplied() { + mStats.setDirectReplied(); + } + + public void recordDismissalSurface(@NotificationStats.DismissalSurface int surface) { + mStats.setDismissalSurface(surface); + } + + public void recordSnoozed() { + mStats.setSnoozed(); + } + + public void recordViewedSettings() { + mStats.setViewedSettings(); + } + public LogMaker getLogMaker(long now) { if (mLogMaker == null) { // initialize fields that only change on update (so a new record) diff --git a/com/android/server/notification/PriorityExtractor.java b/com/android/server/notification/PriorityExtractor.java index 5d5d39de..7a287dba 100644 --- a/com/android/server/notification/PriorityExtractor.java +++ b/com/android/server/notification/PriorityExtractor.java @@ -23,7 +23,7 @@ import android.util.Slog; * Determines if the given notification can bypass Do Not Disturb. */ public class PriorityExtractor implements NotificationSignalExtractor { - private static final String TAG = "ImportantTopicExtractor"; + private static final String TAG = "PriorityExtractor"; private static final boolean DBG = false; private RankingConfig mConfig; diff --git a/com/android/server/notification/RankingConfig.java b/com/android/server/notification/RankingConfig.java index b5ef1c60..b9c0d907 100644 --- a/com/android/server/notification/RankingConfig.java +++ b/com/android/server/notification/RankingConfig.java @@ -39,7 +39,7 @@ public interface RankingConfig { int uid, boolean includeDeleted); void createNotificationChannel(String pkg, int uid, NotificationChannel channel, boolean fromTargetApp); - void updateNotificationChannel(String pkg, int uid, NotificationChannel channel); + void updateNotificationChannel(String pkg, int uid, NotificationChannel channel, boolean fromUser); NotificationChannel getNotificationChannel(String pkg, int uid, String channelId, boolean includeDeleted); void deleteNotificationChannel(String pkg, int uid, String channelId); void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId); diff --git a/com/android/server/notification/RankingHelper.java b/com/android/server/notification/RankingHelper.java index fc24581f..d7e9cf37 100644 --- a/com/android/server/notification/RankingHelper.java +++ b/com/android/server/notification/RankingHelper.java @@ -36,10 +36,13 @@ import android.os.Build; import android.os.UserHandle; import android.provider.Settings.Secure; import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.RankingHelperProto; +import android.service.notification.RankingHelperProto.RecordProto; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Slog; import android.util.SparseBooleanArray; +import android.util.proto.ProtoOutputStream; import org.json.JSONArray; import org.json.JSONException; @@ -228,7 +231,11 @@ public class RankingHelper implements RankingConfig { if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) { NotificationChannel channel = new NotificationChannel(id, channelName, channelImportance); - channel.populateFromXml(parser); + if (forRestore) { + channel.populateFromXmlForRestore(parser, mContext); + } else { + channel.populateFromXml(parser); + } r.channels.put(id, channel); } } @@ -391,7 +398,11 @@ public class RankingHelper implements RankingConfig { } for (NotificationChannel channel : r.channels.values()) { - if (!forBackup || (forBackup && !channel.isDeleted())) { + if (forBackup) { + if (!channel.isDeleted()) { + channel.writeXmlForBackup(out, mContext); + } + } else { channel.writeXml(out); } } @@ -613,7 +624,8 @@ public class RankingHelper implements RankingConfig { } @Override - public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel) { + public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel, + boolean fromUser) { Preconditions.checkNotNull(updatedChannel); Preconditions.checkNotNull(updatedChannel.getId()); Record r = getOrCreateRecord(pkg, uid); @@ -627,7 +639,11 @@ public class RankingHelper implements RankingConfig { if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) { updatedChannel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE); } - lockFieldsForUpdate(channel, updatedChannel); + updatedChannel.unlockFields(updatedChannel.getUserLockedFields()); + updatedChannel.lockFields(channel.getUserLockedFields()); + if (fromUser) { + lockFieldsForUpdate(channel, updatedChannel); + } r.channels.put(updatedChannel.getId(), updatedChannel); if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(updatedChannel.getId())) { @@ -874,8 +890,6 @@ public class RankingHelper implements RankingConfig { @VisibleForTesting void lockFieldsForUpdate(NotificationChannel original, NotificationChannel update) { - update.unlockFields(update.getUserLockedFields()); - update.lockFields(original.getUserLockedFields()); if (original.canBypassDnd() != update.canBypassDnd()) { update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY); } @@ -912,8 +926,7 @@ public class RankingHelper implements RankingConfig { pw.print(" "); pw.println(mSignalExtractors[i]); } - } - if (filter == null) { + pw.print(prefix); pw.println("per-package config:"); } @@ -925,6 +938,52 @@ public class RankingHelper implements RankingConfig { dumpRecords(pw, prefix, filter, mRestoredWithoutUids); } + public void dump(ProtoOutputStream proto, NotificationManagerService.DumpFilter filter) { + final int N = mSignalExtractors.length; + for (int i = 0; i < N; i++) { + proto.write(RankingHelperProto.NOTIFICATION_SIGNAL_EXTRACTORS, + mSignalExtractors[i].getClass().getSimpleName()); + } + synchronized (mRecords) { + dumpRecords(proto, RankingHelperProto.RECORDS, filter, mRecords); + } + dumpRecords(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter, + mRestoredWithoutUids); + } + + private static void dumpRecords(ProtoOutputStream proto, long fieldId, + NotificationManagerService.DumpFilter filter, ArrayMap<String, Record> records) { + final int N = records.size(); + long fToken; + for (int i = 0; i < N; i++) { + final Record r = records.valueAt(i); + if (filter == null || filter.matches(r.pkg)) { + fToken = proto.start(fieldId); + + proto.write(RecordProto.PACKAGE, r.pkg); + proto.write(RecordProto.UID, r.uid); + proto.write(RecordProto.IMPORTANCE, r.importance); + proto.write(RecordProto.PRIORITY, r.priority); + proto.write(RecordProto.VISIBILITY, r.visibility); + proto.write(RecordProto.SHOW_BADGE, r.showBadge); + + long token; + for (NotificationChannel channel : r.channels.values()) { + token = proto.start(RecordProto.CHANNELS); + channel.toProto(proto); + proto.end(token); + } + for (NotificationChannelGroup group : r.groups.values()) { + token = proto.start(RecordProto.CHANNEL_GROUPS); + group.toProto(proto); + proto.end(token); + } + + proto.end(fToken); + } + } + } + private static void dumpRecords(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter, ArrayMap<String, Record> records) { final int N = records.size(); diff --git a/com/android/server/notification/ScheduleCalendar.java b/com/android/server/notification/ScheduleCalendar.java index 9e8b2e34..40230bd2 100644 --- a/com/android/server/notification/ScheduleCalendar.java +++ b/com/android/server/notification/ScheduleCalendar.java @@ -42,7 +42,8 @@ public class ScheduleCalendar { public void maybeSetNextAlarm(long now, long nextAlarm) { if (mSchedule != null) { - if (mSchedule.exitAtAlarm && now > mSchedule.nextAlarm) { + if (mSchedule.exitAtAlarm + && (now > mSchedule.nextAlarm || nextAlarm < mSchedule.nextAlarm)) { mSchedule.nextAlarm = nextAlarm; } } diff --git a/com/android/server/notification/ZenModeHelper.java b/com/android/server/notification/ZenModeHelper.java index ffdafc56..9fcc67df 100644 --- a/com/android/server/notification/ZenModeHelper.java +++ b/com/android/server/notification/ZenModeHelper.java @@ -567,7 +567,7 @@ public class ZenModeHelper { proto.write(ZenModeProto.ENABLED_ACTIVE_CONDITIONS, rule.toString()); } } - proto.write(ZenModeProto.POLICY, mConfig.toNotificationPolicy().toString()); + mConfig.toNotificationPolicy().toProto(proto, ZenModeProto.POLICY); proto.write(ZenModeProto.SUPPRESSED_EFFECTS, mSuppressedEffects); } } diff --git a/com/android/server/oemlock/OemLockService.java b/com/android/server/oemlock/OemLockService.java index 40c66394..5b3d1eca 100644 --- a/com/android/server/oemlock/OemLockService.java +++ b/com/android/server/oemlock/OemLockService.java @@ -31,6 +31,7 @@ import android.os.UserManager; import android.os.UserManagerInternal; import android.os.UserManagerInternal.UserRestrictionsListener; import android.service.oemlock.IOemLockService; +import android.service.persistentdata.PersistentDataBlockManager; import android.util.Slog; import com.android.server.LocalServices; @@ -98,6 +99,7 @@ public class OemLockService extends SystemService { !newRestrictions.getBoolean(UserManager.DISALLOW_FACTORY_RESET); if (!unlockAllowedByAdmin) { mOemLock.setOemUnlockAllowedByDevice(false); + setPersistentDataBlockOemUnlockAllowedBit(false); } } } @@ -158,6 +160,7 @@ public class OemLockService extends SystemService { } mOemLock.setOemUnlockAllowedByDevice(allowedByUser); + setPersistentDataBlockOemUnlockAllowedBit(allowedByUser); } finally { Binder.restoreCallingIdentity(token); } @@ -202,6 +205,20 @@ public class OemLockService extends SystemService { } }; + /** + * Always synchronize the OemUnlockAllowed bit to the FRP partition, which + * is used to erase FRP information on a unlockable device. + */ + private void setPersistentDataBlockOemUnlockAllowedBit(boolean allowed) { + final PersistentDataBlockManager pdbm = (PersistentDataBlockManager) + mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE); + // if mOemLock is PersistentDataBlockLock, then the bit should have already been set + if (pdbm != null && !(mOemLock instanceof PersistentDataBlockLock)) { + Slog.i(TAG, "Update OEM Unlock bit in pst partition to " + allowed); + pdbm.setOemUnlockEnabled(allowed); + } + } + private boolean isOemUnlockAllowedByAdmin() { return !UserManager.get(mContext) .hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET, UserHandle.SYSTEM); diff --git a/com/android/server/pm/BackgroundDexOptService.java b/com/android/server/pm/BackgroundDexOptService.java index 415c9a9c..6d8cac0c 100644 --- a/com/android/server/pm/BackgroundDexOptService.java +++ b/com/android/server/pm/BackgroundDexOptService.java @@ -342,8 +342,7 @@ public class BackgroundDexOptService extends JobService { DexoptOptions.DEXOPT_BOOT_COMPLETE | (downgrade ? DexoptOptions.DEXOPT_DOWNGRADE : 0); if (is_for_primary_dex) { - int result = pm.performDexOptWithStatus(new DexoptOptions(pkg, - PackageManagerService.REASON_BACKGROUND_DEXOPT, + int result = pm.performDexOptWithStatus(new DexoptOptions(pkg, reason, dexoptFlags)); success = result != PackageDexOptimizer.DEX_OPT_FAILED; if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) { @@ -351,8 +350,7 @@ public class BackgroundDexOptService extends JobService { } } else { success = pm.performDexOpt(new DexoptOptions(pkg, - PackageManagerService.REASON_BACKGROUND_DEXOPT, - dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX)); + reason, dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX)); } if (success) { // Dexopt succeeded, remove package from the list of failing ones. diff --git a/com/android/server/pm/BasePermission.java b/com/android/server/pm/BasePermission.java deleted file mode 100644 index 30fda1e5..00000000 --- a/com/android/server/pm/BasePermission.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2006 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.pm; - -import android.content.pm.PackageParser; -import android.content.pm.PermissionInfo; -import android.os.UserHandle; - -final class BasePermission { - final static int TYPE_NORMAL = 0; - - final static int TYPE_BUILTIN = 1; - - final static int TYPE_DYNAMIC = 2; - - final String name; - - String sourcePackage; - - PackageSettingBase packageSetting; - - final int type; - - int protectionLevel; - - PackageParser.Permission perm; - - PermissionInfo pendingInfo; - - /** UID that owns the definition of this permission */ - int uid; - - /** Additional GIDs given to apps granted this permission */ - private int[] gids; - - /** - * Flag indicating that {@link #gids} should be adjusted based on the - * {@link UserHandle} the granted app is running as. - */ - private boolean perUser; - - BasePermission(String _name, String _sourcePackage, int _type) { - name = _name; - sourcePackage = _sourcePackage; - type = _type; - // Default to most conservative protection level. - protectionLevel = PermissionInfo.PROTECTION_SIGNATURE; - } - - @Override - public String toString() { - return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + name - + "}"; - } - - public void setGids(int[] gids, boolean perUser) { - this.gids = gids; - this.perUser = perUser; - } - - public int[] computeGids(int userId) { - if (perUser) { - final int[] userGids = new int[gids.length]; - for (int i = 0; i < gids.length; i++) { - userGids[i] = UserHandle.getUid(userId, gids[i]); - } - return userGids; - } else { - return gids; - } - } - - public boolean isRuntime() { - return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) - == PermissionInfo.PROTECTION_DANGEROUS; - } - - public boolean isDevelopment() { - return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) - == PermissionInfo.PROTECTION_SIGNATURE - && (protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0; - } - - public boolean isInstant() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0; - } - - public boolean isRuntimeOnly() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0; - } -} diff --git a/com/android/server/pm/DefaultPermissionGrantPolicy.java b/com/android/server/pm/DefaultPermissionGrantPolicy.java deleted file mode 100644 index a3811baf..00000000 --- a/com/android/server/pm/DefaultPermissionGrantPolicy.java +++ /dev/null @@ -1,1244 +0,0 @@ -/* - * Copyright (C) 2015 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.pm; - -import android.Manifest; -import android.annotation.NonNull; -import android.app.ActivityManager; -import android.app.DownloadManager; -import android.app.admin.DevicePolicyManager; -import android.companion.CompanionDeviceManager; -import android.content.Intent; -import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal.PackagesProvider; -import android.content.pm.PackageManagerInternal.SyncAdapterPackagesProvider; -import android.content.pm.PackageParser; -import android.content.pm.ProviderInfo; -import android.content.pm.ResolveInfo; -import android.media.RingtoneManager; -import android.net.Uri; -import android.os.Build; -import android.os.Environment; -import android.os.Handler; -import android.os.Message; -import android.os.UserHandle; -import android.os.storage.StorageManager; -import android.print.PrintManager; -import android.provider.CalendarContract; -import android.provider.ContactsContract; -import android.provider.MediaStore; -import android.provider.Telephony.Sms.Intents; -import android.telephony.TelephonyManager; -import android.security.Credentials; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.Log; -import android.util.Slog; -import android.util.Xml; -import com.android.internal.util.XmlUtils; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static android.os.Process.FIRST_APPLICATION_UID; - -/** - * This class is the policy for granting runtime permissions to - * platform components and default handlers in the system such - * that the device is usable out-of-the-box. For example, the - * shell UID is a part of the system and the Phone app should - * have phone related permission by default. - */ -final class DefaultPermissionGrantPolicy { - private static final String TAG = "DefaultPermGrantPolicy"; // must be <= 23 chars - private static final boolean DEBUG = false; - - private static final int DEFAULT_FLAGS = - PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.MATCH_UNINSTALLED_PACKAGES; - - private static final String AUDIO_MIME_TYPE = "audio/mpeg"; - - private static final String TAG_EXCEPTIONS = "exceptions"; - private static final String TAG_EXCEPTION = "exception"; - private static final String TAG_PERMISSION = "permission"; - private static final String ATTR_PACKAGE = "package"; - private static final String ATTR_NAME = "name"; - private static final String ATTR_FIXED = "fixed"; - - private static final Set<String> PHONE_PERMISSIONS = new ArraySet<>(); - static { - PHONE_PERMISSIONS.add(Manifest.permission.READ_PHONE_STATE); - PHONE_PERMISSIONS.add(Manifest.permission.CALL_PHONE); - PHONE_PERMISSIONS.add(Manifest.permission.READ_CALL_LOG); - PHONE_PERMISSIONS.add(Manifest.permission.WRITE_CALL_LOG); - PHONE_PERMISSIONS.add(Manifest.permission.ADD_VOICEMAIL); - PHONE_PERMISSIONS.add(Manifest.permission.USE_SIP); - PHONE_PERMISSIONS.add(Manifest.permission.PROCESS_OUTGOING_CALLS); - } - - private static final Set<String> CONTACTS_PERMISSIONS = new ArraySet<>(); - static { - CONTACTS_PERMISSIONS.add(Manifest.permission.READ_CONTACTS); - CONTACTS_PERMISSIONS.add(Manifest.permission.WRITE_CONTACTS); - CONTACTS_PERMISSIONS.add(Manifest.permission.GET_ACCOUNTS); - } - - private static final Set<String> LOCATION_PERMISSIONS = new ArraySet<>(); - static { - LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION); - LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION); - } - - private static final Set<String> CALENDAR_PERMISSIONS = new ArraySet<>(); - static { - CALENDAR_PERMISSIONS.add(Manifest.permission.READ_CALENDAR); - CALENDAR_PERMISSIONS.add(Manifest.permission.WRITE_CALENDAR); - } - - private static final Set<String> SMS_PERMISSIONS = new ArraySet<>(); - static { - SMS_PERMISSIONS.add(Manifest.permission.SEND_SMS); - SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_SMS); - SMS_PERMISSIONS.add(Manifest.permission.READ_SMS); - SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_WAP_PUSH); - SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_MMS); - SMS_PERMISSIONS.add(Manifest.permission.READ_CELL_BROADCASTS); - } - - private static final Set<String> MICROPHONE_PERMISSIONS = new ArraySet<>(); - static { - MICROPHONE_PERMISSIONS.add(Manifest.permission.RECORD_AUDIO); - } - - private static final Set<String> CAMERA_PERMISSIONS = new ArraySet<>(); - static { - CAMERA_PERMISSIONS.add(Manifest.permission.CAMERA); - } - - private static final Set<String> SENSORS_PERMISSIONS = new ArraySet<>(); - static { - SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS); - } - - private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>(); - static { - STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE); - STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); - } - - private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1; - - private static final String ACTION_TRACK = "com.android.fitness.TRACK"; - - private final PackageManagerService mService; - private final Handler mHandler; - - private PackagesProvider mLocationPackagesProvider; - private PackagesProvider mVoiceInteractionPackagesProvider; - private PackagesProvider mSmsAppPackagesProvider; - private PackagesProvider mDialerAppPackagesProvider; - private PackagesProvider mSimCallManagerPackagesProvider; - private SyncAdapterPackagesProvider mSyncAdapterPackagesProvider; - - private ArrayMap<String, List<DefaultPermissionGrant>> mGrantExceptions; - - public DefaultPermissionGrantPolicy(PackageManagerService service) { - mService = service; - mHandler = new Handler(mService.mHandlerThread.getLooper()) { - @Override - public void handleMessage(Message msg) { - if (msg.what == MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS) { - synchronized (mService.mPackages) { - if (mGrantExceptions == null) { - mGrantExceptions = readDefaultPermissionExceptionsLPw(); - } - } - } - } - }; - } - - public void setLocationPackagesProviderLPw(PackagesProvider provider) { - mLocationPackagesProvider = provider; - } - - public void setVoiceInteractionPackagesProviderLPw(PackagesProvider provider) { - mVoiceInteractionPackagesProvider = provider; - } - - public void setSmsAppPackagesProviderLPw(PackagesProvider provider) { - mSmsAppPackagesProvider = provider; - } - - public void setDialerAppPackagesProviderLPw(PackagesProvider provider) { - mDialerAppPackagesProvider = provider; - } - - public void setSimCallManagerPackagesProviderLPw(PackagesProvider provider) { - mSimCallManagerPackagesProvider = provider; - } - - public void setSyncAdapterPackagesProviderLPw(SyncAdapterPackagesProvider provider) { - mSyncAdapterPackagesProvider = provider; - } - - public void grantDefaultPermissions(int userId) { - if (mService.hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) { - grantAllRuntimePermissions(userId); - } else { - grantPermissionsToSysComponentsAndPrivApps(userId); - grantDefaultSystemHandlerPermissions(userId); - grantDefaultPermissionExceptions(userId); - } - } - - private void grantRuntimePermissionsForPackageLocked(int userId, PackageParser.Package pkg) { - Set<String> permissions = new ArraySet<>(); - for (String permission : pkg.requestedPermissions) { - BasePermission bp = mService.mSettings.mPermissions.get(permission); - if (bp != null && bp.isRuntime()) { - permissions.add(permission); - } - } - if (!permissions.isEmpty()) { - grantRuntimePermissionsLPw(pkg, permissions, true, userId); - } - } - - private void grantAllRuntimePermissions(int userId) { - Log.i(TAG, "Granting all runtime permissions for user " + userId); - synchronized (mService.mPackages) { - for (PackageParser.Package pkg : mService.mPackages.values()) { - grantRuntimePermissionsForPackageLocked(userId, pkg); - } - } - } - - public void scheduleReadDefaultPermissionExceptions() { - mHandler.sendEmptyMessage(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS); - } - - private void grantPermissionsToSysComponentsAndPrivApps(int userId) { - Log.i(TAG, "Granting permissions to platform components for user " + userId); - - synchronized (mService.mPackages) { - for (PackageParser.Package pkg : mService.mPackages.values()) { - if (!isSysComponentOrPersistentPlatformSignedPrivAppLPr(pkg) - || !doesPackageSupportRuntimePermissions(pkg) - || pkg.requestedPermissions.isEmpty()) { - continue; - } - grantRuntimePermissionsForPackageLocked(userId, pkg); - } - } - } - - private void grantDefaultSystemHandlerPermissions(int userId) { - Log.i(TAG, "Granting permissions to default platform handlers for user " + userId); - - final PackagesProvider locationPackagesProvider; - final PackagesProvider voiceInteractionPackagesProvider; - final PackagesProvider smsAppPackagesProvider; - final PackagesProvider dialerAppPackagesProvider; - final PackagesProvider simCallManagerPackagesProvider; - final SyncAdapterPackagesProvider syncAdapterPackagesProvider; - - synchronized (mService.mPackages) { - locationPackagesProvider = mLocationPackagesProvider; - voiceInteractionPackagesProvider = mVoiceInteractionPackagesProvider; - smsAppPackagesProvider = mSmsAppPackagesProvider; - dialerAppPackagesProvider = mDialerAppPackagesProvider; - simCallManagerPackagesProvider = mSimCallManagerPackagesProvider; - syncAdapterPackagesProvider = mSyncAdapterPackagesProvider; - } - - String[] voiceInteractPackageNames = (voiceInteractionPackagesProvider != null) - ? voiceInteractionPackagesProvider.getPackages(userId) : null; - String[] locationPackageNames = (locationPackagesProvider != null) - ? locationPackagesProvider.getPackages(userId) : null; - String[] smsAppPackageNames = (smsAppPackagesProvider != null) - ? smsAppPackagesProvider.getPackages(userId) : null; - String[] dialerAppPackageNames = (dialerAppPackagesProvider != null) - ? dialerAppPackagesProvider.getPackages(userId) : null; - String[] simCallManagerPackageNames = (simCallManagerPackagesProvider != null) - ? simCallManagerPackagesProvider.getPackages(userId) : null; - String[] contactsSyncAdapterPackages = (syncAdapterPackagesProvider != null) ? - syncAdapterPackagesProvider.getPackages(ContactsContract.AUTHORITY, userId) : null; - String[] calendarSyncAdapterPackages = (syncAdapterPackagesProvider != null) ? - syncAdapterPackagesProvider.getPackages(CalendarContract.AUTHORITY, userId) : null; - - synchronized (mService.mPackages) { - // Installer - PackageParser.Package installerPackage = getSystemPackageLPr( - mService.mRequiredInstallerPackage); - if (installerPackage != null - && doesPackageSupportRuntimePermissions(installerPackage)) { - grantRuntimePermissionsLPw(installerPackage, STORAGE_PERMISSIONS, true, userId); - } - - // Verifier - PackageParser.Package verifierPackage = getSystemPackageLPr( - mService.mRequiredVerifierPackage); - if (verifierPackage != null - && doesPackageSupportRuntimePermissions(verifierPackage)) { - grantRuntimePermissionsLPw(verifierPackage, STORAGE_PERMISSIONS, true, userId); - grantRuntimePermissionsLPw(verifierPackage, PHONE_PERMISSIONS, false, userId); - grantRuntimePermissionsLPw(verifierPackage, SMS_PERMISSIONS, false, userId); - } - - // SetupWizard - PackageParser.Package setupPackage = getSystemPackageLPr( - mService.mSetupWizardPackage); - if (setupPackage != null - && doesPackageSupportRuntimePermissions(setupPackage)) { - grantRuntimePermissionsLPw(setupPackage, PHONE_PERMISSIONS, userId); - grantRuntimePermissionsLPw(setupPackage, CONTACTS_PERMISSIONS, userId); - grantRuntimePermissionsLPw(setupPackage, LOCATION_PERMISSIONS, userId); - grantRuntimePermissionsLPw(setupPackage, CAMERA_PERMISSIONS, userId); - } - - // Camera - Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - PackageParser.Package cameraPackage = getDefaultSystemHandlerActivityPackageLPr( - cameraIntent, userId); - if (cameraPackage != null - && doesPackageSupportRuntimePermissions(cameraPackage)) { - grantRuntimePermissionsLPw(cameraPackage, CAMERA_PERMISSIONS, userId); - grantRuntimePermissionsLPw(cameraPackage, MICROPHONE_PERMISSIONS, userId); - grantRuntimePermissionsLPw(cameraPackage, STORAGE_PERMISSIONS, userId); - } - - // Media provider - PackageParser.Package mediaStorePackage = getDefaultProviderAuthorityPackageLPr( - MediaStore.AUTHORITY, userId); - if (mediaStorePackage != null) { - grantRuntimePermissionsLPw(mediaStorePackage, STORAGE_PERMISSIONS, true, userId); - } - - // Downloads provider - PackageParser.Package downloadsPackage = getDefaultProviderAuthorityPackageLPr( - "downloads", userId); - if (downloadsPackage != null) { - grantRuntimePermissionsLPw(downloadsPackage, STORAGE_PERMISSIONS, true, userId); - } - - // Downloads UI - Intent downloadsUiIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS); - PackageParser.Package downloadsUiPackage = getDefaultSystemHandlerActivityPackageLPr( - downloadsUiIntent, userId); - if (downloadsUiPackage != null - && doesPackageSupportRuntimePermissions(downloadsUiPackage)) { - grantRuntimePermissionsLPw(downloadsUiPackage, STORAGE_PERMISSIONS, true, userId); - } - - // Storage provider - PackageParser.Package storagePackage = getDefaultProviderAuthorityPackageLPr( - "com.android.externalstorage.documents", userId); - if (storagePackage != null) { - grantRuntimePermissionsLPw(storagePackage, STORAGE_PERMISSIONS, true, userId); - } - - // CertInstaller - Intent certInstallerIntent = new Intent(Credentials.INSTALL_ACTION); - PackageParser.Package certInstallerPackage = getDefaultSystemHandlerActivityPackageLPr( - certInstallerIntent, userId); - if (certInstallerPackage != null - && doesPackageSupportRuntimePermissions(certInstallerPackage)) { - grantRuntimePermissionsLPw(certInstallerPackage, STORAGE_PERMISSIONS, true, userId); - } - - // Dialer - if (dialerAppPackageNames == null) { - Intent dialerIntent = new Intent(Intent.ACTION_DIAL); - PackageParser.Package dialerPackage = getDefaultSystemHandlerActivityPackageLPr( - dialerIntent, userId); - if (dialerPackage != null) { - grantDefaultPermissionsToDefaultSystemDialerAppLPr(dialerPackage, userId); - } - } else { - for (String dialerAppPackageName : dialerAppPackageNames) { - PackageParser.Package dialerPackage = getSystemPackageLPr(dialerAppPackageName); - if (dialerPackage != null) { - grantDefaultPermissionsToDefaultSystemDialerAppLPr(dialerPackage, userId); - } - } - } - - // Sim call manager - if (simCallManagerPackageNames != null) { - for (String simCallManagerPackageName : simCallManagerPackageNames) { - PackageParser.Package simCallManagerPackage = - getSystemPackageLPr(simCallManagerPackageName); - if (simCallManagerPackage != null) { - grantDefaultPermissionsToDefaultSimCallManagerLPr(simCallManagerPackage, - userId); - } - } - } - - // SMS - if (smsAppPackageNames == null) { - Intent smsIntent = new Intent(Intent.ACTION_MAIN); - smsIntent.addCategory(Intent.CATEGORY_APP_MESSAGING); - PackageParser.Package smsPackage = getDefaultSystemHandlerActivityPackageLPr( - smsIntent, userId); - if (smsPackage != null) { - grantDefaultPermissionsToDefaultSystemSmsAppLPr(smsPackage, userId); - } - } else { - for (String smsPackageName : smsAppPackageNames) { - PackageParser.Package smsPackage = getSystemPackageLPr(smsPackageName); - if (smsPackage != null) { - grantDefaultPermissionsToDefaultSystemSmsAppLPr(smsPackage, userId); - } - } - } - - // Cell Broadcast Receiver - Intent cbrIntent = new Intent(Intents.SMS_CB_RECEIVED_ACTION); - PackageParser.Package cbrPackage = - getDefaultSystemHandlerActivityPackageLPr(cbrIntent, userId); - if (cbrPackage != null && doesPackageSupportRuntimePermissions(cbrPackage)) { - grantRuntimePermissionsLPw(cbrPackage, SMS_PERMISSIONS, userId); - } - - // Carrier Provisioning Service - Intent carrierProvIntent = new Intent(Intents.SMS_CARRIER_PROVISION_ACTION); - PackageParser.Package carrierProvPackage = - getDefaultSystemHandlerServicePackageLPr(carrierProvIntent, userId); - if (carrierProvPackage != null && doesPackageSupportRuntimePermissions(carrierProvPackage)) { - grantRuntimePermissionsLPw(carrierProvPackage, SMS_PERMISSIONS, false, userId); - } - - // Calendar - Intent calendarIntent = new Intent(Intent.ACTION_MAIN); - calendarIntent.addCategory(Intent.CATEGORY_APP_CALENDAR); - PackageParser.Package calendarPackage = getDefaultSystemHandlerActivityPackageLPr( - calendarIntent, userId); - if (calendarPackage != null - && doesPackageSupportRuntimePermissions(calendarPackage)) { - grantRuntimePermissionsLPw(calendarPackage, CALENDAR_PERMISSIONS, userId); - grantRuntimePermissionsLPw(calendarPackage, CONTACTS_PERMISSIONS, userId); - } - - // Calendar provider - PackageParser.Package calendarProviderPackage = getDefaultProviderAuthorityPackageLPr( - CalendarContract.AUTHORITY, userId); - if (calendarProviderPackage != null) { - grantRuntimePermissionsLPw(calendarProviderPackage, CONTACTS_PERMISSIONS, userId); - grantRuntimePermissionsLPw(calendarProviderPackage, CALENDAR_PERMISSIONS, - true, userId); - grantRuntimePermissionsLPw(calendarProviderPackage, STORAGE_PERMISSIONS, userId); - } - - // Calendar provider sync adapters - List<PackageParser.Package> calendarSyncAdapters = getHeadlessSyncAdapterPackagesLPr( - calendarSyncAdapterPackages, userId); - final int calendarSyncAdapterCount = calendarSyncAdapters.size(); - for (int i = 0; i < calendarSyncAdapterCount; i++) { - PackageParser.Package calendarSyncAdapter = calendarSyncAdapters.get(i); - if (doesPackageSupportRuntimePermissions(calendarSyncAdapter)) { - grantRuntimePermissionsLPw(calendarSyncAdapter, CALENDAR_PERMISSIONS, userId); - } - } - - // Contacts - Intent contactsIntent = new Intent(Intent.ACTION_MAIN); - contactsIntent.addCategory(Intent.CATEGORY_APP_CONTACTS); - PackageParser.Package contactsPackage = getDefaultSystemHandlerActivityPackageLPr( - contactsIntent, userId); - if (contactsPackage != null - && doesPackageSupportRuntimePermissions(contactsPackage)) { - grantRuntimePermissionsLPw(contactsPackage, CONTACTS_PERMISSIONS, userId); - grantRuntimePermissionsLPw(contactsPackage, PHONE_PERMISSIONS, userId); - } - - // Contacts provider sync adapters - List<PackageParser.Package> contactsSyncAdapters = getHeadlessSyncAdapterPackagesLPr( - contactsSyncAdapterPackages, userId); - final int contactsSyncAdapterCount = contactsSyncAdapters.size(); - for (int i = 0; i < contactsSyncAdapterCount; i++) { - PackageParser.Package contactsSyncAdapter = contactsSyncAdapters.get(i); - if (doesPackageSupportRuntimePermissions(contactsSyncAdapter)) { - grantRuntimePermissionsLPw(contactsSyncAdapter, CONTACTS_PERMISSIONS, userId); - } - } - - // Contacts provider - PackageParser.Package contactsProviderPackage = getDefaultProviderAuthorityPackageLPr( - ContactsContract.AUTHORITY, userId); - if (contactsProviderPackage != null) { - grantRuntimePermissionsLPw(contactsProviderPackage, CONTACTS_PERMISSIONS, - true, userId); - grantRuntimePermissionsLPw(contactsProviderPackage, PHONE_PERMISSIONS, - true, userId); - grantRuntimePermissionsLPw(contactsProviderPackage, STORAGE_PERMISSIONS, userId); - } - - // Device provisioning - Intent deviceProvisionIntent = new Intent( - DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE); - PackageParser.Package deviceProvisionPackage = - getDefaultSystemHandlerActivityPackageLPr(deviceProvisionIntent, userId); - if (deviceProvisionPackage != null - && doesPackageSupportRuntimePermissions(deviceProvisionPackage)) { - grantRuntimePermissionsLPw(deviceProvisionPackage, CONTACTS_PERMISSIONS, userId); - } - - // Maps - Intent mapsIntent = new Intent(Intent.ACTION_MAIN); - mapsIntent.addCategory(Intent.CATEGORY_APP_MAPS); - PackageParser.Package mapsPackage = getDefaultSystemHandlerActivityPackageLPr( - mapsIntent, userId); - if (mapsPackage != null - && doesPackageSupportRuntimePermissions(mapsPackage)) { - grantRuntimePermissionsLPw(mapsPackage, LOCATION_PERMISSIONS, userId); - } - - // Gallery - Intent galleryIntent = new Intent(Intent.ACTION_MAIN); - galleryIntent.addCategory(Intent.CATEGORY_APP_GALLERY); - PackageParser.Package galleryPackage = getDefaultSystemHandlerActivityPackageLPr( - galleryIntent, userId); - if (galleryPackage != null - && doesPackageSupportRuntimePermissions(galleryPackage)) { - grantRuntimePermissionsLPw(galleryPackage, STORAGE_PERMISSIONS, userId); - } - - // Email - Intent emailIntent = new Intent(Intent.ACTION_MAIN); - emailIntent.addCategory(Intent.CATEGORY_APP_EMAIL); - PackageParser.Package emailPackage = getDefaultSystemHandlerActivityPackageLPr( - emailIntent, userId); - if (emailPackage != null - && doesPackageSupportRuntimePermissions(emailPackage)) { - grantRuntimePermissionsLPw(emailPackage, CONTACTS_PERMISSIONS, userId); - grantRuntimePermissionsLPw(emailPackage, CALENDAR_PERMISSIONS, userId); - } - - // Browser - PackageParser.Package browserPackage = null; - String defaultBrowserPackage = mService.getDefaultBrowserPackageName(userId); - if (defaultBrowserPackage != null) { - browserPackage = getPackageLPr(defaultBrowserPackage); - } - if (browserPackage == null) { - Intent browserIntent = new Intent(Intent.ACTION_MAIN); - browserIntent.addCategory(Intent.CATEGORY_APP_BROWSER); - browserPackage = getDefaultSystemHandlerActivityPackageLPr( - browserIntent, userId); - } - if (browserPackage != null - && doesPackageSupportRuntimePermissions(browserPackage)) { - grantRuntimePermissionsLPw(browserPackage, LOCATION_PERMISSIONS, userId); - } - - // Voice interaction - if (voiceInteractPackageNames != null) { - for (String voiceInteractPackageName : voiceInteractPackageNames) { - PackageParser.Package voiceInteractPackage = getSystemPackageLPr( - voiceInteractPackageName); - if (voiceInteractPackage != null - && doesPackageSupportRuntimePermissions(voiceInteractPackage)) { - grantRuntimePermissionsLPw(voiceInteractPackage, - CONTACTS_PERMISSIONS, userId); - grantRuntimePermissionsLPw(voiceInteractPackage, - CALENDAR_PERMISSIONS, userId); - grantRuntimePermissionsLPw(voiceInteractPackage, - MICROPHONE_PERMISSIONS, userId); - grantRuntimePermissionsLPw(voiceInteractPackage, - PHONE_PERMISSIONS, userId); - grantRuntimePermissionsLPw(voiceInteractPackage, - SMS_PERMISSIONS, userId); - grantRuntimePermissionsLPw(voiceInteractPackage, - LOCATION_PERMISSIONS, userId); - } - } - } - - if (ActivityManager.isLowRamDeviceStatic()) { - // Allow voice search on low-ram devices - Intent globalSearchIntent = new Intent("android.search.action.GLOBAL_SEARCH"); - PackageParser.Package globalSearchPickerPackage = - getDefaultSystemHandlerActivityPackageLPr(globalSearchIntent, userId); - - if (globalSearchPickerPackage != null - && doesPackageSupportRuntimePermissions(globalSearchPickerPackage)) { - grantRuntimePermissionsLPw(globalSearchPickerPackage, - MICROPHONE_PERMISSIONS, true, userId); - grantRuntimePermissionsLPw(globalSearchPickerPackage, - LOCATION_PERMISSIONS, true, userId); - } - } - - // Voice recognition - Intent voiceRecoIntent = new Intent("android.speech.RecognitionService"); - voiceRecoIntent.addCategory(Intent.CATEGORY_DEFAULT); - PackageParser.Package voiceRecoPackage = getDefaultSystemHandlerServicePackageLPr( - voiceRecoIntent, userId); - if (voiceRecoPackage != null - && doesPackageSupportRuntimePermissions(voiceRecoPackage)) { - grantRuntimePermissionsLPw(voiceRecoPackage, MICROPHONE_PERMISSIONS, userId); - } - - // Location - if (locationPackageNames != null) { - for (String packageName : locationPackageNames) { - PackageParser.Package locationPackage = getSystemPackageLPr(packageName); - if (locationPackage != null - && doesPackageSupportRuntimePermissions(locationPackage)) { - grantRuntimePermissionsLPw(locationPackage, CONTACTS_PERMISSIONS, userId); - grantRuntimePermissionsLPw(locationPackage, CALENDAR_PERMISSIONS, userId); - grantRuntimePermissionsLPw(locationPackage, MICROPHONE_PERMISSIONS, userId); - grantRuntimePermissionsLPw(locationPackage, PHONE_PERMISSIONS, userId); - grantRuntimePermissionsLPw(locationPackage, SMS_PERMISSIONS, userId); - grantRuntimePermissionsLPw(locationPackage, LOCATION_PERMISSIONS, - true, userId); - grantRuntimePermissionsLPw(locationPackage, CAMERA_PERMISSIONS, userId); - grantRuntimePermissionsLPw(locationPackage, SENSORS_PERMISSIONS, userId); - grantRuntimePermissionsLPw(locationPackage, STORAGE_PERMISSIONS, userId); - } - } - } - - // Music - Intent musicIntent = new Intent(Intent.ACTION_VIEW); - musicIntent.addCategory(Intent.CATEGORY_DEFAULT); - musicIntent.setDataAndType(Uri.fromFile(new File("foo.mp3")), - AUDIO_MIME_TYPE); - PackageParser.Package musicPackage = getDefaultSystemHandlerActivityPackageLPr( - musicIntent, userId); - if (musicPackage != null - && doesPackageSupportRuntimePermissions(musicPackage)) { - grantRuntimePermissionsLPw(musicPackage, STORAGE_PERMISSIONS, userId); - } - - // Home - Intent homeIntent = new Intent(Intent.ACTION_MAIN); - homeIntent.addCategory(Intent.CATEGORY_HOME); - homeIntent.addCategory(Intent.CATEGORY_LAUNCHER_APP); - PackageParser.Package homePackage = getDefaultSystemHandlerActivityPackageLPr( - homeIntent, userId); - if (homePackage != null - && doesPackageSupportRuntimePermissions(homePackage)) { - grantRuntimePermissionsLPw(homePackage, LOCATION_PERMISSIONS, false, userId); - } - - // Watches - if (mService.hasSystemFeature(PackageManager.FEATURE_WATCH, 0)) { - // Home application on watches - Intent wearHomeIntent = new Intent(Intent.ACTION_MAIN); - wearHomeIntent.addCategory(Intent.CATEGORY_HOME_MAIN); - - PackageParser.Package wearHomePackage = getDefaultSystemHandlerActivityPackageLPr( - wearHomeIntent, userId); - - if (wearHomePackage != null - && doesPackageSupportRuntimePermissions(wearHomePackage)) { - grantRuntimePermissionsLPw(wearHomePackage, CONTACTS_PERMISSIONS, false, - userId); - grantRuntimePermissionsLPw(wearHomePackage, PHONE_PERMISSIONS, true, userId); - grantRuntimePermissionsLPw(wearHomePackage, MICROPHONE_PERMISSIONS, false, - userId); - grantRuntimePermissionsLPw(wearHomePackage, LOCATION_PERMISSIONS, false, - userId); - } - - // Fitness tracking on watches - Intent trackIntent = new Intent(ACTION_TRACK); - PackageParser.Package trackPackage = getDefaultSystemHandlerActivityPackageLPr( - trackIntent, userId); - if (trackPackage != null - && doesPackageSupportRuntimePermissions(trackPackage)) { - grantRuntimePermissionsLPw(trackPackage, SENSORS_PERMISSIONS, false, userId); - grantRuntimePermissionsLPw(trackPackage, LOCATION_PERMISSIONS, false, userId); - } - } - - // Print Spooler - PackageParser.Package printSpoolerPackage = getSystemPackageLPr( - PrintManager.PRINT_SPOOLER_PACKAGE_NAME); - if (printSpoolerPackage != null - && doesPackageSupportRuntimePermissions(printSpoolerPackage)) { - grantRuntimePermissionsLPw(printSpoolerPackage, LOCATION_PERMISSIONS, true, userId); - } - - // EmergencyInfo - Intent emergencyInfoIntent = new Intent(TelephonyManager.ACTION_EMERGENCY_ASSISTANCE); - PackageParser.Package emergencyInfoPckg = getDefaultSystemHandlerActivityPackageLPr( - emergencyInfoIntent, userId); - if (emergencyInfoPckg != null - && doesPackageSupportRuntimePermissions(emergencyInfoPckg)) { - grantRuntimePermissionsLPw(emergencyInfoPckg, CONTACTS_PERMISSIONS, true, userId); - grantRuntimePermissionsLPw(emergencyInfoPckg, PHONE_PERMISSIONS, true, userId); - } - - // NFC Tag viewer - Intent nfcTagIntent = new Intent(Intent.ACTION_VIEW); - nfcTagIntent.setType("vnd.android.cursor.item/ndef_msg"); - PackageParser.Package nfcTagPkg = getDefaultSystemHandlerActivityPackageLPr( - nfcTagIntent, userId); - if (nfcTagPkg != null - && doesPackageSupportRuntimePermissions(nfcTagPkg)) { - grantRuntimePermissionsLPw(nfcTagPkg, CONTACTS_PERMISSIONS, false, userId); - grantRuntimePermissionsLPw(nfcTagPkg, PHONE_PERMISSIONS, false, userId); - } - - // Storage Manager - Intent storageManagerIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE); - PackageParser.Package storageManagerPckg = getDefaultSystemHandlerActivityPackageLPr( - storageManagerIntent, userId); - if (storageManagerPckg != null - && doesPackageSupportRuntimePermissions(storageManagerPckg)) { - grantRuntimePermissionsLPw(storageManagerPckg, STORAGE_PERMISSIONS, true, userId); - } - - // Companion devices - PackageParser.Package companionDeviceDiscoveryPackage = getSystemPackageLPr( - CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME); - if (companionDeviceDiscoveryPackage != null - && doesPackageSupportRuntimePermissions(companionDeviceDiscoveryPackage)) { - grantRuntimePermissionsLPw(companionDeviceDiscoveryPackage, - LOCATION_PERMISSIONS, true, userId); - } - - // Ringtone Picker - Intent ringtonePickerIntent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER); - PackageParser.Package ringtonePickerPackage = - getDefaultSystemHandlerActivityPackageLPr(ringtonePickerIntent, userId); - if (ringtonePickerPackage != null - && doesPackageSupportRuntimePermissions(ringtonePickerPackage)) { - grantRuntimePermissionsLPw(ringtonePickerPackage, - STORAGE_PERMISSIONS, true, userId); - } - - mService.mSettings.onDefaultRuntimePermissionsGrantedLPr(userId); - } - } - - private void grantDefaultPermissionsToDefaultSystemDialerAppLPr( - PackageParser.Package dialerPackage, int userId) { - if (doesPackageSupportRuntimePermissions(dialerPackage)) { - boolean isPhonePermFixed = - mService.hasSystemFeature(PackageManager.FEATURE_WATCH, 0); - grantRuntimePermissionsLPw( - dialerPackage, PHONE_PERMISSIONS, isPhonePermFixed, userId); - grantRuntimePermissionsLPw(dialerPackage, CONTACTS_PERMISSIONS, userId); - grantRuntimePermissionsLPw(dialerPackage, SMS_PERMISSIONS, userId); - grantRuntimePermissionsLPw(dialerPackage, MICROPHONE_PERMISSIONS, userId); - grantRuntimePermissionsLPw(dialerPackage, CAMERA_PERMISSIONS, userId); - } - } - - private void grantDefaultPermissionsToDefaultSystemSmsAppLPr( - PackageParser.Package smsPackage, int userId) { - if (doesPackageSupportRuntimePermissions(smsPackage)) { - grantRuntimePermissionsLPw(smsPackage, PHONE_PERMISSIONS, userId); - grantRuntimePermissionsLPw(smsPackage, CONTACTS_PERMISSIONS, userId); - grantRuntimePermissionsLPw(smsPackage, SMS_PERMISSIONS, userId); - grantRuntimePermissionsLPw(smsPackage, STORAGE_PERMISSIONS, userId); - grantRuntimePermissionsLPw(smsPackage, MICROPHONE_PERMISSIONS, userId); - grantRuntimePermissionsLPw(smsPackage, CAMERA_PERMISSIONS, userId); - } - } - - public void grantDefaultPermissionsToDefaultSmsAppLPr(String packageName, int userId) { - Log.i(TAG, "Granting permissions to default sms app for user:" + userId); - if (packageName == null) { - return; - } - PackageParser.Package smsPackage = getPackageLPr(packageName); - if (smsPackage != null && doesPackageSupportRuntimePermissions(smsPackage)) { - grantRuntimePermissionsLPw(smsPackage, PHONE_PERMISSIONS, false, true, userId); - grantRuntimePermissionsLPw(smsPackage, CONTACTS_PERMISSIONS, false, true, userId); - grantRuntimePermissionsLPw(smsPackage, SMS_PERMISSIONS, false, true, userId); - grantRuntimePermissionsLPw(smsPackage, STORAGE_PERMISSIONS, false, true, userId); - grantRuntimePermissionsLPw(smsPackage, MICROPHONE_PERMISSIONS, false, true, userId); - grantRuntimePermissionsLPw(smsPackage, CAMERA_PERMISSIONS, false, true, userId); - } - } - - public void grantDefaultPermissionsToDefaultDialerAppLPr(String packageName, int userId) { - Log.i(TAG, "Granting permissions to default dialer app for user:" + userId); - if (packageName == null) { - return; - } - PackageParser.Package dialerPackage = getPackageLPr(packageName); - if (dialerPackage != null - && doesPackageSupportRuntimePermissions(dialerPackage)) { - grantRuntimePermissionsLPw(dialerPackage, PHONE_PERMISSIONS, false, true, userId); - grantRuntimePermissionsLPw(dialerPackage, CONTACTS_PERMISSIONS, false, true, userId); - grantRuntimePermissionsLPw(dialerPackage, SMS_PERMISSIONS, false, true, userId); - grantRuntimePermissionsLPw(dialerPackage, MICROPHONE_PERMISSIONS, false, true, userId); - grantRuntimePermissionsLPw(dialerPackage, CAMERA_PERMISSIONS, false, true, userId); - } - } - - private void grantDefaultPermissionsToDefaultSimCallManagerLPr( - PackageParser.Package simCallManagerPackage, int userId) { - Log.i(TAG, "Granting permissions to sim call manager for user:" + userId); - if (doesPackageSupportRuntimePermissions(simCallManagerPackage)) { - grantRuntimePermissionsLPw(simCallManagerPackage, PHONE_PERMISSIONS, userId); - grantRuntimePermissionsLPw(simCallManagerPackage, MICROPHONE_PERMISSIONS, userId); - } - } - - public void grantDefaultPermissionsToDefaultSimCallManagerLPr(String packageName, int userId) { - if (packageName == null) { - return; - } - PackageParser.Package simCallManagerPackage = getPackageLPr(packageName); - if (simCallManagerPackage != null) { - grantDefaultPermissionsToDefaultSimCallManagerLPr(simCallManagerPackage, userId); - } - } - - public void grantDefaultPermissionsToEnabledCarrierAppsLPr(String[] packageNames, int userId) { - Log.i(TAG, "Granting permissions to enabled carrier apps for user:" + userId); - if (packageNames == null) { - return; - } - for (String packageName : packageNames) { - PackageParser.Package carrierPackage = getSystemPackageLPr(packageName); - if (carrierPackage != null - && doesPackageSupportRuntimePermissions(carrierPackage)) { - grantRuntimePermissionsLPw(carrierPackage, PHONE_PERMISSIONS, userId); - grantRuntimePermissionsLPw(carrierPackage, LOCATION_PERMISSIONS, userId); - grantRuntimePermissionsLPw(carrierPackage, SMS_PERMISSIONS, userId); - } - } - } - - public void grantDefaultPermissionsToEnabledImsServicesLPr(String[] packageNames, int userId) { - Log.i(TAG, "Granting permissions to enabled ImsServices for user:" + userId); - if (packageNames == null) { - return; - } - for (String packageName : packageNames) { - PackageParser.Package imsServicePackage = getSystemPackageLPr(packageName); - if (imsServicePackage != null - && doesPackageSupportRuntimePermissions(imsServicePackage)) { - grantRuntimePermissionsLPw(imsServicePackage, PHONE_PERMISSIONS, userId); - grantRuntimePermissionsLPw(imsServicePackage, MICROPHONE_PERMISSIONS, userId); - grantRuntimePermissionsLPw(imsServicePackage, LOCATION_PERMISSIONS, userId); - grantRuntimePermissionsLPw(imsServicePackage, CAMERA_PERMISSIONS, userId); - } - } - } - - public void grantDefaultPermissionsToDefaultBrowserLPr(String packageName, int userId) { - Log.i(TAG, "Granting permissions to default browser for user:" + userId); - if (packageName == null) { - return; - } - PackageParser.Package browserPackage = getSystemPackageLPr(packageName); - if (browserPackage != null - && doesPackageSupportRuntimePermissions(browserPackage)) { - grantRuntimePermissionsLPw(browserPackage, LOCATION_PERMISSIONS, false, false, userId); - } - } - - private PackageParser.Package getDefaultSystemHandlerActivityPackageLPr( - Intent intent, int userId) { - ResolveInfo handler = mService.resolveIntent(intent, - intent.resolveType(mService.mContext.getContentResolver()), DEFAULT_FLAGS, userId); - if (handler == null || handler.activityInfo == null) { - return null; - } - ActivityInfo activityInfo = handler.activityInfo; - if (activityInfo.packageName.equals(mService.mResolveActivity.packageName) - && activityInfo.name.equals(mService.mResolveActivity.name)) { - return null; - } - return getSystemPackageLPr(handler.activityInfo.packageName); - } - - private PackageParser.Package getDefaultSystemHandlerServicePackageLPr( - Intent intent, int userId) { - List<ResolveInfo> handlers = mService.queryIntentServices(intent, - intent.resolveType(mService.mContext.getContentResolver()), DEFAULT_FLAGS, userId) - .getList(); - if (handlers == null) { - return null; - } - final int handlerCount = handlers.size(); - for (int i = 0; i < handlerCount; i++) { - ResolveInfo handler = handlers.get(i); - PackageParser.Package handlerPackage = getSystemPackageLPr( - handler.serviceInfo.packageName); - if (handlerPackage != null) { - return handlerPackage; - } - } - return null; - } - - private List<PackageParser.Package> getHeadlessSyncAdapterPackagesLPr( - String[] syncAdapterPackageNames, int userId) { - List<PackageParser.Package> syncAdapterPackages = new ArrayList<>(); - - Intent homeIntent = new Intent(Intent.ACTION_MAIN); - homeIntent.addCategory(Intent.CATEGORY_LAUNCHER); - - for (String syncAdapterPackageName : syncAdapterPackageNames) { - homeIntent.setPackage(syncAdapterPackageName); - - ResolveInfo homeActivity = mService.resolveIntent(homeIntent, - homeIntent.resolveType(mService.mContext.getContentResolver()), DEFAULT_FLAGS, - userId); - if (homeActivity != null) { - continue; - } - - PackageParser.Package syncAdapterPackage = getSystemPackageLPr(syncAdapterPackageName); - if (syncAdapterPackage != null) { - syncAdapterPackages.add(syncAdapterPackage); - } - } - - return syncAdapterPackages; - } - - private PackageParser.Package getDefaultProviderAuthorityPackageLPr( - String authority, int userId) { - ProviderInfo provider = mService.resolveContentProvider(authority, DEFAULT_FLAGS, userId); - if (provider != null) { - return getSystemPackageLPr(provider.packageName); - } - return null; - } - - private PackageParser.Package getPackageLPr(String packageName) { - return mService.mPackages.get(packageName); - } - - private PackageParser.Package getSystemPackageLPr(String packageName) { - PackageParser.Package pkg = getPackageLPr(packageName); - if (pkg != null && pkg.isSystemApp()) { - return !isSysComponentOrPersistentPlatformSignedPrivAppLPr(pkg) ? pkg : null; - } - return null; - } - - private void grantRuntimePermissionsLPw(PackageParser.Package pkg, Set<String> permissions, - int userId) { - grantRuntimePermissionsLPw(pkg, permissions, false, false, userId); - } - - private void grantRuntimePermissionsLPw(PackageParser.Package pkg, Set<String> permissions, - boolean systemFixed, int userId) { - grantRuntimePermissionsLPw(pkg, permissions, systemFixed, false, userId); - } - - private void grantRuntimePermissionsLPw(PackageParser.Package pkg, Set<String> permissions, - boolean systemFixed, boolean isDefaultPhoneOrSms, int userId) { - if (pkg.requestedPermissions.isEmpty()) { - return; - } - - List<String> requestedPermissions = pkg.requestedPermissions; - Set<String> grantablePermissions = null; - - // If this is the default Phone or SMS app we grant permissions regardless - // whether the version on the system image declares the permission as used since - // selecting the app as the default Phone or SMS the user makes a deliberate - // choice to grant this app the permissions needed to function. For all other - // apps, (default grants on first boot and user creation) we don't grant default - // permissions if the version on the system image does not declare them. - if (!isDefaultPhoneOrSms && pkg.isUpdatedSystemApp()) { - PackageSetting sysPs = mService.mSettings.getDisabledSystemPkgLPr(pkg.packageName); - if (sysPs != null && sysPs.pkg != null) { - if (sysPs.pkg.requestedPermissions.isEmpty()) { - return; - } - if (!requestedPermissions.equals(sysPs.pkg.requestedPermissions)) { - grantablePermissions = new ArraySet<>(requestedPermissions); - requestedPermissions = sysPs.pkg.requestedPermissions; - } - } - } - - final int grantablePermissionCount = requestedPermissions.size(); - for (int i = 0; i < grantablePermissionCount; i++) { - String permission = requestedPermissions.get(i); - - // If there is a disabled system app it may request a permission the updated - // version ot the data partition doesn't, In this case skip the permission. - if (grantablePermissions != null && !grantablePermissions.contains(permission)) { - continue; - } - - if (permissions.contains(permission)) { - final int flags = mService.getPermissionFlags(permission, pkg.packageName, userId); - - // If any flags are set to the permission, then it is either set in - // its current state by the system or device/profile owner or the user. - // In all these cases we do not want to clobber the current state. - // Unless the caller wants to override user choices. The override is - // to make sure we can grant the needed permission to the default - // sms and phone apps after the user chooses this in the UI. - if (flags == 0 || isDefaultPhoneOrSms) { - // Never clobber policy or system. - final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED - | PackageManager.FLAG_PERMISSION_POLICY_FIXED; - if ((flags & fixedFlags) != 0) { - continue; - } - - mService.grantRuntimePermission(pkg.packageName, permission, userId); - if (DEBUG) { - Log.i(TAG, "Granted " + (systemFixed ? "fixed " : "not fixed ") - + permission + " to default handler " + pkg.packageName); - } - - int newFlags = PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; - if (systemFixed) { - newFlags |= PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; - } - - mService.updatePermissionFlags(permission, pkg.packageName, - newFlags, newFlags, userId); - } - - // If a component gets a permission for being the default handler A - // and also default handler B, we grant the weaker grant form. - if ((flags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0 - && (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0 - && !systemFixed) { - if (DEBUG) { - Log.i(TAG, "Granted not fixed " + permission + " to default handler " - + pkg.packageName); - } - mService.updatePermissionFlags(permission, pkg.packageName, - PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, userId); - } - } - } - } - - private boolean isSysComponentOrPersistentPlatformSignedPrivAppLPr(PackageParser.Package pkg) { - if (UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) { - return true; - } - if (!pkg.isPrivilegedApp()) { - return false; - } - PackageSetting sysPkg = mService.mSettings.getDisabledSystemPkgLPr(pkg.packageName); - if (sysPkg != null && sysPkg.pkg != null) { - if ((sysPkg.pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) { - return false; - } - } else if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) { - return false; - } - return PackageManagerService.compareSignatures(mService.mPlatformPackage.mSignatures, - pkg.mSignatures) == PackageManager.SIGNATURE_MATCH; - } - - private void grantDefaultPermissionExceptions(int userId) { - synchronized (mService.mPackages) { - mHandler.removeMessages(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS); - - if (mGrantExceptions == null) { - mGrantExceptions = readDefaultPermissionExceptionsLPw(); - } - - // mGrantExceptions is null only before the first read and then - // it serves as a cache of the default grants that should be - // performed for every user. If there is an entry then the app - // is on the system image and supports runtime permissions. - Set<String> permissions = null; - final int exceptionCount = mGrantExceptions.size(); - for (int i = 0; i < exceptionCount; i++) { - String packageName = mGrantExceptions.keyAt(i); - PackageParser.Package pkg = getSystemPackageLPr(packageName); - List<DefaultPermissionGrant> permissionGrants = mGrantExceptions.valueAt(i); - final int permissionGrantCount = permissionGrants.size(); - for (int j = 0; j < permissionGrantCount; j++) { - DefaultPermissionGrant permissionGrant = permissionGrants.get(j); - if (permissions == null) { - permissions = new ArraySet<>(); - } else { - permissions.clear(); - } - permissions.add(permissionGrant.name); - grantRuntimePermissionsLPw(pkg, permissions, - permissionGrant.fixed, userId); - } - } - } - } - - private File[] getDefaultPermissionFiles() { - ArrayList<File> ret = new ArrayList<File>(); - File dir = new File(Environment.getRootDirectory(), "etc/default-permissions"); - if (dir.isDirectory() && dir.canRead()) { - Collections.addAll(ret, dir.listFiles()); - } - dir = new File(Environment.getVendorDirectory(), "etc/default-permissions"); - if (dir.isDirectory() && dir.canRead()) { - Collections.addAll(ret, dir.listFiles()); - } - return ret.isEmpty() ? null : ret.toArray(new File[0]); - } - - private @NonNull ArrayMap<String, List<DefaultPermissionGrant>> - readDefaultPermissionExceptionsLPw() { - File[] files = getDefaultPermissionFiles(); - if (files == null) { - return new ArrayMap<>(0); - } - - ArrayMap<String, List<DefaultPermissionGrant>> grantExceptions = new ArrayMap<>(); - - // Iterate over the files in the directory and scan .xml files - for (File file : files) { - if (!file.getPath().endsWith(".xml")) { - Slog.i(TAG, "Non-xml file " + file + " in " + file.getParent() + " directory, ignoring"); - continue; - } - if (!file.canRead()) { - Slog.w(TAG, "Default permissions file " + file + " cannot be read"); - continue; - } - try ( - InputStream str = new BufferedInputStream(new FileInputStream(file)) - ) { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(str, null); - parse(parser, grantExceptions); - } catch (XmlPullParserException | IOException e) { - Slog.w(TAG, "Error reading default permissions file " + file, e); - } - } - - return grantExceptions; - } - - private void parse(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>> - outGrantExceptions) throws IOException, XmlPullParserException { - final int outerDepth = parser.getDepth(); - int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue; - } - if (TAG_EXCEPTIONS.equals(parser.getName())) { - parseExceptions(parser, outGrantExceptions); - } else { - Log.e(TAG, "Unknown tag " + parser.getName()); - } - } - } - - private void parseExceptions(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>> - outGrantExceptions) throws IOException, XmlPullParserException { - final int outerDepth = parser.getDepth(); - int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue; - } - if (TAG_EXCEPTION.equals(parser.getName())) { - String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); - - List<DefaultPermissionGrant> packageExceptions = - outGrantExceptions.get(packageName); - if (packageExceptions == null) { - // The package must be on the system image - PackageParser.Package pkg = getSystemPackageLPr(packageName); - if (pkg == null) { - Log.w(TAG, "Unknown package:" + packageName); - XmlUtils.skipCurrentTag(parser); - continue; - } - - // The package must support runtime permissions - if (!doesPackageSupportRuntimePermissions(pkg)) { - Log.w(TAG, "Skipping non supporting runtime permissions package:" - + packageName); - XmlUtils.skipCurrentTag(parser); - continue; - } - packageExceptions = new ArrayList<>(); - outGrantExceptions.put(packageName, packageExceptions); - } - - parsePermission(parser, packageExceptions); - } else { - Log.e(TAG, "Unknown tag " + parser.getName() + "under <exceptions>"); - } - } - } - - private void parsePermission(XmlPullParser parser, List<DefaultPermissionGrant> - outPackageExceptions) throws IOException, XmlPullParserException { - final int outerDepth = parser.getDepth(); - int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue; - } - - if (TAG_PERMISSION.contains(parser.getName())) { - String name = parser.getAttributeValue(null, ATTR_NAME); - if (name == null) { - Log.w(TAG, "Mandatory name attribute missing for permission tag"); - XmlUtils.skipCurrentTag(parser); - continue; - } - - final boolean fixed = XmlUtils.readBooleanAttribute(parser, ATTR_FIXED); - - DefaultPermissionGrant exception = new DefaultPermissionGrant(name, fixed); - outPackageExceptions.add(exception); - } else { - Log.e(TAG, "Unknown tag " + parser.getName() + "under <exception>"); - } - } - } - - private static boolean doesPackageSupportRuntimePermissions(PackageParser.Package pkg) { - return pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1; - } - - private static final class DefaultPermissionGrant { - final String name; - final boolean fixed; - - public DefaultPermissionGrant(String name, boolean fixed) { - this.name = name; - this.fixed = fixed; - } - } -} diff --git a/com/android/server/pm/DumpState.java b/com/android/server/pm/DumpState.java new file mode 100644 index 00000000..7ebef83a --- /dev/null +++ b/com/android/server/pm/DumpState.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2017 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.pm; + +public final class DumpState { + public static final int DUMP_LIBS = 1 << 0; + public static final int DUMP_FEATURES = 1 << 1; + public static final int DUMP_ACTIVITY_RESOLVERS = 1 << 2; + public static final int DUMP_SERVICE_RESOLVERS = 1 << 3; + public static final int DUMP_RECEIVER_RESOLVERS = 1 << 4; + public static final int DUMP_CONTENT_RESOLVERS = 1 << 5; + public static final int DUMP_PERMISSIONS = 1 << 6; + public static final int DUMP_PACKAGES = 1 << 7; + public static final int DUMP_SHARED_USERS = 1 << 8; + public static final int DUMP_MESSAGES = 1 << 9; + public static final int DUMP_PROVIDERS = 1 << 10; + public static final int DUMP_VERIFIERS = 1 << 11; + public static final int DUMP_PREFERRED = 1 << 12; + public static final int DUMP_PREFERRED_XML = 1 << 13; + public static final int DUMP_KEYSETS = 1 << 14; + public static final int DUMP_VERSION = 1 << 15; + public static final int DUMP_INSTALLS = 1 << 16; + public static final int DUMP_INTENT_FILTER_VERIFIERS = 1 << 17; + public static final int DUMP_DOMAIN_PREFERRED = 1 << 18; + public static final int DUMP_FROZEN = 1 << 19; + public static final int DUMP_DEXOPT = 1 << 20; + public static final int DUMP_COMPILER_STATS = 1 << 21; + public static final int DUMP_CHANGES = 1 << 22; + public static final int DUMP_VOLUMES = 1 << 23; + + public static final int OPTION_SHOW_FILTERS = 1 << 0; + + private int mTypes; + + private int mOptions; + + private boolean mTitlePrinted; + + private SharedUserSetting mSharedUser; + + public boolean isDumping(int type) { + if (mTypes == 0 && type != DUMP_PREFERRED_XML) { + return true; + } + + return (mTypes & type) != 0; + } + + public void setDump(int type) { + mTypes |= type; + } + + public boolean isOptionEnabled(int option) { + return (mOptions & option) != 0; + } + + public void setOptionEnabled(int option) { + mOptions |= option; + } + + public boolean onTitlePrinted() { + final boolean printed = mTitlePrinted; + mTitlePrinted = true; + return printed; + } + + public boolean getTitlePrinted() { + return mTitlePrinted; + } + + public void setTitlePrinted(boolean enabled) { + mTitlePrinted = enabled; + } + + public SharedUserSetting getSharedUser() { + return mSharedUser; + } + + public void setSharedUser(SharedUserSetting user) { + mSharedUser = user; + } +}
\ No newline at end of file diff --git a/com/android/server/pm/InstantAppRegistry.java b/com/android/server/pm/InstantAppRegistry.java index e1e5b355..c964f912 100644 --- a/com/android/server/pm/InstantAppRegistry.java +++ b/com/android/server/pm/InstantAppRegistry.java @@ -49,6 +49,8 @@ import com.android.internal.os.BackgroundThread; import com.android.internal.os.SomeArgs; import com.android.internal.util.ArrayUtils; import com.android.internal.util.XmlUtils; +import com.android.server.pm.permission.BasePermission; + import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -878,8 +880,9 @@ class InstantAppRegistry { final long identity = Binder.clearCallingIdentity(); try { for (String grantedPermission : appInfo.getGrantedPermissions()) { - BasePermission bp = mService.mSettings.mPermissions.get(grantedPermission); - if (bp != null && (bp.isRuntime() || bp.isDevelopment()) && bp.isInstant()) { + final boolean propagatePermission = + mService.mSettings.canPropagatePermissionToInstantApp(grantedPermission); + if (propagatePermission) { mService.grantRuntimePermission(packageName, grantedPermission, userId); } } diff --git a/com/android/server/pm/KeySetManagerService.java b/com/android/server/pm/KeySetManagerService.java index 49d3c8bc..35744662 100644 --- a/com/android/server/pm/KeySetManagerService.java +++ b/com/android/server/pm/KeySetManagerService.java @@ -565,7 +565,7 @@ public class KeySetManagerService { } public void dumpLPr(PrintWriter pw, String packageName, - PackageManagerService.DumpState dumpState) { + DumpState dumpState) { boolean printedHeader = false; for (ArrayMap.Entry<String, PackageSetting> e : mPackages.entrySet()) { String keySetPackage = e.getKey(); diff --git a/com/android/server/pm/PackageDexOptimizer.java b/com/android/server/pm/PackageDexOptimizer.java index 0f580d81..8ebeeae2 100644 --- a/com/android/server/pm/PackageDexOptimizer.java +++ b/com/android/server/pm/PackageDexOptimizer.java @@ -23,6 +23,7 @@ import android.content.pm.PackageParser; import android.os.FileUtils; import android.os.PowerManager; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.WorkSource; import android.util.Log; @@ -103,7 +104,17 @@ public class PackageDexOptimizer { } static boolean canOptimizePackage(PackageParser.Package pkg) { - return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0; + // We do not dexopt a package with no code. + if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) == 0) { + return false; + } + + // We do not dexopt a priv-app package when pm.dexopt.priv-apps is false. + if (pkg.isPrivilegedApp()) { + return SystemProperties.getBoolean("pm.dexopt.priv-apps", true); + } + + return true; } /** @@ -354,18 +365,13 @@ public class PackageDexOptimizer { + " dexoptFlags=" + printDexoptFlags(dexoptFlags) + " target-filter=" + compilerFilter); - String classLoaderContext; - if (dexUseInfo.isUnknownClassLoaderContext() || - dexUseInfo.isUnsupportedClassLoaderContext() || - dexUseInfo.isVariableClassLoaderContext()) { - // If we have an unknown (not yet set), unsupported (custom class loaders), or a - // variable class loader chain, compile without a context and mark the oat file with - // SKIP_SHARED_LIBRARY_CHECK. Note that his might lead to a incorrect compilation. - // TODO(calin): We should just extract in this case. - classLoaderContext = SKIP_SHARED_LIBRARY_CHECK; - } else { - classLoaderContext = dexUseInfo.getClassLoaderContext(); - } + // TODO(calin): b/64530081 b/66984396. Use SKIP_SHARED_LIBRARY_CHECK for the context + // (instead of dexUseInfo.getClassLoaderContext()) in order to compile secondary dex files + // in isolation (and avoid to extract/verify the main apk if it's in the class path). + // Note this trades correctness for performance since the resulting slow down is + // unacceptable in some cases until b/64530081 is fixed. + String classLoaderContext = SKIP_SHARED_LIBRARY_CHECK; + try { for (String isa : dexUseInfo.getLoaderIsas()) { // Reuse the same dexopt path as for the primary apks. We don't need all the @@ -425,7 +431,7 @@ public class PackageDexOptimizer { } if (useInfo.isUsedByOtherApps(path)) { - pw.println("used be other apps: " + useInfo.getLoadingPackages(path)); + pw.println("used by other apps: " + useInfo.getLoadingPackages(path)); } Map<String, PackageDexUsage.DexUseInfo> dexUseInfoMap = useInfo.getDexUseInfoMap(); @@ -438,19 +444,10 @@ public class PackageDexOptimizer { PackageDexUsage.DexUseInfo dexUseInfo = e.getValue(); pw.println(dex); pw.increaseIndent(); - for (String isa : dexUseInfo.getLoaderIsas()) { - String status = null; - try { - status = DexFile.getDexFileStatus(path, isa); - } catch (IOException ioe) { - status = "[Exception]: " + ioe.getMessage(); - } - pw.println(isa + ": " + status); - } - + // TODO(calin): get the status of the oat file (needs installd call) pw.println("class loader context: " + dexUseInfo.getClassLoaderContext()); if (dexUseInfo.isUsedByOtherApps()) { - pw.println("used be other apps: " + dexUseInfo.getLoadingPackages()); + pw.println("used by other apps: " + dexUseInfo.getLoadingPackages()); } pw.decreaseIndent(); } @@ -474,8 +471,9 @@ public class PackageDexOptimizer { } if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps) { - // If the dex files is used by other apps, we cannot use profile-guided compilation. - return getNonProfileGuidedCompilerFilter(targetCompilerFilter); + // If the dex files is used by other apps, apply the shared filter. + return PackageManagerServiceCompilerMapping.getCompilerFilterForReason( + PackageManagerService.REASON_SHARED); } return targetCompilerFilter; diff --git a/com/android/server/pm/PackageInstallerService.java b/com/android/server/pm/PackageInstallerService.java index 1fa37b91..09f9cb8c 100644 --- a/com/android/server/pm/PackageInstallerService.java +++ b/com/android/server/pm/PackageInstallerService.java @@ -80,6 +80,8 @@ import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.ImageUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.server.IoThread; +import com.android.server.LocalServices; +import com.android.server.pm.permission.PermissionManagerInternal; import libcore.io.IoUtils; @@ -122,6 +124,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub { private final Context mContext; private final PackageManagerService mPm; + private final PermissionManagerInternal mPermissionManager; private AppOpsManager mAppOps; @@ -177,6 +180,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub { public PackageInstallerService(Context context, PackageManagerService pm) { mContext = context; mPm = pm; + mPermissionManager = LocalServices.getService(PermissionManagerInternal.class); mInstallThread = new HandlerThread(TAG); mInstallThread.start(); @@ -243,35 +247,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub { } } - public void onSecureContainersAvailable() { - synchronized (mSessions) { - final ArraySet<String> unclaimed = new ArraySet<>(); - for (String cid : PackageHelper.getSecureContainerList()) { - if (isStageName(cid)) { - unclaimed.add(cid); - } - } - - // Ignore stages claimed by active sessions - for (int i = 0; i < mSessions.size(); i++) { - final PackageInstallerSession session = mSessions.valueAt(i); - final String cid = session.stageCid; - - if (unclaimed.remove(cid)) { - // Claimed by active session, mount it - PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(), - Process.SYSTEM_UID); - } - } - - // Clean up orphaned staging containers - for (String cid : unclaimed) { - Slog.w(TAG, "Deleting orphan container " + cid); - PackageHelper.destroySdDir(cid); - } - } - } - public static boolean isStageName(String name) { final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp"); final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp"); @@ -426,7 +401,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub { private int createSessionInternal(SessionParams params, String installerPackageName, int userId) throws IOException { final int callingUid = Binder.getCallingUid(); - mPm.enforceCrossUserPermission(callingUid, userId, true, true, "createSession"); + mPermissionManager.enforceCrossUserPermission( + callingUid, userId, true, true, "createSession"); if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) { throw new SecurityException("User restriction prevents installing"); @@ -671,13 +647,6 @@ public class PackageInstallerService extends IPackageInstaller.Stub { return "smdl" + sessionId + ".tmp"; } - static void prepareExternalStageCid(String stageCid, long sizeBytes) throws IOException { - if (PackageHelper.createSdDir(sizeBytes, stageCid, PackageManagerService.getEncryptKey(), - Process.SYSTEM_UID, true) == null) { - throw new IOException("Failed to create session cid: " + stageCid); - } - } - @Override public SessionInfo getSessionInfo(int sessionId) { synchronized (mSessions) { @@ -688,7 +657,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub { @Override public ParceledListSlice<SessionInfo> getAllSessions(int userId) { - mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getAllSessions"); + mPermissionManager.enforceCrossUserPermission( + Binder.getCallingUid(), userId, true, false, "getAllSessions"); final List<SessionInfo> result = new ArrayList<>(); synchronized (mSessions) { @@ -704,7 +674,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub { @Override public ParceledListSlice<SessionInfo> getMySessions(String installerPackageName, int userId) { - mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "getMySessions"); + mPermissionManager.enforceCrossUserPermission( + Binder.getCallingUid(), userId, true, false, "getMySessions"); mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName); final List<SessionInfo> result = new ArrayList<>(); @@ -726,7 +697,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub { public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags, IntentSender statusReceiver, int userId) throws RemoteException { final int callingUid = Binder.getCallingUid(); - mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall"); + mPermissionManager.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall"); if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) { mAppOps.checkPackage(callingUid, callerPackageName); } @@ -775,7 +746,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub { @Override public void registerCallback(IPackageInstallerCallback callback, int userId) { - mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "registerCallback"); + mPermissionManager.enforceCrossUserPermission( + Binder.getCallingUid(), userId, true, false, "registerCallback"); mCallbacks.register(callback, userId); } diff --git a/com/android/server/pm/PackageInstallerSession.java b/com/android/server/pm/PackageInstallerSession.java index ff6e5b3b..d62f0934 100644 --- a/com/android/server/pm/PackageInstallerSession.java +++ b/com/android/server/pm/PackageInstallerSession.java @@ -36,7 +36,6 @@ import static com.android.internal.util.XmlUtils.writeIntAttribute; import static com.android.internal.util.XmlUtils.writeLongAttribute; import static com.android.internal.util.XmlUtils.writeStringAttribute; import static com.android.internal.util.XmlUtils.writeUriAttribute; -import static com.android.server.pm.PackageInstallerService.prepareExternalStageCid; import static com.android.server.pm.PackageInstallerService.prepareStageDir; import android.Manifest; @@ -481,12 +480,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (stageDir != null) { mResolvedStageDir = stageDir; } else { - final String path = PackageHelper.getSdDir(stageCid); - if (path != null) { - mResolvedStageDir = new File(path); - } else { - throw new IOException("Failed to resolve path to container " + stageCid); - } + throw new IOException("Missing stageDir"); } } return mResolvedStageDir; @@ -880,14 +874,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return; } - if (stageCid != null) { - // Figure out the final installed size and resize the container once - // and for all. Internally the parser handles straddling between two - // locations when inheriting. - final long finalSize = calculateInstalledSize(); - resizeContainer(stageCid, finalSize); - } - // Inherit any packages and native libraries from existing install that // haven't been overridden. if (params.mode == SessionParams.MODE_INHERIT_EXISTING) { @@ -924,11 +910,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Unpack native libraries extractNativeLibraries(mResolvedStageDir, params.abiOverride); - // Container is ready to go, let's seal it up! - if (stageCid != null) { - finalizeAndFixContainer(stageCid); - } - // We've reached point of no return; call into PMS to install the stage. // Regardless of success or failure we always destroy session. final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() { @@ -953,7 +934,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } mRelinquished = true; - mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params, + mPm.installStage(mPackageName, stageDir, localObserver, params, mInstallerPackageName, mInstallerUid, user, mCertificates); } @@ -1212,11 +1193,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // straddled between the inherited and staged APKs. final PackageLite pkg = new PackageLite(null, baseApk, null, null, null, null, splitPaths.toArray(new String[splitPaths.size()]), null); - final boolean isForwardLocked = - (params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0; try { - return PackageHelper.calculateInstalledSize(pkg, isForwardLocked, params.abiOverride); + return PackageHelper.calculateInstalledSize(pkg, params.abiOverride); } catch (IOException e) { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Failed to calculate install size", e); @@ -1345,52 +1324,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } - private static void resizeContainer(String cid, long targetSize) - throws PackageManagerException { - String path = PackageHelper.getSdDir(cid); - if (path == null) { - throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, - "Failed to find mounted " + cid); - } - - final long currentSize = new File(path).getTotalSpace(); - if (currentSize > targetSize) { - Slog.w(TAG, "Current size " + currentSize + " is larger than target size " - + targetSize + "; skipping resize"); - return; - } - - if (!PackageHelper.unMountSdDir(cid)) { - throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, - "Failed to unmount " + cid + " before resize"); - } - - if (!PackageHelper.resizeSdDir(targetSize, cid, - PackageManagerService.getEncryptKey())) { - throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, - "Failed to resize " + cid + " to " + targetSize + " bytes"); - } - - path = PackageHelper.mountSdDir(cid, PackageManagerService.getEncryptKey(), - Process.SYSTEM_UID, false); - if (path == null) { - throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, - "Failed to mount " + cid + " after resize"); - } - } - - private void finalizeAndFixContainer(String cid) throws PackageManagerException { - if (!PackageHelper.finalizeSdDir(cid)) { - throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, - "Failed to finalize container " + cid); - } - - if (!PackageHelper.fixSdPermissions(cid, defaultContainerGid, null)) { - throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, - "Failed to fix permissions on container " + cid); - } - } - void setPermissionsResult(boolean accepted) { if (!mSealed) { throw new SecurityException("Must be sealed to accept permissions"); @@ -1419,20 +1352,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (!mPrepared) { if (stageDir != null) { prepareStageDir(stageDir); - } else if (stageCid != null) { - final long identity = Binder.clearCallingIdentity(); - try { - prepareExternalStageCid(stageCid, params.sizeBytes); - } finally { - Binder.restoreCallingIdentity(identity); - } - - // TODO: deliver more granular progress for ASEC allocation - mInternalProgress = 0.25f; - computeProgressLocked(true); } else { - throw new IllegalArgumentException( - "Exactly one of stageDir or stageCid stage must be set"); + throw new IllegalArgumentException("stageDir must be set"); } mPrepared = true; @@ -1534,9 +1455,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } catch (InstallerException ignored) { } } - if (stageCid != null) { - PackageHelper.destroySdDir(stageCid); - } } void dump(IndentingPrintWriter pw) { diff --git a/com/android/server/pm/PackageManagerService.java b/com/android/server/pm/PackageManagerService.java index ff52e0eb..7d1a6470 100644 --- a/com/android/server/pm/PackageManagerService.java +++ b/com/android/server/pm/PackageManagerService.java @@ -55,7 +55,6 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY; import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_FAILED_USER_RESTRICTED; import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; -import static android.content.pm.PackageManager.INSTALL_FORWARD_LOCK; import static android.content.pm.PackageManager.INSTALL_INTERNAL; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; @@ -103,10 +102,9 @@ import static com.android.server.pm.InstructionSets.getPreferredInstructionSet; import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet; import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason; import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter; -import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_FAILURE; -import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS; -import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED; - +import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE; +import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS; +import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED; import static dalvik.system.DexFile.getNonProfileGuidedCompilerFilter; import android.Manifest; @@ -160,10 +158,11 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.LegacyPackageDeleteObserver; import android.content.pm.PackageManagerInternal; +import android.content.pm.PackageManager.LegacyPackageDeleteObserver; import android.content.pm.PackageParser; import android.content.pm.PackageParser.ActivityIntentInfo; +import android.content.pm.PackageParser.Package; import android.content.pm.PackageParser.PackageLite; import android.content.pm.PackageParser.PackageParserException; import android.content.pm.PackageStats; @@ -230,7 +229,6 @@ import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Base64; -import android.util.TimingsTraceLog; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.ExceptionUtils; @@ -244,6 +242,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; +import android.util.TimingsTraceLog; import android.util.Xml; import android.util.jar.StrictJarFile; import android.util.proto.ProtoOutputStream; @@ -283,12 +282,19 @@ import com.android.server.SystemServerInitThreadPool; import com.android.server.Watchdog; import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.pm.Installer.InstallerException; -import com.android.server.pm.PermissionsState.PermissionState; import com.android.server.pm.Settings.DatabaseVersion; import com.android.server.pm.Settings.VersionInfo; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.DexoptOptions; import com.android.server.pm.dex.PackageDexUsage; +import com.android.server.pm.permission.BasePermission; +import com.android.server.pm.permission.DefaultPermissionGrantPolicy; +import com.android.server.pm.permission.PermissionManagerService; +import com.android.server.pm.permission.PermissionManagerInternal; +import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback; +import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback; +import com.android.server.pm.permission.PermissionsState; +import com.android.server.pm.permission.PermissionsState.PermissionState; import com.android.server.storage.DeviceStorageMonitorInternal; import dalvik.system.CloseGuard; @@ -338,6 +344,7 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -385,7 +392,7 @@ import java.util.zip.GZIPInputStream; public class PackageManagerService extends IPackageManager.Stub implements PackageSender { static final String TAG = "PackageManager"; - static final boolean DEBUG_SETTINGS = false; + public static final boolean DEBUG_SETTINGS = false; static final boolean DEBUG_PREFERRED = false; static final boolean DEBUG_UPGRADE = false; static final boolean DEBUG_DOMAIN_VERIFICATION = false; @@ -396,7 +403,7 @@ public class PackageManagerService extends IPackageManager.Stub private static final boolean DEBUG_SHOW_INFO = false; private static final boolean DEBUG_PACKAGE_INFO = false; private static final boolean DEBUG_INTENT_MATCHING = false; - private static final boolean DEBUG_PACKAGE_SCANNING = false; + public static final boolean DEBUG_PACKAGE_SCANNING = false; private static final boolean DEBUG_VERIFY = false; private static final boolean DEBUG_FILTERS = false; private static final boolean DEBUG_PERMISSIONS = false; @@ -427,9 +434,6 @@ public class PackageManagerService extends IPackageManager.Stub private static final int BLUETOOTH_UID = Process.BLUETOOTH_UID; private static final int SHELL_UID = Process.SHELL_UID; - // Cap the size of permission trees that 3rd party apps can define - private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768; // characters of text - // Suffix used during package installation when copying/moving // package apks to install directory. private static final String INSTALL_PACKAGE_SUFFIX = "-"; @@ -578,8 +582,9 @@ public class PackageManagerService extends IPackageManager.Stub public static final int REASON_BACKGROUND_DEXOPT = 3; public static final int REASON_AB_OTA = 4; public static final int REASON_INACTIVE_PACKAGE_DOWNGRADE = 5; + public static final int REASON_SHARED = 6; - public static final int REASON_LAST = REASON_INACTIVE_PACKAGE_DOWNGRADE; + public static final int REASON_LAST = REASON_SHARED; /** All dangerous permission names in the same order as the events in MetricsEvent */ private static final List<String> ALL_DANGEROUS_PERMISSIONS = Arrays.asList( @@ -654,9 +659,6 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mPackages") private boolean mDexOptDialogShown; - /** The location for ASEC container files on internal storage. */ - final String mAsecInternalPath; - // Used for privilege escalation. MUST NOT BE CALLED WITH mPackages // LOCK HELD. Can be called with mInstallLock held. @GuardedBy("mInstallLock") @@ -862,7 +864,7 @@ public class PackageManagerService extends IPackageManager.Stub String targetPath) { return getStaticOverlayPaths(targetPackageName, targetPath); } - }; + } class ParallelPackageParserCallback extends PackageParserCallback { List<PackageParser.Package> mOverlayPackages = null; @@ -1004,7 +1006,9 @@ public class PackageManagerService extends IPackageManager.Stub final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates = new SparseArray<IntentFilterVerificationState>(); + // TODO remove this and go through mPermissonManager directly final DefaultPermissionGrantPolicy mDefaultPermissionPolicy; + private final PermissionManagerInternal mPermissionManager; // List of packages names to keep cached, even if they are uninstalled for all users private List<String> mKeepUninstalledPackages; @@ -1315,7 +1319,6 @@ public class PackageManagerService extends IPackageManager.Stub static final int POST_INSTALL = 9; static final int MCS_RECONNECT = 10; static final int MCS_GIVE_UP = 11; - static final int UPDATED_MEDIA_STATUS = 12; static final int WRITE_SETTINGS = 13; static final int WRITE_PACKAGE_RESTRICTIONS = 14; static final int PACKAGE_VERIFIED = 15; @@ -1714,32 +1717,6 @@ public class PackageManagerService extends IPackageManager.Stub Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1); } break; - case UPDATED_MEDIA_STATUS: { - if (DEBUG_SD_INSTALL) Log.i(TAG, "Got message UPDATED_MEDIA_STATUS"); - boolean reportStatus = msg.arg1 == 1; - boolean doGc = msg.arg2 == 1; - if (DEBUG_SD_INSTALL) Log.i(TAG, "reportStatus=" + reportStatus + ", doGc = " + doGc); - if (doGc) { - // Force a gc to clear up stale containers. - Runtime.getRuntime().gc(); - } - if (msg.obj != null) { - @SuppressWarnings("unchecked") - Set<AsecInstallArgs> args = (Set<AsecInstallArgs>) msg.obj; - if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading all containers"); - // Unload containers - unloadAllContainers(args); - } - if (reportStatus) { - try { - if (DEBUG_SD_INSTALL) Log.i(TAG, - "Invoking StorageManagerService call back"); - PackageHelper.getStorageManager().finishMediaUpdate(); - } catch (RemoteException e) { - Log.e(TAG, "StorageManagerService not running?"); - } - } - } break; case WRITE_SETTINGS: { Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); synchronized (mPackages) { @@ -1910,6 +1887,69 @@ public class PackageManagerService extends IPackageManager.Stub } } + private PermissionCallback mPermissionCallback = new PermissionCallback() { + @Override + public void onGidsChanged(int appId, int userId) { + mHandler.post(new Runnable() { + @Override + public void run() { + killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED); + } + }); + } + @Override + public void onPermissionGranted(int uid, int userId) { + mOnPermissionChangeListeners.onPermissionsChanged(uid); + + // Not critical; if this is lost, the application has to request again. + synchronized (mPackages) { + mSettings.writeRuntimePermissionsForUserLPr(userId, false); + } + } + @Override + public void onInstallPermissionGranted() { + synchronized (mPackages) { + scheduleWriteSettingsLocked(); + } + } + @Override + public void onPermissionRevoked(int uid, int userId) { + mOnPermissionChangeListeners.onPermissionsChanged(uid); + + synchronized (mPackages) { + // Critical; after this call the application should never have the permission + mSettings.writeRuntimePermissionsForUserLPr(userId, true); + } + + final int appId = UserHandle.getAppId(uid); + killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED); + } + @Override + public void onInstallPermissionRevoked() { + synchronized (mPackages) { + scheduleWriteSettingsLocked(); + } + } + @Override + public void onPermissionUpdated(int userId) { + synchronized (mPackages) { + mSettings.writeRuntimePermissionsForUserLPr(userId, false); + } + } + @Override + public void onInstallPermissionUpdated() { + synchronized (mPackages) { + scheduleWriteSettingsLocked(); + } + } + @Override + public void onPermissionRemoved() { + synchronized (mPackages) { + mSettings.writeLPr(); + } + } + }; + private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions, boolean killApp, boolean virtualPreload, String[] grantedPermissions, boolean launchedForRestore, String installerPackage, @@ -1926,7 +1966,10 @@ public class PackageManagerService extends IPackageManager.Stub // review flag which is used to emulate runtime permissions for // legacy apps. if (grantPermissions) { - grantRequestedRuntimePermissions(res.pkg, res.newUsers, grantedPermissions); + final int callingUid = Binder.getCallingUid(); + mPermissionManager.grantRequestedRuntimePermissions( + res.pkg, res.newUsers, grantedPermissions, callingUid, + mPermissionCallback); } final boolean update = res.removedInfo != null @@ -1942,9 +1985,9 @@ public class PackageManagerService extends IPackageManager.Stub // app that had no children, we grant requested runtime permissions to the new // children if the parent on the system image had them already granted. if (res.pkg.parentPackage != null) { - synchronized (mPackages) { - grantRuntimePermissionsGrantedToDisabledPrivSysPackageParentLPw(res.pkg); - } + final int callingUid = Binder.getCallingUid(); + mPermissionManager.grantRuntimePermissionsGrantedToDisabledPackage( + res.pkg, callingUid, mPermissionCallback); } synchronized (mPackages) { @@ -2109,39 +2152,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - private void grantRuntimePermissionsGrantedToDisabledPrivSysPackageParentLPw( - PackageParser.Package pkg) { - if (pkg.parentPackage == null) { - return; - } - if (pkg.requestedPermissions == null) { - return; - } - final PackageSetting disabledSysParentPs = mSettings - .getDisabledSystemPkgLPr(pkg.parentPackage.packageName); - if (disabledSysParentPs == null || disabledSysParentPs.pkg == null - || !disabledSysParentPs.isPrivileged() - || (disabledSysParentPs.childPackageNames != null - && !disabledSysParentPs.childPackageNames.isEmpty())) { - return; - } - final int[] allUserIds = sUserManager.getUserIds(); - final int permCount = pkg.requestedPermissions.size(); - for (int i = 0; i < permCount; i++) { - String permission = pkg.requestedPermissions.get(i); - BasePermission bp = mSettings.mPermissions.get(permission); - if (bp == null || !(bp.isRuntime() || bp.isDevelopment())) { - continue; - } - for (int userId : allUserIds) { - if (disabledSysParentPs.getPermissionsState().hasRuntimePermission( - permission, userId)) { - grantRuntimePermission(pkg.packageName, permission, userId); - } - } - } - } - private StorageEventListener mStorageListener = new StorageEventListener() { @Override public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { @@ -2164,14 +2174,6 @@ public class PackageManagerService extends IPackageManager.Stub unloadPrivatePackages(vol); } } - - if (vol.type == VolumeInfo.TYPE_PUBLIC && vol.isPrimary()) { - if (vol.state == VolumeInfo.STATE_MOUNTED) { - updateExternalMediaStatus(true, false); - } else if (vol.state == VolumeInfo.STATE_EJECTING) { - updateExternalMediaStatus(false, false); - } - } } @Override @@ -2202,58 +2204,6 @@ public class PackageManagerService extends IPackageManager.Stub } }; - private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds, - String[] grantedPermissions) { - for (int userId : userIds) { - grantRequestedRuntimePermissionsForUser(pkg, userId, grantedPermissions); - } - } - - private void grantRequestedRuntimePermissionsForUser(PackageParser.Package pkg, int userId, - String[] grantedPermissions) { - PackageSetting ps = (PackageSetting) pkg.mExtras; - if (ps == null) { - return; - } - - PermissionsState permissionsState = ps.getPermissionsState(); - - final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED - | PackageManager.FLAG_PERMISSION_POLICY_FIXED; - - final boolean supportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion - >= Build.VERSION_CODES.M; - - final boolean instantApp = isInstantApp(pkg.packageName, userId); - - for (String permission : pkg.requestedPermissions) { - final BasePermission bp; - synchronized (mPackages) { - bp = mSettings.mPermissions.get(permission); - } - if (bp != null && (bp.isRuntime() || bp.isDevelopment()) - && (!instantApp || bp.isInstant()) - && (supportsRuntimePermissions || !bp.isRuntimeOnly()) - && (grantedPermissions == null - || ArrayUtils.contains(grantedPermissions, permission))) { - final int flags = permissionsState.getPermissionFlags(permission, userId); - if (supportsRuntimePermissions) { - // Installer cannot change immutable permissions. - if ((flags & immutableFlags) == 0) { - grantRuntimePermission(pkg.packageName, permission, userId); - } - } else if (mPermissionReviewRequired) { - // In permission review mode we clear the review flag when we - // are asked to install the app with all permissions granted. - if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { - updatePermissionFlags(permission, pkg.packageName, - PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, 0, userId); - } - } - } - } - } - Bundle extrasForInstallResult(PackageInstalledInfo res) { Bundle extras = null; switch (res.returnCode) { @@ -2422,7 +2372,29 @@ public class PackageManagerService extends IPackageManager.Stub mFactoryTest = factoryTest; mOnlyCore = onlyCore; mMetrics = new DisplayMetrics(); - mSettings = new Settings(mPackages); + mInstaller = installer; + + // Create sub-components that provide services / data. Order here is important. + synchronized (mInstallLock) { + synchronized (mPackages) { + // Expose private service for system components to use. + LocalServices.addService( + PackageManagerInternal.class, new PackageManagerInternalImpl()); + sUserManager = new UserManagerService(context, this, + new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages); + mPermissionManager = PermissionManagerService.create(context, + new DefaultPermissionGrantedCallback() { + @Override + public void onDefaultRuntimePermissionsGranted(int userId) { + synchronized(mPackages) { + mSettings.onDefaultRuntimePermissionsGrantedLPr(userId); + } + } + }, mPackages /*externalLock*/); + mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy(); + mSettings = new Settings(mPermissionManager.getPermissionSettings(), mPackages); + } + } mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, @@ -2453,7 +2425,6 @@ public class PackageManagerService extends IPackageManager.Stub mSeparateProcesses = null; } - mInstaller = installer; mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context, "*dexopt*"); mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock); @@ -2482,32 +2453,12 @@ public class PackageManagerService extends IPackageManager.Stub mHandler = new PackageHandler(mHandlerThread.getLooper()); mProcessLoggingHandler = new ProcessLoggingHandler(); Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT); - - mDefaultPermissionPolicy = new DefaultPermissionGrantPolicy(this); mInstantAppRegistry = new InstantAppRegistry(this); File dataDir = Environment.getDataDirectory(); mAppInstallDir = new File(dataDir, "app"); mAppLib32InstallDir = new File(dataDir, "app-lib"); - mAsecInternalPath = new File(dataDir, "app-asec").getPath(); mDrmAppPrivateInstallDir = new File(dataDir, "app-private"); - sUserManager = new UserManagerService(context, this, - new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages); - - // Propagate permission configuration in to package manager. - ArrayMap<String, SystemConfig.PermissionEntry> permConfig - = systemConfig.getPermissions(); - for (int i=0; i<permConfig.size(); i++) { - SystemConfig.PermissionEntry perm = permConfig.valueAt(i); - BasePermission bp = mSettings.mPermissions.get(perm.name); - if (bp == null) { - bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN); - mSettings.mPermissions.put(perm.name, bp); - } - if (perm.gids != null) { - bp.setGids(perm.gids, perm.perUser); - } - } ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries(); final int builtInLibCount = libConfig.size(); @@ -3110,8 +3061,6 @@ public class PackageManagerService extends IPackageManager.Stub // once we have a booted system. mInstaller.setWarnIfHeld(mPackages); - // Expose private service for system components to use. - LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl()); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } @@ -3314,6 +3263,24 @@ public class PackageManagerService extends IPackageManager.Stub removeCodePathLI(dstCodePath); return null; } + + // If we have a profile for a compressed APK, copy it to the reference location. + // Since the package is the stub one, remove the stub suffix to get the normal package and + // APK name. + File profileFile = new File(getPrebuildProfilePath(pkg).replace(STUB_SUFFIX, "")); + if (profileFile.exists()) { + try { + // We could also do this lazily before calling dexopt in + // PackageDexOptimizer to prevent this happening on first boot. The issue + // is that we don't have a good way to say "do this only once". + if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), + pkg.applicationInfo.uid, pkg.packageName)) { + Log.e(TAG, "decompressPackage failed to copy system profile!"); + } + } catch (Exception e) { + Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); + } + } return dstCodePath; } @@ -3909,7 +3876,7 @@ public class PackageManagerService extends IPackageManager.Stub public boolean isPackageAvailable(String packageName, int userId) { if (!sUserManager.exists(userId)) return false; final int callingUid = Binder.getCallingUid(); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "is package available"); synchronized (mPackages) { PackageParser.Package p = mPackages.get(packageName); @@ -3952,7 +3919,7 @@ public class PackageManagerService extends IPackageManager.Stub int flags, int filterCallingUid, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForPackage(flags, userId, packageName); - enforceCrossUserPermission(Binder.getCallingUid(), userId, + mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get package info"); // reader @@ -4214,7 +4181,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!sUserManager.exists(userId)) return -1; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForPackage(flags, userId, packageName); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "getPackageUid"); // reader @@ -4244,7 +4211,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForPackage(flags, userId, packageName); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "getPackageGids"); // reader @@ -4271,116 +4238,23 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - static PermissionInfo generatePermissionInfo(BasePermission bp, int flags) { - if (bp.perm != null) { - return PackageParser.generatePermissionInfo(bp.perm, flags); - } - PermissionInfo pi = new PermissionInfo(); - pi.name = bp.name; - pi.packageName = bp.sourcePackage; - pi.nonLocalizedLabel = bp.name; - pi.protectionLevel = bp.protectionLevel; - return pi; - } - @Override public PermissionInfo getPermissionInfo(String name, String packageName, int flags) { - final int callingUid = Binder.getCallingUid(); - if (getInstantAppPackageName(callingUid) != null) { - return null; - } - // reader - synchronized (mPackages) { - final BasePermission p = mSettings.mPermissions.get(name); - if (p == null) { - return null; - } - // If the caller is an app that targets pre 26 SDK drop protection flags. - PermissionInfo permissionInfo = generatePermissionInfo(p, flags); - if (permissionInfo != null) { - final int protectionLevel = adjustPermissionProtectionFlagsLPr( - permissionInfo.protectionLevel, packageName, callingUid); - if (permissionInfo.protectionLevel != protectionLevel) { - // If we return different protection level, don't use the cached info - if (p.perm != null && p.perm.info == permissionInfo) { - permissionInfo = new PermissionInfo(permissionInfo); - } - permissionInfo.protectionLevel = protectionLevel; - } - } - return permissionInfo; - } - } - - private int adjustPermissionProtectionFlagsLPr(int protectionLevel, - String packageName, int uid) { - // Signature permission flags area always reported - final int protectionLevelMasked = protectionLevel - & (PermissionInfo.PROTECTION_NORMAL - | PermissionInfo.PROTECTION_DANGEROUS - | PermissionInfo.PROTECTION_SIGNATURE); - if (protectionLevelMasked == PermissionInfo.PROTECTION_SIGNATURE) { - return protectionLevel; - } - - // System sees all flags. - final int appId = UserHandle.getAppId(uid); - if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID - || appId == Process.SHELL_UID) { - return protectionLevel; - } - - // Normalize package name to handle renamed packages and static libs - packageName = resolveInternalPackageNameLPr(packageName, - PackageManager.VERSION_CODE_HIGHEST); - - // Apps that target O see flags for all protection levels. - final PackageSetting ps = mSettings.mPackages.get(packageName); - if (ps == null) { - return protectionLevel; - } - if (ps.appId != appId) { - return protectionLevel; - } - - final PackageParser.Package pkg = mPackages.get(packageName); - if (pkg == null) { - return protectionLevel; - } - if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) { - return protectionLevelMasked; - } - - return protectionLevel; + return mPermissionManager.getPermissionInfo(name, packageName, flags, getCallingUid()); } @Override - public @Nullable ParceledListSlice<PermissionInfo> queryPermissionsByGroup(String group, + public @Nullable ParceledListSlice<PermissionInfo> queryPermissionsByGroup(String groupName, int flags) { - if (getInstantAppPackageName(Binder.getCallingUid()) != null) { - return null; - } - // reader + // TODO Move this to PermissionManager when mPermissionGroups is moved there synchronized (mPackages) { - if (group != null && !mPermissionGroups.containsKey(group)) { + if (groupName != null && !mPermissionGroups.containsKey(groupName)) { // This is thrown as NameNotFoundException return null; } - - ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10); - for (BasePermission p : mSettings.mPermissions.values()) { - if (group == null) { - if (p.perm == null || p.perm.info.group == null) { - out.add(generatePermissionInfo(p, flags)); - } - } else { - if (p.perm != null && group.equals(p.perm.info.group)) { - out.add(PackageParser.generatePermissionInfo(p.perm, flags)); - } - } - } - return new ParceledListSlice<>(out); } + return new ParceledListSlice<>( + mPermissionManager.getPermissionInfoByGroup(groupName, flags, getCallingUid())); } @Override @@ -4455,7 +4329,7 @@ public class PackageManagerService extends IPackageManager.Stub int filterCallingUid, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForApplication(flags, userId, packageName); - enforceCrossUserPermission(Binder.getCallingUid(), userId, + mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get application info"); // writer @@ -4764,7 +4638,8 @@ public class PackageManagerService extends IPackageManager.Stub triaged = false; } if ((flags & PackageManager.MATCH_ANY_USER) != 0) { - enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, + mPermissionManager.enforceCrossUserPermission( + Binder.getCallingUid(), userId, false, false, "MATCH_ANY_USER flag requires INTERACT_ACROSS_USERS permission at " + Debug.getCallers(5)); } else if ((flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0 && isCallerSystemUser @@ -4893,7 +4768,7 @@ public class PackageManagerService extends IPackageManager.Stub int filterCallingUid, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForComponent(flags, userId, component); - enforceCrossUserPermission(Binder.getCallingUid(), userId, + mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get activity info"); synchronized (mPackages) { PackageParser.Activity a = mActivities.mActivities.get(component); @@ -4952,7 +4827,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId, component); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get receiver info"); synchronized (mPackages) { PackageParser.Activity a = mReceivers.mActivities.get(component); @@ -5089,7 +4964,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId, component); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get service info"); synchronized (mPackages) { PackageParser.Service s = mServices.mServices.get(component); @@ -5113,7 +4988,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId, component); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get provider info"); synchronized (mPackages) { PackageParser.Provider p = mProviders.mProviders.get(component); @@ -5276,39 +5151,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public int checkPermission(String permName, String pkgName, int userId) { - if (!sUserManager.exists(userId)) { - return PackageManager.PERMISSION_DENIED; - } - final int callingUid = Binder.getCallingUid(); - - synchronized (mPackages) { - final PackageParser.Package p = mPackages.get(pkgName); - if (p != null && p.mExtras != null) { - final PackageSetting ps = (PackageSetting) p.mExtras; - if (filterAppAccessLPr(ps, callingUid, userId)) { - return PackageManager.PERMISSION_DENIED; - } - final boolean instantApp = ps.getInstantApp(userId); - final PermissionsState permissionsState = ps.getPermissionsState(); - if (permissionsState.hasPermission(permName, userId)) { - if (instantApp) { - BasePermission bp = mSettings.mPermissions.get(permName); - if (bp != null && bp.isInstant()) { - return PackageManager.PERMISSION_GRANTED; - } - } else { - return PackageManager.PERMISSION_GRANTED; - } - } - // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION - if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState - .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) { - return PackageManager.PERMISSION_GRANTED; - } - } - } - - return PackageManager.PERMISSION_DENIED; + return mPermissionManager.checkPermission(permName, pkgName, getCallingUid(), userId); } @Override @@ -5339,8 +5182,7 @@ public class PackageManagerService extends IPackageManager.Stub final PermissionsState permissionsState = settingBase.getPermissionsState(); if (permissionsState.hasPermission(permName, userId)) { if (isUidInstantApp) { - BasePermission bp = mSettings.mPermissions.get(permName); - if (bp != null && bp.isInstant()) { + if (mPermissionManager.isPermissionInstant(permName)) { return PackageManager.PERMISSION_GRANTED; } } else { @@ -5409,448 +5251,49 @@ public class PackageManagerService extends IPackageManager.Stub } } - /** - * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS - * or INTERACT_ACROSS_USERS_FULL permissions, if the userid is not for the caller. - * @param checkShell whether to prevent shell from access if there's a debugging restriction - * @param message the message to log on security exception - */ - void enforceCrossUserPermission(int callingUid, int userId, boolean requireFullPermission, - boolean checkShell, String message) { - if (userId < 0) { - throw new IllegalArgumentException("Invalid userId " + userId); - } - if (checkShell) { - enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId); - } - if (userId == UserHandle.getUserId(callingUid)) return; - if (callingUid != Process.SYSTEM_UID && callingUid != 0) { - if (requireFullPermission) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message); - } else { - try { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message); - } catch (SecurityException se) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS, message); - } - } - } - } - - void enforceShellRestriction(String restriction, int callingUid, int userHandle) { - if (callingUid == Process.SHELL_UID) { - if (userHandle >= 0 - && sUserManager.hasUserRestriction(restriction, userHandle)) { - throw new SecurityException("Shell does not have permission to access user " - + userHandle); - } else if (userHandle < 0) { - Slog.e(TAG, "Unable to check shell permission for user " + userHandle + "\n\t" - + Debug.getCallers(3)); - } - } - } - - private BasePermission findPermissionTreeLP(String permName) { - for(BasePermission bp : mSettings.mPermissionTrees.values()) { - if (permName.startsWith(bp.name) && - permName.length() > bp.name.length() && - permName.charAt(bp.name.length()) == '.') { - return bp; - } - } - return null; - } - - private BasePermission checkPermissionTreeLP(String permName) { - if (permName != null) { - BasePermission bp = findPermissionTreeLP(permName); - if (bp != null) { - if (bp.uid == UserHandle.getAppId(Binder.getCallingUid())) { - return bp; - } - throw new SecurityException("Calling uid " - + Binder.getCallingUid() - + " is not allowed to add to permission tree " - + bp.name + " owned by uid " + bp.uid); - } - } - throw new SecurityException("No permission tree found for " + permName); - } - - static boolean compareStrings(CharSequence s1, CharSequence s2) { - if (s1 == null) { - return s2 == null; - } - if (s2 == null) { - return false; - } - if (s1.getClass() != s2.getClass()) { - return false; - } - return s1.equals(s2); - } - - static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) { - if (pi1.icon != pi2.icon) return false; - if (pi1.logo != pi2.logo) return false; - if (pi1.protectionLevel != pi2.protectionLevel) return false; - if (!compareStrings(pi1.name, pi2.name)) return false; - if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false; - // We'll take care of setting this one. - if (!compareStrings(pi1.packageName, pi2.packageName)) return false; - // These are not currently stored in settings. - //if (!compareStrings(pi1.group, pi2.group)) return false; - //if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false; - //if (pi1.labelRes != pi2.labelRes) return false; - //if (pi1.descriptionRes != pi2.descriptionRes) return false; - return true; - } - - int permissionInfoFootprint(PermissionInfo info) { - int size = info.name.length(); - if (info.nonLocalizedLabel != null) size += info.nonLocalizedLabel.length(); - if (info.nonLocalizedDescription != null) size += info.nonLocalizedDescription.length(); - return size; - } - - int calculateCurrentPermissionFootprintLocked(BasePermission tree) { - int size = 0; - for (BasePermission perm : mSettings.mPermissions.values()) { - if (perm.uid == tree.uid) { - size += perm.name.length() + permissionInfoFootprint(perm.perm.info); - } - } - return size; - } - - void enforcePermissionCapLocked(PermissionInfo info, BasePermission tree) { - // We calculate the max size of permissions defined by this uid and throw - // if that plus the size of 'info' would exceed our stated maximum. - if (tree.uid != Process.SYSTEM_UID) { - final int curTreeSize = calculateCurrentPermissionFootprintLocked(tree); - if (curTreeSize + permissionInfoFootprint(info) > MAX_PERMISSION_TREE_FOOTPRINT) { - throw new SecurityException("Permission tree size cap exceeded"); - } - } - } - - boolean addPermissionLocked(PermissionInfo info, boolean async) { - if (getInstantAppPackageName(Binder.getCallingUid()) != null) { - throw new SecurityException("Instant apps can't add permissions"); - } - if (info.labelRes == 0 && info.nonLocalizedLabel == null) { - throw new SecurityException("Label must be specified in permission"); - } - BasePermission tree = checkPermissionTreeLP(info.name); - BasePermission bp = mSettings.mPermissions.get(info.name); - boolean added = bp == null; - boolean changed = true; - int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel); - if (added) { - enforcePermissionCapLocked(info, tree); - bp = new BasePermission(info.name, tree.sourcePackage, - BasePermission.TYPE_DYNAMIC); - } else if (bp.type != BasePermission.TYPE_DYNAMIC) { - throw new SecurityException( - "Not allowed to modify non-dynamic permission " - + info.name); - } else { - if (bp.protectionLevel == fixedLevel - && bp.perm.owner.equals(tree.perm.owner) - && bp.uid == tree.uid - && comparePermissionInfos(bp.perm.info, info)) { - changed = false; - } - } - bp.protectionLevel = fixedLevel; - info = new PermissionInfo(info); - info.protectionLevel = fixedLevel; - bp.perm = new PackageParser.Permission(tree.perm.owner, info); - bp.perm.info.packageName = tree.perm.info.packageName; - bp.uid = tree.uid; - if (added) { - mSettings.mPermissions.put(info.name, bp); - } - if (changed) { - if (!async) { - mSettings.writeLPr(); - } else { - scheduleWriteSettingsLocked(); - } - } - return added; + boolean addPermission(PermissionInfo info, final boolean async) { + return mPermissionManager.addPermission( + info, async, getCallingUid(), new PermissionCallback() { + @Override + public void onPermissionChanged() { + if (!async) { + mSettings.writeLPr(); + } else { + scheduleWriteSettingsLocked(); + } + } + }); } @Override public boolean addPermission(PermissionInfo info) { synchronized (mPackages) { - return addPermissionLocked(info, false); + return addPermission(info, false); } } @Override public boolean addPermissionAsync(PermissionInfo info) { synchronized (mPackages) { - return addPermissionLocked(info, true); + return addPermission(info, true); } } @Override - public void removePermission(String name) { - if (getInstantAppPackageName(Binder.getCallingUid()) != null) { - throw new SecurityException("Instant applications don't have access to this method"); - } - synchronized (mPackages) { - checkPermissionTreeLP(name); - BasePermission bp = mSettings.mPermissions.get(name); - if (bp != null) { - if (bp.type != BasePermission.TYPE_DYNAMIC) { - throw new SecurityException( - "Not allowed to modify non-dynamic permission " - + name); - } - mSettings.mPermissions.remove(name); - mSettings.writeLPr(); - } - } - } - - private static void enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission( - PackageParser.Package pkg, BasePermission bp) { - int index = pkg.requestedPermissions.indexOf(bp.name); - if (index == -1) { - throw new SecurityException("Package " + pkg.packageName - + " has not requested permission " + bp.name); - } - if (!bp.isRuntime() && !bp.isDevelopment()) { - throw new SecurityException("Permission " + bp.name - + " is not a changeable permission type"); - } + public void removePermission(String permName) { + mPermissionManager.removePermission(permName, getCallingUid(), mPermissionCallback); } @Override - public void grantRuntimePermission(String packageName, String name, final int userId) { - grantRuntimePermission(packageName, name, userId, false /* Only if not fixed by policy */); - } - - private void grantRuntimePermission(String packageName, String name, final int userId, - boolean overridePolicy) { - if (!sUserManager.exists(userId)) { - Log.e(TAG, "No such user:" + userId); - return; - } - final int callingUid = Binder.getCallingUid(); - - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, - "grantRuntimePermission"); - - enforceCrossUserPermission(callingUid, userId, - true /* requireFullPermission */, true /* checkShell */, - "grantRuntimePermission"); - - final int uid; - final PackageSetting ps; - - synchronized (mPackages) { - final PackageParser.Package pkg = mPackages.get(packageName); - if (pkg == null) { - throw new IllegalArgumentException("Unknown package: " + packageName); - } - final BasePermission bp = mSettings.mPermissions.get(name); - if (bp == null) { - throw new IllegalArgumentException("Unknown permission: " + name); - } - ps = (PackageSetting) pkg.mExtras; - if (ps == null - || filterAppAccessLPr(ps, callingUid, userId)) { - throw new IllegalArgumentException("Unknown package: " + packageName); - } - - enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp); - - // If a permission review is required for legacy apps we represent - // their permissions as always granted runtime ones since we need - // to keep the review required permission flag per user while an - // install permission's state is shared across all users. - if (mPermissionReviewRequired - && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M - && bp.isRuntime()) { - return; - } - - uid = UserHandle.getUid(userId, pkg.applicationInfo.uid); - - final PermissionsState permissionsState = ps.getPermissionsState(); - - final int flags = permissionsState.getPermissionFlags(name, userId); - if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) { - throw new SecurityException("Cannot grant system fixed permission " - + name + " for package " + packageName); - } - if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) { - throw new SecurityException("Cannot grant policy fixed permission " - + name + " for package " + packageName); - } - - if (bp.isDevelopment()) { - // Development permissions must be handled specially, since they are not - // normal runtime permissions. For now they apply to all users. - if (permissionsState.grantInstallPermission(bp) != - PermissionsState.PERMISSION_OPERATION_FAILURE) { - scheduleWriteSettingsLocked(); - } - return; - } - - if (ps.getInstantApp(userId) && !bp.isInstant()) { - throw new SecurityException("Cannot grant non-ephemeral permission" - + name + " for package " + packageName); - } - - if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { - Slog.w(TAG, "Cannot grant runtime permission to a legacy app"); - return; - } - - final int result = permissionsState.grantRuntimePermission(bp, userId); - switch (result) { - case PermissionsState.PERMISSION_OPERATION_FAILURE: { - return; - } - - case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: { - final int appId = UserHandle.getAppId(pkg.applicationInfo.uid); - mHandler.post(new Runnable() { - @Override - public void run() { - killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED); - } - }); - } - break; - } - - if (bp.isRuntime()) { - logPermissionGranted(mContext, name, packageName); - } - - mOnPermissionChangeListeners.onPermissionsChanged(uid); - - // Not critical if that is lost - app has to request again. - mSettings.writeRuntimePermissionsForUserLPr(userId, false); - } - - // Only need to do this if user is initialized. Otherwise it's a new user - // and there are no processes running as the user yet and there's no need - // to make an expensive call to remount processes for the changed permissions. - if (READ_EXTERNAL_STORAGE.equals(name) - || WRITE_EXTERNAL_STORAGE.equals(name)) { - final long token = Binder.clearCallingIdentity(); - try { - if (sUserManager.isInitialized(userId)) { - StorageManagerInternal storageManagerInternal = LocalServices.getService( - StorageManagerInternal.class); - storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName); - } - } finally { - Binder.restoreCallingIdentity(token); - } - } + public void grantRuntimePermission(String packageName, String permName, final int userId) { + mPermissionManager.grantRuntimePermission(permName, packageName, false /*overridePolicy*/, + getCallingUid(), userId, mPermissionCallback); } @Override - public void revokeRuntimePermission(String packageName, String name, int userId) { - revokeRuntimePermission(packageName, name, userId, false /* Only if not fixed by policy */); - } - - private void revokeRuntimePermission(String packageName, String name, int userId, - boolean overridePolicy) { - if (!sUserManager.exists(userId)) { - Log.e(TAG, "No such user:" + userId); - return; - } - - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, - "revokeRuntimePermission"); - - enforceCrossUserPermission(Binder.getCallingUid(), userId, - true /* requireFullPermission */, true /* checkShell */, - "revokeRuntimePermission"); - - final int appId; - - synchronized (mPackages) { - final PackageParser.Package pkg = mPackages.get(packageName); - if (pkg == null) { - throw new IllegalArgumentException("Unknown package: " + packageName); - } - final PackageSetting ps = (PackageSetting) pkg.mExtras; - if (ps == null - || filterAppAccessLPr(ps, Binder.getCallingUid(), userId)) { - throw new IllegalArgumentException("Unknown package: " + packageName); - } - final BasePermission bp = mSettings.mPermissions.get(name); - if (bp == null) { - throw new IllegalArgumentException("Unknown permission: " + name); - } - - enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp); - - // If a permission review is required for legacy apps we represent - // their permissions as always granted runtime ones since we need - // to keep the review required permission flag per user while an - // install permission's state is shared across all users. - if (mPermissionReviewRequired - && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M - && bp.isRuntime()) { - return; - } - - final PermissionsState permissionsState = ps.getPermissionsState(); - - final int flags = permissionsState.getPermissionFlags(name, userId); - if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) { - throw new SecurityException("Cannot revoke system fixed permission " - + name + " for package " + packageName); - } - if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) { - throw new SecurityException("Cannot revoke policy fixed permission " - + name + " for package " + packageName); - } - - if (bp.isDevelopment()) { - // Development permissions must be handled specially, since they are not - // normal runtime permissions. For now they apply to all users. - if (permissionsState.revokeInstallPermission(bp) != - PermissionsState.PERMISSION_OPERATION_FAILURE) { - scheduleWriteSettingsLocked(); - } - return; - } - - if (permissionsState.revokeRuntimePermission(bp, userId) == - PermissionsState.PERMISSION_OPERATION_FAILURE) { - return; - } - - if (bp.isRuntime()) { - logPermissionRevoked(mContext, name, packageName); - } - - mOnPermissionChangeListeners.onPermissionsChanged(pkg.applicationInfo.uid); - - // Critical, after this call app should never have the permission. - mSettings.writeRuntimePermissionsForUserLPr(userId, true); - - appId = UserHandle.getAppId(pkg.applicationInfo.uid); - } - - killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED); + public void revokeRuntimePermission(String packageName, String permName, int userId) { + mPermissionManager.revokeRuntimePermission(permName, packageName, false /*overridePolicy*/, + getCallingUid(), userId, mPermissionCallback); } /** @@ -5943,91 +5386,16 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public int getPermissionFlags(String name, String packageName, int userId) { - if (!sUserManager.exists(userId)) { - return 0; - } - - enforceGrantRevokeRuntimePermissionPermissions("getPermissionFlags"); - - final int callingUid = Binder.getCallingUid(); - enforceCrossUserPermission(callingUid, userId, - true /* requireFullPermission */, false /* checkShell */, - "getPermissionFlags"); - - synchronized (mPackages) { - final PackageParser.Package pkg = mPackages.get(packageName); - if (pkg == null) { - return 0; - } - final BasePermission bp = mSettings.mPermissions.get(name); - if (bp == null) { - return 0; - } - final PackageSetting ps = (PackageSetting) pkg.mExtras; - if (ps == null - || filterAppAccessLPr(ps, callingUid, userId)) { - return 0; - } - PermissionsState permissionsState = ps.getPermissionsState(); - return permissionsState.getPermissionFlags(name, userId); - } + public int getPermissionFlags(String permName, String packageName, int userId) { + return mPermissionManager.getPermissionFlags(permName, packageName, getCallingUid(), userId); } @Override - public void updatePermissionFlags(String name, String packageName, int flagMask, + public void updatePermissionFlags(String permName, String packageName, int flagMask, int flagValues, int userId) { - if (!sUserManager.exists(userId)) { - return; - } - - enforceGrantRevokeRuntimePermissionPermissions("updatePermissionFlags"); - - final int callingUid = Binder.getCallingUid(); - enforceCrossUserPermission(callingUid, userId, - true /* requireFullPermission */, true /* checkShell */, - "updatePermissionFlags"); - - // Only the system can change these flags and nothing else. - if (getCallingUid() != Process.SYSTEM_UID) { - flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; - flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; - flagMask &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; - flagValues &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; - flagValues &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; - } - - synchronized (mPackages) { - final PackageParser.Package pkg = mPackages.get(packageName); - if (pkg == null) { - throw new IllegalArgumentException("Unknown package: " + packageName); - } - final PackageSetting ps = (PackageSetting) pkg.mExtras; - if (ps == null - || filterAppAccessLPr(ps, callingUid, userId)) { - throw new IllegalArgumentException("Unknown package: " + packageName); - } - - final BasePermission bp = mSettings.mPermissions.get(name); - if (bp == null) { - throw new IllegalArgumentException("Unknown permission: " + name); - } - - PermissionsState permissionsState = ps.getPermissionsState(); - - boolean hadState = permissionsState.getRuntimePermissionState(name, userId) != null; - - if (permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues)) { - // Install and runtime permissions are stored in different places, - // so figure out what permission changed and persist the change. - if (permissionsState.getInstallPermissionState(name) != null) { - scheduleWriteSettingsLocked(); - } else if (permissionsState.getRuntimePermissionState(name, userId) != null - || hadState) { - mSettings.writeRuntimePermissionsForUserLPr(userId, false); - } - } - } + mPermissionManager.updatePermissionFlags( + permName, packageName, flagMask, flagValues, getCallingUid(), userId, + mPermissionCallback); } /** @@ -6036,52 +5404,16 @@ public class PackageManagerService extends IPackageManager.Stub */ @Override public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId) { - if (!sUserManager.exists(userId)) { - return; - } - - enforceGrantRevokeRuntimePermissionPermissions("updatePermissionFlagsForAllApps"); - - enforceCrossUserPermission(Binder.getCallingUid(), userId, - true /* requireFullPermission */, true /* checkShell */, - "updatePermissionFlagsForAllApps"); - - // Only the system can change system fixed flags. - if (getCallingUid() != Process.SYSTEM_UID) { - flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; - flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; - } - synchronized (mPackages) { - boolean changed = false; - final int packageCount = mPackages.size(); - for (int pkgIndex = 0; pkgIndex < packageCount; pkgIndex++) { - final PackageParser.Package pkg = mPackages.valueAt(pkgIndex); - final PackageSetting ps = (PackageSetting) pkg.mExtras; - if (ps == null) { - continue; - } - PermissionsState permissionsState = ps.getPermissionsState(); - changed |= permissionsState.updatePermissionFlagsForAllPermissions( - userId, flagMask, flagValues); - } + final boolean changed = mPermissionManager.updatePermissionFlagsForAllApps( + flagMask, flagValues, getCallingUid(), userId, mPackages.values(), + mPermissionCallback); if (changed) { mSettings.writeRuntimePermissionsForUserLPr(userId, false); } } } - private void enforceGrantRevokeRuntimePermissionPermissions(String message) { - if (mContext.checkCallingOrSelfPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS) - != PackageManager.PERMISSION_GRANTED - && mContext.checkCallingOrSelfPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException(message + " requires " - + Manifest.permission.GRANT_RUNTIME_PERMISSIONS + " or " - + Manifest.permission.REVOKE_RUNTIME_PERMISSIONS); - } - } - @Override public boolean shouldShowRequestPermissionRationale(String permissionName, String packageName, int userId) { @@ -6273,7 +5605,7 @@ public class PackageManagerService extends IPackageManager.Stub * <br /> * {@link PackageManager#SIGNATURE_NO_MATCH}: if the two signature sets differ. */ - static int compareSignatures(Signature[] s1, Signature[] s2) { + public static int compareSignatures(Signature[] s1, Signature[] s2) { if (s1 == null) { return s2 == null ? PackageManager.SIGNATURE_NEITHER_SIGNED @@ -6627,9 +5959,14 @@ public class PackageManagerService extends IPackageManager.Stub public ResolveInfo resolveIntent(Intent intent, String resolvedType, int flags, int userId) { return resolveIntentInternal( - intent, resolvedType, flags, userId, false /*includeInstantApps*/); + intent, resolvedType, flags, userId, false /*resolveForStart*/); } + /** + * Normally instant apps can only be resolved when they're visible to the caller. + * However, if {@code resolveForStart} is {@code true}, all instant apps are visible + * since we need to allow the system to start any installed application. + */ private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType, int flags, int userId, boolean resolveForStart) { try { @@ -6638,7 +5975,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!sUserManager.exists(userId)) return null; final int callingUid = Binder.getCallingUid(); flags = updateFlagsForResolve(flags, userId, intent, callingUid, resolveForStart); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "resolve intent"); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities"); @@ -7219,7 +6556,7 @@ public class PackageManagerService extends IPackageManager.Stub boolean resolveForStart, boolean allowDynamicSplits) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final String instantAppPkgName = getInstantAppPackageName(filterCallingUid); - enforceCrossUserPermission(Binder.getCallingUid(), userId, + mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "query intent activities"); final String pkgName = intent.getPackage(); @@ -7974,7 +7311,7 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUid = Binder.getCallingUid(); flags = updateFlagsForResolve(flags, userId, intent, callingUid, false /*includeInstantApps*/); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent activity options"); final String resultsAction = intent.getAction(); @@ -8155,7 +7492,7 @@ public class PackageManagerService extends IPackageManager.Stub String resolvedType, int flags, int userId, boolean allowDynamicSplits) { if (!sUserManager.exists(userId)) return Collections.emptyList(); final int callingUid = Binder.getCallingUid(); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); @@ -8267,7 +7604,7 @@ public class PackageManagerService extends IPackageManager.Stub String resolvedType, int flags, int userId, int callingUid, boolean includeInstantApps) { if (!sUserManager.exists(userId)) return Collections.emptyList(); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "query intent receivers"); final String instantAppPkgName = getInstantAppPackageName(callingUid); @@ -8507,7 +7844,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); flags = updateFlagsForPackage(flags, userId, null); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "get installed packages"); @@ -8594,7 +7931,7 @@ public class PackageManagerService extends IPackageManager.Stub String[] permissions, int flags, int userId) { if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList(); flags = updateFlagsForPackage(flags, userId, permissions); - enforceCrossUserPermission(Binder.getCallingUid(), userId, + mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "get packages holding permissions"); final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; @@ -8699,7 +8036,7 @@ public class PackageManagerService extends IPackageManager.Stub mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS, "getEphemeralApplications"); } - enforceCrossUserPermission(Binder.getCallingUid(), userId, + mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getEphemeralApplications"); synchronized (mPackages) { @@ -8714,7 +8051,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean isInstantApp(String packageName, int userId) { - enforceCrossUserPermission(Binder.getCallingUid(), userId, + mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "isInstantApp"); if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) { @@ -8747,7 +8084,7 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - enforceCrossUserPermission(Binder.getCallingUid(), userId, + mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getInstantAppCookie"); if (!isCallerSameApp(packageName, Binder.getCallingUid())) { @@ -8765,7 +8102,7 @@ public class PackageManagerService extends IPackageManager.Stub return true; } - enforceCrossUserPermission(Binder.getCallingUid(), userId, + mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, true /* checkShell */, "setInstantAppCookie"); if (!isCallerSameApp(packageName, Binder.getCallingUid())) { @@ -8787,7 +8124,7 @@ public class PackageManagerService extends IPackageManager.Stub mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS, "getInstantAppIcon"); } - enforceCrossUserPermission(Binder.getCallingUid(), userId, + mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getInstantAppIcon"); @@ -8847,6 +8184,10 @@ public class PackageManagerService extends IPackageManager.Stub @Override public ProviderInfo resolveContentProvider(String name, int flags, int userId) { + return resolveContentProviderInternal(name, flags, userId); + } + + private ProviderInfo resolveContentProviderInternal(String name, int flags, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForComponent(flags, userId, name); final String instantAppPkgName = getInstantAppPackageName(Binder.getCallingUid()); @@ -9105,7 +8446,7 @@ public class PackageManagerService extends IPackageManager.Stub return fname; } - static void reportSettingsProblem(int priority, String msg) { + public static void reportSettingsProblem(int priority, String msg) { logCriticalInfo(priority, msg); } @@ -9738,7 +9079,7 @@ public class PackageManagerService extends IPackageManager.Stub * and {@code numberOfPackagesFailed}. */ private int[] performDexOptUpgrade(List<PackageParser.Package> pkgs, boolean showDialog, - String compilerFilter, boolean bootComplete) { + final String compilerFilter, boolean bootComplete) { int numberOfPackagesVisited = 0; int numberOfPackagesOptimized = 0; @@ -9749,6 +9090,8 @@ public class PackageManagerService extends IPackageManager.Stub for (PackageParser.Package pkg : pkgs) { numberOfPackagesVisited++; + boolean useProfileForDexopt = false; + if ((isFirstBoot() || isUpgrade()) && isSystemApp(pkg)) { // Copy over initial preopt profiles since we won't get any JIT samples for methods // that are already compiled. @@ -9762,11 +9105,30 @@ public class PackageManagerService extends IPackageManager.Stub if (!mInstaller.copySystemProfile(profileFile.getAbsolutePath(), pkg.applicationInfo.uid, pkg.packageName)) { Log.e(TAG, "Installer failed to copy system profile!"); + } else { + // Disabled as this causes speed-profile compilation during first boot + // even if things are already compiled. + // useProfileForDexopt = true; } } catch (Exception e) { Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ", e); } + } else { + PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName); + // Handle compressed APKs in this path. Only do this for stubs with profiles to + // minimize the number off apps being speed-profile compiled during first boot. + // The other paths will not change the filter. + if (disabledPs != null && disabledPs.pkg.isStub) { + // The package is the stub one, remove the stub suffix to get the normal + // package and APK names. + String systemProfilePath = + getPrebuildProfilePath(disabledPs.pkg).replace(STUB_SUFFIX, ""); + File systemProfile = new File(systemProfilePath); + // Use the profile for compilation if there exists one for the same package + // in the system partition. + useProfileForDexopt = systemProfile.exists(); + } } } @@ -9795,17 +9157,13 @@ public class PackageManagerService extends IPackageManager.Stub } } - // If the OTA updates a system app which was previously preopted to a non-preopted state - // the app might end up being verified at runtime. That's because by default the apps - // are verify-profile but for preopted apps there's no profile. - // Do a hacky check to ensure that if we have no profiles (a reasonable indication - // that before the OTA the app was preopted) the app gets compiled with a non-profile - // filter (by default 'quicken'). - // Note that at this stage unused apps are already filtered. - if (isSystemApp(pkg) && - DexFile.isProfileGuidedCompilerFilter(compilerFilter) && - !Environment.getReferenceProfile(pkg.packageName).exists()) { - compilerFilter = getNonProfileGuidedCompilerFilter(compilerFilter); + String pkgCompilerFilter = compilerFilter; + if (useProfileForDexopt) { + // Use background dexopt mode to try and use the profile. Note that this does not + // guarantee usage of the profile. + pkgCompilerFilter = + PackageManagerServiceCompilerMapping.getCompilerFilterForReason( + PackageManagerService.REASON_BACKGROUND_DEXOPT); } // checkProfiles is false to avoid merging profiles during boot which @@ -9816,22 +9174,9 @@ public class PackageManagerService extends IPackageManager.Stub int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0; int primaryDexOptStaus = performDexOptTraced(new DexoptOptions( pkg.packageName, - compilerFilter, + pkgCompilerFilter, dexoptFlags)); - if (pkg.isSystemApp()) { - // Only dexopt shared secondary dex files belonging to system apps to not slow down - // too much boot after an OTA. - int secondaryDexoptFlags = dexoptFlags | - DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX | - DexoptOptions.DEXOPT_ONLY_SHARED_DEX; - mDexManager.dexoptSecondaryDex(new DexoptOptions( - pkg.packageName, - compilerFilter, - secondaryDexoptFlags)); - } - - // TODO(shubhamajmera): Record secondary dexopt stats. switch (primaryDexOptStaus) { case PackageDexOptimizer.DEX_OPT_PERFORMED: numberOfPackagesOptimized++; @@ -10388,7 +9733,8 @@ public class PackageManagerService extends IPackageManager.Stub } } - private void addSharedLibraryLPr(ArraySet<String> usesLibraryFiles, SharedLibraryEntry file, + private void addSharedLibraryLPr(Set<String> usesLibraryFiles, + SharedLibraryEntry file, PackageParser.Package changingLib) { if (file.path != null) { usesLibraryFiles.add(file.path); @@ -10417,7 +9763,10 @@ public class PackageManagerService extends IPackageManager.Stub if (pkg == null) { return; } - ArraySet<String> usesLibraryFiles = null; + // The collection used here must maintain the order of addition (so + // that libraries are searched in the correct order) and must have no + // duplicates. + Set<String> usesLibraryFiles = null; if (pkg.usesLibraries != null) { usesLibraryFiles = addSharedLibrariesLPw(pkg.usesLibraries, null, null, pkg.packageName, changingLib, true, @@ -10441,10 +9790,10 @@ public class PackageManagerService extends IPackageManager.Stub } } - private ArraySet<String> addSharedLibrariesLPw(@NonNull List<String> requestedLibraries, + private Set<String> addSharedLibrariesLPw(@NonNull List<String> requestedLibraries, @Nullable int[] requiredVersions, @Nullable String[][] requiredCertDigests, @NonNull String packageName, @Nullable PackageParser.Package changingLib, - boolean required, int targetSdk, @Nullable ArraySet<String> outUsedLibraries) + boolean required, int targetSdk, @Nullable Set<String> outUsedLibraries) throws PackageManagerException { final int libCount = requestedLibraries.size(); for (int i = 0; i < libCount; i++) { @@ -10510,7 +9859,9 @@ public class PackageManagerService extends IPackageManager.Stub } if (outUsedLibraries == null) { - outUsedLibraries = new ArraySet<>(); + // Use LinkedHashSet to preserve the order of files added to + // usesLibraryFiles while eliminating duplicates. + outUsedLibraries = new LinkedHashSet<>(); } addSharedLibraryLPr(outUsedLibraries, libEntry, changingLib); } @@ -10703,6 +10054,12 @@ public class PackageManagerService extends IPackageManager.Stub assertPackageIsValid(pkg, policyFlags, scanFlags); + if (Build.IS_DEBUGGABLE && + pkg.isPrivilegedApp() && + !SystemProperties.getBoolean("pm.dexopt.priv-apps", true)) { + PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg); + } + // Initialize package source and resource directories final File scanFile = new File(pkg.codePath); final File destCodeFile = new File(pkg.applicationInfo.getCodePath()); @@ -11866,86 +11223,27 @@ public class PackageManagerService extends IPackageManager.Stub } } - ArrayMap<String, BasePermission> permissionMap = - p.tree ? mSettings.mPermissionTrees - : mSettings.mPermissions; - BasePermission bp = permissionMap.get(p.info.name); - - // Allow system apps to redefine non-system permissions - if (bp != null && !Objects.equals(bp.sourcePackage, p.info.packageName)) { - final boolean currentOwnerIsSystem = (bp.perm != null - && isSystemApp(bp.perm.owner)); - if (isSystemApp(p.owner)) { - if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) { - // It's a built-in permission and no owner, take ownership now - bp.packageSetting = pkgSetting; - bp.perm = p; - bp.uid = pkg.applicationInfo.uid; - bp.sourcePackage = p.info.packageName; - p.info.flags |= PermissionInfo.FLAG_INSTALLED; - } else if (!currentOwnerIsSystem) { - String msg = "New decl " + p.owner + " of permission " - + p.info.name + " is system; overriding " + bp.sourcePackage; - reportSettingsProblem(Log.WARN, msg); - bp = null; - } - } - } - - if (bp == null) { - bp = new BasePermission(p.info.name, p.info.packageName, - BasePermission.TYPE_NORMAL); + // TODO Move to PermissionManager once mPermissionTrees moves there. +// p.tree ? mSettings.mPermissionTrees +// : mSettings.mPermissions; +// final BasePermission bp = BasePermission.createOrUpdate( +// permissionMap.get(p.info.name), p, pkg, mSettings.mPermissionTrees, chatty); +// permissionMap.put(p.info.name, bp); + if (p.tree) { + final ArrayMap<String, BasePermission> permissionMap = + mSettings.mPermissionTrees; + final BasePermission bp = BasePermission.createOrUpdate( + permissionMap.get(p.info.name), p, pkg, mSettings.mPermissionTrees, + chatty); permissionMap.put(p.info.name, bp); - } - - if (bp.perm == null) { - if (bp.sourcePackage == null - || bp.sourcePackage.equals(p.info.packageName)) { - BasePermission tree = findPermissionTreeLP(p.info.name); - if (tree == null - || tree.sourcePackage.equals(p.info.packageName)) { - bp.packageSetting = pkgSetting; - bp.perm = p; - bp.uid = pkg.applicationInfo.uid; - bp.sourcePackage = p.info.packageName; - p.info.flags |= PermissionInfo.FLAG_INSTALLED; - if (chatty) { - if (r == null) { - r = new StringBuilder(256); - } else { - r.append(' '); - } - r.append(p.info.name); - } - } else { - Slog.w(TAG, "Permission " + p.info.name + " from package " - + p.info.packageName + " ignored: base tree " - + tree.name + " is from package " - + tree.sourcePackage); - } - } else { - Slog.w(TAG, "Permission " + p.info.name + " from package " - + p.info.packageName + " ignored: original from " - + bp.sourcePackage); - } - } else if (chatty) { - if (r == null) { - r = new StringBuilder(256); - } else { - r.append(' '); - } - r.append("DUP:"); - r.append(p.info.name); - } - if (bp.perm == p) { - bp.protectionLevel = p.info.protectionLevel; + } else { + final BasePermission bp = BasePermission.createOrUpdate( + (BasePermission) mPermissionManager.getPermissionTEMP(p.info.name), + p, pkg, mSettings.mPermissionTrees, chatty); + mPermissionManager.putPermissionTEMP(p.info.name, bp); } } - if (r != null) { - if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Permissions: " + r); - } - N = pkg.instrumentation.size(); r = null; for (i=0; i<N; i++) { @@ -12673,12 +11971,12 @@ public class PackageManagerService extends IPackageManager.Stub r = null; for (i=0; i<N; i++) { PackageParser.Permission p = pkg.permissions.get(i); - BasePermission bp = mSettings.mPermissions.get(p.info.name); + BasePermission bp = (BasePermission) mPermissionManager.getPermissionTEMP(p.info.name); if (bp == null) { bp = mSettings.mPermissionTrees.get(p.info.name); } - if (bp != null && bp.perm == p) { - bp.perm = null; + if (bp != null && bp.isPermission(p)) { + bp.setPermission(null); if (DEBUG_REMOVE && chatty) { if (r == null) { r = new StringBuilder(256); @@ -12703,8 +12001,7 @@ public class PackageManagerService extends IPackageManager.Stub r = null; for (i=0; i<N; i++) { String perm = pkg.requestedPermissions.get(i); - BasePermission bp = mSettings.mPermissions.get(perm); - if (bp != null && (bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) { + if (mPermissionManager.isPermissionAppOp(perm)) { ArraySet<String> appOpPkgs = mAppOpPermissionPackages.get(perm); if (appOpPkgs != null) { appOpPkgs.remove(pkg.packageName); @@ -12809,23 +12106,32 @@ public class PackageManagerService extends IPackageManager.Stub private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo, String replaceVolumeUuid, int flags) { + // TODO: Most of the methods exposing BasePermission internals [source package name, + // etc..] shouldn't be needed. Instead, when we've parsed a permission that doesn't + // have package settings, we should make note of it elsewhere [map between + // source package name and BasePermission] and cycle through that here. Then we + // define a single method on BasePermission that takes a PackageSetting, changing + // package name and a package. + // NOTE: With this approach, we also don't need to tree trees differently than + // normal permissions. Today, we need two separate loops because these BasePermission + // objects are stored separately. // Make sure there are no dangling permission trees. Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator(); while (it.hasNext()) { final BasePermission bp = it.next(); - if (bp.packageSetting == null) { + if (bp.getSourcePackageSetting() == null) { // We may not yet have parsed the package, so just see if // we still know about its settings. - bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage); + bp.setSourcePackageSetting(mSettings.mPackages.get(bp.getSourcePackageName())); } - if (bp.packageSetting == null) { - Slog.w(TAG, "Removing dangling permission tree: " + bp.name - + " from package " + bp.sourcePackage); + if (bp.getSourcePackageSetting() == null) { + Slog.w(TAG, "Removing dangling permission tree: " + bp.getName() + + " from package " + bp.getSourcePackageName()); it.remove(); - } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) { - if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) { - Slog.i(TAG, "Removing old permission tree: " + bp.name - + " from package " + bp.sourcePackage); + } else if (changingPkg != null && changingPkg.equals(bp.getSourcePackageName())) { + if (pkgInfo == null || !hasPermission(pkgInfo, bp.getName())) { + Slog.i(TAG, "Removing old permission tree: " + bp.getName() + + " from package " + bp.getSourcePackageName()); flags |= UPDATE_PERMISSIONS_ALL; it.remove(); } @@ -12834,40 +12140,28 @@ public class PackageManagerService extends IPackageManager.Stub // Make sure all dynamic permissions have been assigned to a package, // and make sure there are no dangling permissions. - it = mSettings.mPermissions.values().iterator(); - while (it.hasNext()) { - final BasePermission bp = it.next(); - if (bp.type == BasePermission.TYPE_DYNAMIC) { - if (DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name=" - + bp.name + " pkg=" + bp.sourcePackage - + " info=" + bp.pendingInfo); - if (bp.packageSetting == null && bp.pendingInfo != null) { - final BasePermission tree = findPermissionTreeLP(bp.name); - if (tree != null && tree.perm != null) { - bp.packageSetting = tree.packageSetting; - bp.perm = new PackageParser.Permission(tree.perm.owner, - new PermissionInfo(bp.pendingInfo)); - bp.perm.info.packageName = tree.perm.info.packageName; - bp.perm.info.name = bp.name; - bp.uid = tree.uid; - } - } - } - if (bp.packageSetting == null) { + final Iterator<BasePermission> permissionIter = + mPermissionManager.getPermissionIteratorTEMP(); + while (permissionIter.hasNext()) { + final BasePermission bp = permissionIter.next(); + if (bp.isDynamic()) { + bp.updateDynamicPermission(mSettings.mPermissionTrees); + } + if (bp.getSourcePackageSetting() == null) { // We may not yet have parsed the package, so just see if // we still know about its settings. - bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage); - } - if (bp.packageSetting == null) { - Slog.w(TAG, "Removing dangling permission: " + bp.name - + " from package " + bp.sourcePackage); - it.remove(); - } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) { - if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) { - Slog.i(TAG, "Removing old permission: " + bp.name - + " from package " + bp.sourcePackage); + bp.setSourcePackageSetting(mSettings.mPackages.get(bp.getSourcePackageName())); + } + if (bp.getSourcePackageSetting() == null) { + Slog.w(TAG, "Removing dangling permission: " + bp.getName() + + " from package " + bp.getSourcePackageName()); + permissionIter.remove(); + } else if (changingPkg != null && changingPkg.equals(bp.getSourcePackageName())) { + if (pkgInfo == null || !hasPermission(pkgInfo, bp.getName())) { + Slog.i(TAG, "Removing old permission: " + bp.getName() + + " from package " + bp.getSourcePackageName()); flags |= UPDATE_PERMISSIONS_ALL; - it.remove(); + permissionIter.remove(); } } } @@ -12936,8 +12230,9 @@ public class PackageManagerService extends IPackageManager.Stub // the runtime ones are written only if changed. The only cases of // changed runtime permissions here are promotion of an install to // runtime and revocation of a runtime from a shared user. - changedRuntimePermissionUserIds = revokeUnusedSharedUserPermissionsLPw( - ps.sharedUser, UserManagerService.getInstance().getUserIds()); + changedRuntimePermissionUserIds = + mPermissionManager.revokeUnusedSharedUserPermissions( + ps.sharedUser, UserManagerService.getInstance().getUserIds()); if (!ArrayUtils.isEmpty(changedRuntimePermissionUserIds)) { runtimePermissionsRevoked = true; } @@ -12949,7 +12244,7 @@ public class PackageManagerService extends IPackageManager.Stub final int N = pkg.requestedPermissions.size(); for (int i=0; i<N; i++) { final String name = pkg.requestedPermissions.get(i); - final BasePermission bp = mSettings.mPermissions.get(name); + final BasePermission bp = (BasePermission) mPermissionManager.getPermissionTEMP(name); final boolean appSupportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M; @@ -12957,7 +12252,7 @@ public class PackageManagerService extends IPackageManager.Stub Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp); } - if (bp == null || bp.packageSetting == null) { + if (bp == null || bp.getSourcePackageSetting() == null) { if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) { if (DEBUG_PERMISSIONS) { Slog.i(TAG, "Unknown permission " + name @@ -12971,7 +12266,7 @@ public class PackageManagerService extends IPackageManager.Stub // Limit ephemeral apps to ephemeral allowed permissions. if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) { if (DEBUG_PERMISSIONS) { - Log.i(TAG, "Denying non-ephemeral permission " + bp.name + " for package " + Log.i(TAG, "Denying non-ephemeral permission " + bp.getName() + " for package " + pkg.packageName); } continue; @@ -12979,64 +12274,57 @@ public class PackageManagerService extends IPackageManager.Stub if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) { if (DEBUG_PERMISSIONS) { - Log.i(TAG, "Denying runtime-only permission " + bp.name + " for package " + Log.i(TAG, "Denying runtime-only permission " + bp.getName() + " for package " + pkg.packageName); } continue; } - final String perm = bp.name; + final String perm = bp.getName(); boolean allowedSig = false; int grant = GRANT_DENIED; // Keep track of app op permissions. - if ((bp.protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0) { - ArraySet<String> pkgs = mAppOpPermissionPackages.get(bp.name); + if (bp.isAppOp()) { + ArraySet<String> pkgs = mAppOpPermissionPackages.get(perm); if (pkgs == null) { pkgs = new ArraySet<>(); - mAppOpPermissionPackages.put(bp.name, pkgs); + mAppOpPermissionPackages.put(perm, pkgs); } pkgs.add(pkg.packageName); } - final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE; - switch (level) { - case PermissionInfo.PROTECTION_NORMAL: { - // For all apps normal permissions are install time ones. + if (bp.isNormal()) { + // For all apps normal permissions are install time ones. + grant = GRANT_INSTALL; + } else if (bp.isRuntime()) { + // If a permission review is required for legacy apps we represent + // their permissions as always granted runtime ones since we need + // to keep the review required permission flag per user while an + // install permission's state is shared across all users. + if (!appSupportsRuntimePermissions && !mPermissionReviewRequired) { + // For legacy apps dangerous permissions are install time ones. grant = GRANT_INSTALL; - } break; - - case PermissionInfo.PROTECTION_DANGEROUS: { - // If a permission review is required for legacy apps we represent - // their permissions as always granted runtime ones since we need - // to keep the review required permission flag per user while an - // install permission's state is shared across all users. - if (!appSupportsRuntimePermissions && !mPermissionReviewRequired) { - // For legacy apps dangerous permissions are install time ones. - grant = GRANT_INSTALL; - } else if (origPermissions.hasInstallPermission(bp.name)) { - // For legacy apps that became modern, install becomes runtime. - grant = GRANT_UPGRADE; - } else if (mPromoteSystemApps - && isSystemApp(ps) - && mExistingSystemPackages.contains(ps.name)) { - // For legacy system apps, install becomes runtime. - // We cannot check hasInstallPermission() for system apps since those - // permissions were granted implicitly and not persisted pre-M. - grant = GRANT_UPGRADE; - } else { - // For modern apps keep runtime permissions unchanged. - grant = GRANT_RUNTIME; - } - } break; - - case PermissionInfo.PROTECTION_SIGNATURE: { - // For all apps signature permissions are install time ones. - allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions); - if (allowedSig) { - grant = GRANT_INSTALL; - } - } break; + } else if (origPermissions.hasInstallPermission(bp.getName())) { + // For legacy apps that became modern, install becomes runtime. + grant = GRANT_UPGRADE; + } else if (mPromoteSystemApps + && isSystemApp(ps) + && mExistingSystemPackages.contains(ps.name)) { + // For legacy system apps, install becomes runtime. + // We cannot check hasInstallPermission() for system apps since those + // permissions were granted implicitly and not persisted pre-M. + grant = GRANT_UPGRADE; + } else { + // For modern apps keep runtime permissions unchanged. + grant = GRANT_RUNTIME; + } + } else if (bp.isSignature()) { + // For all apps signature permissions are install time ones. + allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions); + if (allowedSig) { + grant = GRANT_INSTALL; + } } if (DEBUG_PERMISSIONS) { @@ -13065,7 +12353,7 @@ public class PackageManagerService extends IPackageManager.Stub // for legacy apps for (int userId : UserManagerService.getInstance().getUserIds()) { if (origPermissions.getRuntimePermissionState( - bp.name, userId) != null) { + perm, userId) != null) { // Revoke the runtime permission and clear the flags. origPermissions.revokeRuntimePermission(bp, userId); origPermissions.updatePermissionFlags(bp, userId, @@ -13086,10 +12374,10 @@ public class PackageManagerService extends IPackageManager.Stub // Grant previously granted runtime permissions. for (int userId : UserManagerService.getInstance().getUserIds()) { PermissionState permissionState = origPermissions - .getRuntimePermissionState(bp.name, userId); + .getRuntimePermissionState(perm, userId); int flags = permissionState != null ? permissionState.getFlags() : 0; - if (origPermissions.hasRuntimePermission(bp.name, userId)) { + if (origPermissions.hasRuntimePermission(perm, userId)) { // Don't propagate the permission in a permission review mode if // the former was revoked, i.e. marked to not propagate on upgrade. // Note that in a permission review mode install permissions are @@ -13132,7 +12420,7 @@ public class PackageManagerService extends IPackageManager.Stub // permissions as these are the only ones the platform knows // how to disable the API to simulate revocation as legacy // apps don't expect to run with revoked permissions. - if (PLATFORM_PACKAGE_NAME.equals(bp.sourcePackage)) { + if (PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName())) { if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) { flags |= FLAG_PERMISSION_REVIEW_REQUIRED; // We changed the flags, hence have to write. @@ -13155,7 +12443,7 @@ public class PackageManagerService extends IPackageManager.Stub case GRANT_UPGRADE: { // Grant runtime permissions for a previously held install permission. PermissionState permissionState = origPermissions - .getInstallPermissionState(bp.name); + .getInstallPermissionState(perm); final int flags = permissionState != null ? permissionState.getFlags() : 0; if (origPermissions.revokeInstallPermission(bp) @@ -13203,10 +12491,10 @@ public class PackageManagerService extends IPackageManager.Stub changedInstallPermission = true; Slog.i(TAG, "Un-granting permission " + perm + " from package " + pkg.packageName - + " (protectionLevel=" + bp.protectionLevel + + " (protectionLevel=" + bp.getProtectionLevel() + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags) + ")"); - } else if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) == 0) { + } else if (bp.isAppOp()) { // Don't print warning for app op permissions, since it is fine for them // not to be granted, there is a UI for the user to decide. if (DEBUG_PERMISSIONS @@ -13214,7 +12502,7 @@ public class PackageManagerService extends IPackageManager.Stub || packageOfInterest.equals(pkg.packageName))) { Slog.i(TAG, "Not granting permission " + perm + " to package " + pkg.packageName - + " (protectionLevel=" + bp.protectionLevel + + " (protectionLevel=" + bp.getProtectionLevel() + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags) + ")"); } @@ -13274,13 +12562,11 @@ public class PackageManagerService extends IPackageManager.Stub private boolean grantSignaturePermission(String perm, PackageParser.Package pkg, BasePermission bp, PermissionsState origPermissions) { - boolean oemPermission = (bp.protectionLevel - & PermissionInfo.PROTECTION_FLAG_OEM) != 0; - boolean privilegedPermission = (bp.protectionLevel - & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0; + boolean oemPermission = bp.isOEM(); + boolean privilegedPermission = bp.isPrivileged(); boolean privappPermissionsDisable = RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE; - boolean platformPermission = PLATFORM_PACKAGE_NAME.equals(bp.sourcePackage); + boolean platformPermission = PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName()); boolean platformPackage = PLATFORM_PACKAGE_NAME.equals(pkg.packageName); if (!privappPermissionsDisable && privilegedPermission && pkg.isPrivilegedApp() && !platformPackage && platformPermission) { @@ -13309,7 +12595,7 @@ public class PackageManagerService extends IPackageManager.Stub } } boolean allowed = (compareSignatures( - bp.packageSetting.signatures.mSignatures, pkg.mSignatures) + bp.getSourcePackageSetting().signatures.mSignatures, pkg.mSignatures) == PackageManager.SIGNATURE_MATCH) || (compareSignatures(mPlatformPackage.mSignatures, pkg.mSignatures) == PackageManager.SIGNATURE_MATCH); @@ -13386,39 +12672,37 @@ public class PackageManagerService extends IPackageManager.Stub } } if (!allowed) { - if (!allowed && (bp.protectionLevel - & PermissionInfo.PROTECTION_FLAG_PRE23) != 0 + if (!allowed + && bp.isPre23() && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { // If this was a previously normal/dangerous permission that got moved // to a system permission as part of the runtime permission redesign, then // we still want to blindly grant it to old apps. allowed = true; } - if (!allowed && (bp.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0 + if (!allowed && bp.isInstaller() && pkg.packageName.equals(mRequiredInstallerPackage)) { // If this permission is to be granted to the system installer and // this app is an installer, then it gets the permission. allowed = true; } - if (!allowed && (bp.protectionLevel & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0 + if (!allowed && bp.isVerifier() && pkg.packageName.equals(mRequiredVerifierPackage)) { // If this permission is to be granted to the system verifier and // this app is a verifier, then it gets the permission. allowed = true; } - if (!allowed && (bp.protectionLevel - & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0 + if (!allowed && bp.isPreInstalled() && isSystemApp(pkg)) { // Any pre-installed system app is allowed to get this permission. allowed = true; } - if (!allowed && (bp.protectionLevel - & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) { + if (!allowed && bp.isDevelopment()) { // For development permissions, a development permission // is granted only if it was already granted. allowed = origPermissions.hasInstallPermission(perm); } - if (!allowed && (bp.protectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0 + if (!allowed && bp.isSetup() && pkg.packageName.equals(mSetupWizardPackage)) { // If this permission is to be granted to the system setup wizard and // this app is a setup wizard, then it gets the permission. @@ -14719,7 +14003,7 @@ public class PackageManagerService extends IPackageManager.Stub mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null); final int callingUid = Binder.getCallingUid(); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, true /* checkShell */, "installPackageAsUser"); if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) { @@ -14847,7 +14131,7 @@ public class PackageManagerService extends IPackageManager.Stub return installReason; } - void installStage(String packageName, File stagedDir, String stagedCid, + void installStage(String packageName, File stagedDir, IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams, String installerPackageName, int installerUid, UserHandle user, Certificate[][] certificates) { @@ -14860,12 +14144,7 @@ public class PackageManagerService extends IPackageManager.Stub sessionParams.originatingUri, sessionParams.referrerUri, sessionParams.originatingUid, installerUid); - final OriginInfo origin; - if (stagedDir != null) { - origin = OriginInfo.fromStagedFile(stagedDir); - } else { - origin = OriginInfo.fromStagedContainer(stagedCid); - } + final OriginInfo origin = OriginInfo.fromStagedFile(stagedDir); final Message msg = mHandler.obtainMessage(INIT_COPY); final int installReason = fixUpInstallReason(installerPackageName, installerUid, @@ -14963,7 +14242,7 @@ public class PackageManagerService extends IPackageManager.Stub mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null); PackageSetting pkgSetting; final int callingUid = Binder.getCallingUid(); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, true /* checkShell */, "setApplicationHiddenSetting for user " + userId); @@ -15065,7 +14344,7 @@ public class PackageManagerService extends IPackageManager.Stub public boolean getApplicationHiddenSettingAsUser(String packageName, int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null); final int callingUid = Binder.getCallingUid(); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "getApplicationHidden for user " + userId); PackageSetting ps; @@ -15097,7 +14376,7 @@ public class PackageManagerService extends IPackageManager.Stub null); PackageSetting pkgSetting; final int callingUid = Binder.getCallingUid(); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, true /* checkShell */, "installExistingPackage for user " + userId); if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) { @@ -15202,7 +14481,7 @@ public class PackageManagerService extends IPackageManager.Stub int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null); final int callingUid = Binder.getCallingUid(); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, true /* checkShell */, "setPackagesSuspended for user " + userId); @@ -15263,7 +14542,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean isPackageSuspendedForUser(String packageName, int userId) { final int callingUid = Binder.getCallingUid(); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "isPackageSuspendedForUser for user " + userId); synchronized (mPackages) { @@ -15710,7 +14989,7 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mPackages) { boolean result = mSettings.setDefaultBrowserPackageNameLPw(packageName, userId); if (packageName != null) { - mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultBrowserLPr( + mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultBrowser( packageName, userId); } return result; @@ -16065,7 +15344,6 @@ public class PackageManagerService extends IPackageManager.Stub * file, or a cluster directory. This location may be untrusted. */ final File file; - final String cid; /** * Flag indicating that {@link #file} or {@link #cid} has already been @@ -16084,35 +15362,27 @@ public class PackageManagerService extends IPackageManager.Stub final File resolvedFile; static OriginInfo fromNothing() { - return new OriginInfo(null, null, false, false); + return new OriginInfo(null, false, false); } static OriginInfo fromUntrustedFile(File file) { - return new OriginInfo(file, null, false, false); + return new OriginInfo(file, false, false); } static OriginInfo fromExistingFile(File file) { - return new OriginInfo(file, null, false, true); + return new OriginInfo(file, false, true); } static OriginInfo fromStagedFile(File file) { - return new OriginInfo(file, null, true, false); + return new OriginInfo(file, true, false); } - static OriginInfo fromStagedContainer(String cid) { - return new OriginInfo(null, cid, true, false); - } - - private OriginInfo(File file, String cid, boolean staged, boolean existing) { + private OriginInfo(File file, boolean staged, boolean existing) { this.file = file; - this.cid = cid; this.staged = staged; this.existing = existing; - if (cid != null) { - resolvedPath = PackageHelper.getSdDir(cid); - resolvedFile = new File(resolvedPath); - } else if (file != null) { + if (file != null) { resolvedPath = file.getAbsolutePath(); resolvedFile = file; } else { @@ -16205,7 +15475,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public String toString() { return "InstallParams{" + Integer.toHexString(System.identityHashCode(this)) - + " file=" + origin.file + " cid=" + origin.cid + "}"; + + " file=" + origin.file + "}"; } private int installLocationPolicy(PackageInfoLite pkgLite) { @@ -16316,9 +15586,6 @@ public class PackageManagerService extends IPackageManager.Stub if (origin.file != null) { installFlags |= PackageManager.INSTALL_INTERNAL; installFlags &= ~PackageManager.INSTALL_EXTERNAL; - } else if (origin.cid != null) { - installFlags |= PackageManager.INSTALL_EXTERNAL; - installFlags &= ~PackageManager.INSTALL_INTERNAL; } else { throw new IllegalStateException("Invalid stage location"); } @@ -16356,7 +15623,7 @@ public class PackageManagerService extends IPackageManager.Stub Environment.getDataDirectory()); final long sizeBytes = mContainerService.calculateInstalledSize( - origin.resolvedPath, isForwardLocked(), packageAbiOverride); + origin.resolvedPath, packageAbiOverride); try { mInstaller.freeCache(null, sizeBytes + lowThreshold, 0, 0); @@ -16593,43 +15860,11 @@ public class PackageManagerService extends IPackageManager.Stub mArgs = createInstallArgs(this); mRet = PackageManager.INSTALL_FAILED_INTERNAL_ERROR; } - - public boolean isForwardLocked() { - return (installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0; - } - } - - /** - * Used during creation of InstallArgs - * - * @param installFlags package installation flags - * @return true if should be installed on external storage - */ - private static boolean installOnExternalAsec(int installFlags) { - if ((installFlags & PackageManager.INSTALL_INTERNAL) != 0) { - return false; - } - if ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0) { - return true; - } - return false; - } - - /** - * Used during creation of InstallArgs - * - * @param installFlags package installation flags - * @return true if should be installed as forward locked - */ - private static boolean installForwardLocked(int installFlags) { - return (installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0; } private InstallArgs createInstallArgs(InstallParams params) { if (params.move != null) { return new MoveInstallArgs(params); - } else if (installOnExternalAsec(params.installFlags) || params.isForwardLocked()) { - return new AsecInstallArgs(params); } else { return new FileInstallArgs(params); } @@ -16641,27 +15876,7 @@ public class PackageManagerService extends IPackageManager.Stub */ private InstallArgs createInstallArgsForExisting(int installFlags, String codePath, String resourcePath, String[] instructionSets) { - final boolean isInAsec; - if (installOnExternalAsec(installFlags)) { - /* Apps on SD card are always in ASEC containers. */ - isInAsec = true; - } else if (installForwardLocked(installFlags) - && !codePath.startsWith(mDrmAppPrivateInstallDir.getAbsolutePath())) { - /* - * Forward-locked apps are only in ASEC containers if they're the - * new style - */ - isInAsec = true; - } else { - isInAsec = false; - } - - if (isInAsec) { - return new AsecInstallArgs(codePath, instructionSets, - installOnExternalAsec(installFlags), installForwardLocked(installFlags)); - } else { - return new FileInstallArgs(codePath, resourcePath, instructionSets); - } + return new FileInstallArgs(codePath, resourcePath, instructionSets); } static abstract class InstallArgs { @@ -16995,11 +16210,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - private boolean isAsecExternal(String cid) { - final String asecPath = PackageHelper.getSdFilesystem(cid); - return !asecPath.startsWith(mAsecInternalPath); - } - private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet) throws PackageManagerException { if (copyRet < 0) { @@ -17022,308 +16232,6 @@ public class PackageManagerService extends IPackageManager.Stub } /** - * Logic to handle installation of ASEC applications, including copying and - * renaming logic. - */ - class AsecInstallArgs extends InstallArgs { - static final String RES_FILE_NAME = "pkg.apk"; - static final String PUBLIC_RES_FILE_NAME = "res.zip"; - - String cid; - String packagePath; - String resourcePath; - - /** New install */ - AsecInstallArgs(InstallParams params) { - super(params.origin, params.move, params.observer, params.installFlags, - params.installerPackageName, params.volumeUuid, - params.getUser(), null /* instruction sets */, params.packageAbiOverride, - params.grantedRuntimePermissions, - params.traceMethod, params.traceCookie, params.certificates, - params.installReason); - } - - /** Existing install */ - AsecInstallArgs(String fullCodePath, String[] instructionSets, - boolean isExternal, boolean isForwardLocked) { - super(OriginInfo.fromNothing(), null, null, (isExternal ? INSTALL_EXTERNAL : 0) - | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, - instructionSets, null, null, null, 0, null /*certificates*/, - PackageManager.INSTALL_REASON_UNKNOWN); - // Hackily pretend we're still looking at a full code path - if (!fullCodePath.endsWith(RES_FILE_NAME)) { - fullCodePath = new File(fullCodePath, RES_FILE_NAME).getAbsolutePath(); - } - - // Extract cid from fullCodePath - int eidx = fullCodePath.lastIndexOf("/"); - String subStr1 = fullCodePath.substring(0, eidx); - int sidx = subStr1.lastIndexOf("/"); - cid = subStr1.substring(sidx+1, eidx); - setMountPath(subStr1); - } - - AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked) { - super(OriginInfo.fromNothing(), null, null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0) - | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, - instructionSets, null, null, null, 0, null /*certificates*/, - PackageManager.INSTALL_REASON_UNKNOWN); - this.cid = cid; - setMountPath(PackageHelper.getSdDir(cid)); - } - - void createCopyFile() { - cid = mInstallerService.allocateExternalStageCidLegacy(); - } - - int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { - if (origin.staged && origin.cid != null) { - if (DEBUG_INSTALL) Slog.d(TAG, origin.cid + " already staged; skipping copy"); - cid = origin.cid; - setMountPath(PackageHelper.getSdDir(cid)); - return PackageManager.INSTALL_SUCCEEDED; - } - - if (temp) { - createCopyFile(); - } else { - /* - * Pre-emptively destroy the container since it's destroyed if - * copying fails due to it existing anyway. - */ - PackageHelper.destroySdDir(cid); - } - - final String newMountPath = imcs.copyPackageToContainer( - origin.file.getAbsolutePath(), cid, getEncryptKey(), isExternalAsec(), - isFwdLocked(), deriveAbiOverride(abiOverride, null /* settings */)); - - if (newMountPath != null) { - setMountPath(newMountPath); - return PackageManager.INSTALL_SUCCEEDED; - } else { - return PackageManager.INSTALL_FAILED_CONTAINER_ERROR; - } - } - - @Override - String getCodePath() { - return packagePath; - } - - @Override - String getResourcePath() { - return resourcePath; - } - - int doPreInstall(int status) { - if (status != PackageManager.INSTALL_SUCCEEDED) { - // Destroy container - PackageHelper.destroySdDir(cid); - } else { - boolean mounted = PackageHelper.isContainerMounted(cid); - if (!mounted) { - String newMountPath = PackageHelper.mountSdDir(cid, getEncryptKey(), - Process.SYSTEM_UID); - if (newMountPath != null) { - setMountPath(newMountPath); - } else { - return PackageManager.INSTALL_FAILED_CONTAINER_ERROR; - } - } - } - return status; - } - - boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) { - String newCacheId = getNextCodePath(oldCodePath, pkg.packageName, "/" + RES_FILE_NAME); - String newMountPath = null; - if (PackageHelper.isContainerMounted(cid)) { - // Unmount the container - if (!PackageHelper.unMountSdDir(cid)) { - Slog.i(TAG, "Failed to unmount " + cid + " before renaming"); - return false; - } - } - if (!PackageHelper.renameSdDir(cid, newCacheId)) { - Slog.e(TAG, "Failed to rename " + cid + " to " + newCacheId + - " which might be stale. Will try to clean up."); - // Clean up the stale container and proceed to recreate. - if (!PackageHelper.destroySdDir(newCacheId)) { - Slog.e(TAG, "Very strange. Cannot clean up stale container " + newCacheId); - return false; - } - // Successfully cleaned up stale container. Try to rename again. - if (!PackageHelper.renameSdDir(cid, newCacheId)) { - Slog.e(TAG, "Failed to rename " + cid + " to " + newCacheId - + " inspite of cleaning it up."); - return false; - } - } - if (!PackageHelper.isContainerMounted(newCacheId)) { - Slog.w(TAG, "Mounting container " + newCacheId); - newMountPath = PackageHelper.mountSdDir(newCacheId, - getEncryptKey(), Process.SYSTEM_UID); - } else { - newMountPath = PackageHelper.getSdDir(newCacheId); - } - if (newMountPath == null) { - Slog.w(TAG, "Failed to get cache path for " + newCacheId); - return false; - } - Log.i(TAG, "Succesfully renamed " + cid + - " to " + newCacheId + - " at new path: " + newMountPath); - cid = newCacheId; - - final File beforeCodeFile = new File(packagePath); - setMountPath(newMountPath); - final File afterCodeFile = new File(packagePath); - - // Reflect the rename in scanned details - pkg.setCodePath(afterCodeFile.getAbsolutePath()); - pkg.setBaseCodePath(FileUtils.rewriteAfterRename(beforeCodeFile, - afterCodeFile, pkg.baseCodePath)); - pkg.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile, - afterCodeFile, pkg.splitCodePaths)); - - // Reflect the rename in app info - pkg.setApplicationVolumeUuid(pkg.volumeUuid); - pkg.setApplicationInfoCodePath(pkg.codePath); - pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath); - pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths); - pkg.setApplicationInfoResourcePath(pkg.codePath); - pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath); - pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths); - - return true; - } - - private void setMountPath(String mountPath) { - final File mountFile = new File(mountPath); - - final File monolithicFile = new File(mountFile, RES_FILE_NAME); - if (monolithicFile.exists()) { - packagePath = monolithicFile.getAbsolutePath(); - if (isFwdLocked()) { - resourcePath = new File(mountFile, PUBLIC_RES_FILE_NAME).getAbsolutePath(); - } else { - resourcePath = packagePath; - } - } else { - packagePath = mountFile.getAbsolutePath(); - resourcePath = packagePath; - } - } - - int doPostInstall(int status, int uid) { - if (status != PackageManager.INSTALL_SUCCEEDED) { - cleanUp(); - } else { - final int groupOwner; - final String protectedFile; - if (isFwdLocked()) { - groupOwner = UserHandle.getSharedAppGid(uid); - protectedFile = RES_FILE_NAME; - } else { - groupOwner = -1; - protectedFile = null; - } - - if (uid < Process.FIRST_APPLICATION_UID - || !PackageHelper.fixSdPermissions(cid, groupOwner, protectedFile)) { - Slog.e(TAG, "Failed to finalize " + cid); - PackageHelper.destroySdDir(cid); - return PackageManager.INSTALL_FAILED_CONTAINER_ERROR; - } - - boolean mounted = PackageHelper.isContainerMounted(cid); - if (!mounted) { - PackageHelper.mountSdDir(cid, getEncryptKey(), Process.myUid()); - } - } - return status; - } - - private void cleanUp() { - if (DEBUG_SD_INSTALL) Slog.i(TAG, "cleanUp"); - - // Destroy secure container - PackageHelper.destroySdDir(cid); - } - - private List<String> getAllCodePaths() { - final File codeFile = new File(getCodePath()); - if (codeFile != null && codeFile.exists()) { - try { - final PackageLite pkg = PackageParser.parsePackageLite(codeFile, 0); - return pkg.getAllCodePaths(); - } catch (PackageParserException e) { - // Ignored; we tried our best - } - } - return Collections.EMPTY_LIST; - } - - void cleanUpResourcesLI() { - // Enumerate all code paths before deleting - cleanUpResourcesLI(getAllCodePaths()); - } - - private void cleanUpResourcesLI(List<String> allCodePaths) { - cleanUp(); - removeDexFiles(allCodePaths, instructionSets); - } - - String getPackageName() { - return getAsecPackageName(cid); - } - - boolean doPostDeleteLI(boolean delete) { - if (DEBUG_SD_INSTALL) Slog.i(TAG, "doPostDeleteLI() del=" + delete); - final List<String> allCodePaths = getAllCodePaths(); - boolean mounted = PackageHelper.isContainerMounted(cid); - if (mounted) { - // Unmount first - if (PackageHelper.unMountSdDir(cid)) { - mounted = false; - } - } - if (!mounted && delete) { - cleanUpResourcesLI(allCodePaths); - } - return !mounted; - } - - @Override - int doPreCopy() { - if (isFwdLocked()) { - if (!PackageHelper.fixSdPermissions(cid, getPackageUid(DEFAULT_CONTAINER_PACKAGE, - MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM), RES_FILE_NAME)) { - return PackageManager.INSTALL_FAILED_CONTAINER_ERROR; - } - } - - return PackageManager.INSTALL_SUCCEEDED; - } - - @Override - int doPostCopy(int uid) { - if (isFwdLocked()) { - if (uid < Process.FIRST_APPLICATION_UID - || !PackageHelper.fixSdPermissions(cid, UserHandle.getSharedAppGid(uid), - RES_FILE_NAME)) { - Slog.e(TAG, "Failed to finalize " + cid); - PackageHelper.destroySdDir(cid); - return PackageManager.INSTALL_FAILED_CONTAINER_ERROR; - } - } - - return PackageManager.INSTALL_SUCCEEDED; - } - } - - /** * Logic to handle movement of existing installed applications. */ class MoveInstallArgs extends InstallArgs { @@ -17627,9 +16535,9 @@ public class PackageManagerService extends IPackageManager.Stub Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } - private boolean shouldCheckUpgradeKeySetLP(PackageSetting oldPs, int scanFlags) { + private boolean shouldCheckUpgradeKeySetLP(PackageSettingBase oldPs, int scanFlags) { // Can't rotate keys during boot or if sharedUser. - if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || oldPs.sharedUser != null + if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || oldPs.isSharedUser() || !oldPs.keySetData.isUsingUpgradeKeySets()) { return false; } @@ -17649,7 +16557,7 @@ public class PackageManagerService extends IPackageManager.Stub return true; } - private boolean checkUpgradeKeySetLP(PackageSetting oldPS, PackageParser.Package newPkg) { + private boolean checkUpgradeKeySetLP(PackageSettingBase oldPS, PackageParser.Package newPkg) { // Upgrade keysets are being used. Determine if new package has a superset of the // required keys. long[] upgradeKeySets = oldPS.keySetData.getUpgradeKeySets(); @@ -18225,66 +17133,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - private int[] revokeUnusedSharedUserPermissionsLPw(SharedUserSetting su, int[] allUserIds) { - // Collect all used permissions in the UID - ArraySet<String> usedPermissions = new ArraySet<>(); - final int packageCount = su.packages.size(); - for (int i = 0; i < packageCount; i++) { - PackageSetting ps = su.packages.valueAt(i); - if (ps.pkg == null) { - continue; - } - final int requestedPermCount = ps.pkg.requestedPermissions.size(); - for (int j = 0; j < requestedPermCount; j++) { - String permission = ps.pkg.requestedPermissions.get(j); - BasePermission bp = mSettings.mPermissions.get(permission); - if (bp != null) { - usedPermissions.add(permission); - } - } - } - - PermissionsState permissionsState = su.getPermissionsState(); - // Prune install permissions - List<PermissionState> installPermStates = permissionsState.getInstallPermissionStates(); - final int installPermCount = installPermStates.size(); - for (int i = installPermCount - 1; i >= 0; i--) { - PermissionState permissionState = installPermStates.get(i); - if (!usedPermissions.contains(permissionState.getName())) { - BasePermission bp = mSettings.mPermissions.get(permissionState.getName()); - if (bp != null) { - permissionsState.revokeInstallPermission(bp); - permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, - PackageManager.MASK_PERMISSION_FLAGS, 0); - } - } - } - - int[] runtimePermissionChangedUserIds = EmptyArray.INT; - - // Prune runtime permissions - for (int userId : allUserIds) { - List<PermissionState> runtimePermStates = permissionsState - .getRuntimePermissionStates(userId); - final int runtimePermCount = runtimePermStates.size(); - for (int i = runtimePermCount - 1; i >= 0; i--) { - PermissionState permissionState = runtimePermStates.get(i); - if (!usedPermissions.contains(permissionState.getName())) { - BasePermission bp = mSettings.mPermissions.get(permissionState.getName()); - if (bp != null) { - permissionsState.revokeRuntimePermission(bp, userId); - permissionsState.updatePermissionFlags(bp, userId, - PackageManager.MASK_PERMISSION_FLAGS, 0); - runtimePermissionChangedUserIds = ArrayUtils.appendInt( - runtimePermissionChangedUserIds, userId); - } - } - } - } - - return runtimePermissionChangedUserIds; - } - private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName, int[] allUsers, PackageInstalledInfo res, UserHandle user, int installReason) { // Update the parent package setting @@ -18685,8 +17533,9 @@ public class PackageManagerService extends IPackageManager.Stub int N = pkg.permissions.size(); for (int i = N-1; i >= 0; i--) { - PackageParser.Permission perm = pkg.permissions.get(i); - BasePermission bp = mSettings.mPermissions.get(perm.info.name); + final PackageParser.Permission perm = pkg.permissions.get(i); + final BasePermission bp = + (BasePermission) mPermissionManager.getPermissionTEMP(perm.info.name); // Don't allow anyone but the system to define ephemeral permissions. if ((perm.info.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0 @@ -18703,25 +17552,26 @@ public class PackageManagerService extends IPackageManager.Stub // also includes the "updating the same package" case, of course. // "updating same package" could also involve key-rotation. final boolean sigsOk; - if (bp.sourcePackage.equals(pkg.packageName) - && (bp.packageSetting instanceof PackageSetting) - && (shouldCheckUpgradeKeySetLP((PackageSetting) bp.packageSetting, + final String sourcePackageName = bp.getSourcePackageName(); + final PackageSettingBase sourcePackageSetting = bp.getSourcePackageSetting(); + if (sourcePackageName.equals(pkg.packageName) + && (shouldCheckUpgradeKeySetLP(sourcePackageSetting, scanFlags))) { - sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg); + sigsOk = checkUpgradeKeySetLP(sourcePackageSetting, pkg); } else { - sigsOk = compareSignatures(bp.packageSetting.signatures.mSignatures, + sigsOk = compareSignatures(sourcePackageSetting.signatures.mSignatures, pkg.mSignatures) == PackageManager.SIGNATURE_MATCH; } if (!sigsOk) { // If the owning package is the system itself, we log but allow // install to proceed; we fail the install on all other permission // redefinitions. - if (!bp.sourcePackage.equals("android")) { + if (!sourcePackageName.equals("android")) { res.setError(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package " + pkg.packageName + " attempting to redeclare permission " - + perm.info.name + " already owned by " + bp.sourcePackage); + + perm.info.name + " already owned by " + sourcePackageName); res.origPermission = perm.info.name; - res.origPackage = bp.sourcePackage; + res.origPackage = sourcePackageName; return; } else { Slog.w(TAG, "Package " + pkg.packageName @@ -18740,7 +17590,7 @@ public class PackageManagerService extends IPackageManager.Stub Slog.w(TAG, "Package " + pkg.packageName + " trying to change a " + "non-runtime permission " + perm.info.name + " to runtime; keeping old protection level"); - perm.info.protectionLevel = bp.protectionLevel; + perm.info.protectionLevel = bp.getProtectionLevel(); } } } @@ -18855,7 +17705,13 @@ public class PackageManagerService extends IPackageManager.Stub // TODO: Layering violation BackgroundDexOptService.notifyPackageChanged(pkg.packageName); - startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg); + if (!instantApp) { + startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg); + } else { + if (DEBUG_DOMAIN_VERIFICATION) { + Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName); + } + } try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags, "installPackageLI")) { @@ -20440,7 +19296,7 @@ public class PackageManagerService extends IPackageManager.Stub android.Manifest.permission.CLEAR_APP_USER_DATA, null); final int callingUid = Binder.getCallingUid(); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "clear application data"); final PackageSetting ps = mSettings.getPackageLPr(packageName); @@ -20579,9 +19435,9 @@ public class PackageManagerService extends IPackageManager.Stub final int permissionCount = ps.pkg.requestedPermissions.size(); for (int i = 0; i < permissionCount; i++) { - String permission = ps.pkg.requestedPermissions.get(i); - - BasePermission bp = mSettings.mPermissions.get(permission); + final String permName = ps.pkg.requestedPermissions.get(i); + final BasePermission bp = + (BasePermission) mPermissionManager.getPermissionTEMP(permName); if (bp == null) { continue; } @@ -20593,7 +19449,7 @@ public class PackageManagerService extends IPackageManager.Stub for (int j = 0; j < packageCount; j++) { PackageSetting pkg = ps.sharedUser.packages.valueAt(j); if (pkg.pkg != null && !pkg.pkg.packageName.equals(ps.pkg.packageName) - && pkg.pkg.requestedPermissions.contains(permission)) { + && pkg.pkg.requestedPermissions.contains(permName)) { used = true; break; } @@ -20603,13 +19459,13 @@ public class PackageManagerService extends IPackageManager.Stub } } - PermissionsState permissionsState = ps.getPermissionsState(); + final PermissionsState permissionsState = ps.getPermissionsState(); - final int oldFlags = permissionsState.getPermissionFlags(bp.name, userId); + final int oldFlags = permissionsState.getPermissionFlags(permName, userId); // Always clear the user settable flags. - final boolean hasInstallState = permissionsState.getInstallPermissionState( - bp.name) != null; + final boolean hasInstallState = + permissionsState.getInstallPermissionState(permName) != null; // If permission review is enabled and this is a legacy app, mark the // permission as requiring a review as this is the initial state. int flags = 0; @@ -20709,7 +19565,7 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUid = Binder.getCallingUid(); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.DELETE_CACHE_FILES, null); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, /* requireFullPermission= */ true, /* checkShell= */ false, "delete application cache files"); final int hasAccessInstantApps = mContext.checkCallingOrSelfPermission( @@ -20829,7 +19685,7 @@ public class PackageManagerService extends IPackageManager.Stub String opname) { // writer int callingUid = Binder.getCallingUid(); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "add preferred activity"); if (filter.countActions() == 0) { Slog.w(TAG, "Cannot set a preferred activity with no filter actions"); @@ -20894,7 +19750,7 @@ public class PackageManagerService extends IPackageManager.Stub } final int callingUid = Binder.getCallingUid(); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "replace preferred activity"); synchronized (mPackages) { @@ -21571,8 +20427,11 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); newFlagSet |= FLAG_PERMISSION_REVOKE_ON_UPGRADE; } if (DEBUG_BACKUP) { - Slog.v(TAG, " + Restoring grant: pkg=" + pkgName + " perm=" + permName - + " granted=" + isGranted + " bits=0x" + Integer.toHexString(newFlagSet)); + Slog.v(TAG, " + Restoring grant:" + + " pkg=" + pkgName + + " perm=" + permName + + " granted=" + isGranted + + " bits=0x" + Integer.toHexString(newFlagSet)); } final PackageSetting ps = mSettings.mPackages.get(pkgName); if (ps != null) { @@ -21581,13 +20440,15 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); Slog.v(TAG, " + already installed; applying"); } PermissionsState perms = ps.getPermissionsState(); - BasePermission bp = mSettings.mPermissions.get(permName); + BasePermission bp = + (BasePermission) mPermissionManager.getPermissionTEMP(permName); if (bp != null) { if (isGranted) { perms.grantRuntimePermission(bp, userId); } if (newFlagSet != 0) { - perms.updatePermissionFlags(bp, userId, USER_RUNTIME_GRANT_MASK, newFlagSet); + perms.updatePermissionFlags( + bp, userId, USER_RUNTIME_GRANT_MASK, newFlagSet); } } } else { @@ -21616,7 +20477,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); int callingUid = Binder.getCallingUid(); enforceOwnerRights(ownerPackage, callingUid); - enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId); + PackageManagerServiceUtils.enforceShellRestriction( + UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId); if (intentFilter.countActions() == 0) { Slog.w(TAG, "Cannot set a crossProfile intent filter with no filter actions"); return; @@ -21647,7 +20509,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); final int callingUid = Binder.getCallingUid(); enforceOwnerRights(ownerPackage, callingUid); - enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId); + PackageManagerServiceUtils.enforceShellRestriction( + UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId); synchronized (mPackages) { CrossProfileIntentResolver resolver = mSettings.editCrossProfileIntentResolverLPw(sourceUserId); @@ -21877,7 +20740,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); permission = mContext.checkCallingOrSelfPermission( android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE); } - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, true /* checkShell */, "set enabled"); final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED); boolean sendNow = false; @@ -22166,7 +21029,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); if (!sUserManager.exists(userId)) { return; } - enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/, + mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/, false /* checkShell */, "flushPackageRestrictions"); synchronized (mPackages) { mSettings.writePackageRestrictionsLPr(userId); @@ -22208,7 +21071,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); final int permission = mContext.checkCallingOrSelfPermission( android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE); final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, true /* checkShell */, "stop package"); // writer synchronized (mPackages) { @@ -22248,7 +21111,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); public int getApplicationEnabledSetting(String packageName, int userId) { if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED; int callingUid = Binder.getCallingUid(); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get enabled"); // reader synchronized (mPackages) { @@ -22263,7 +21126,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); public int getComponentEnabledSetting(ComponentName component, int userId) { if (!sUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED; int callingUid = Binder.getCallingUid(); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "getComponentEnabled"); synchronized (mPackages) { if (filterAppAccessLPr(mSettings.getPackageLPr(component.getPackageName()), callingUid, @@ -22361,7 +21224,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); // If we upgraded grant all default permissions before kicking off. for (int userId : grantPermissionsUserIds) { - mDefaultPermissionPolicy.grantDefaultPermissions(userId); + mDefaultPermissionPolicy.grantDefaultPermissions(mPackages.values(), userId); } if (grantPermissionsUserIds == EMPTY_INT_ARRAY) { @@ -22465,85 +21328,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); return buf.toString(); } - static class DumpState { - public static final int DUMP_LIBS = 1 << 0; - public static final int DUMP_FEATURES = 1 << 1; - public static final int DUMP_ACTIVITY_RESOLVERS = 1 << 2; - public static final int DUMP_SERVICE_RESOLVERS = 1 << 3; - public static final int DUMP_RECEIVER_RESOLVERS = 1 << 4; - public static final int DUMP_CONTENT_RESOLVERS = 1 << 5; - public static final int DUMP_PERMISSIONS = 1 << 6; - public static final int DUMP_PACKAGES = 1 << 7; - public static final int DUMP_SHARED_USERS = 1 << 8; - public static final int DUMP_MESSAGES = 1 << 9; - public static final int DUMP_PROVIDERS = 1 << 10; - public static final int DUMP_VERIFIERS = 1 << 11; - public static final int DUMP_PREFERRED = 1 << 12; - public static final int DUMP_PREFERRED_XML = 1 << 13; - public static final int DUMP_KEYSETS = 1 << 14; - public static final int DUMP_VERSION = 1 << 15; - public static final int DUMP_INSTALLS = 1 << 16; - public static final int DUMP_INTENT_FILTER_VERIFIERS = 1 << 17; - public static final int DUMP_DOMAIN_PREFERRED = 1 << 18; - public static final int DUMP_FROZEN = 1 << 19; - public static final int DUMP_DEXOPT = 1 << 20; - public static final int DUMP_COMPILER_STATS = 1 << 21; - public static final int DUMP_CHANGES = 1 << 22; - public static final int DUMP_VOLUMES = 1 << 23; - - public static final int OPTION_SHOW_FILTERS = 1 << 0; - - private int mTypes; - - private int mOptions; - - private boolean mTitlePrinted; - - private SharedUserSetting mSharedUser; - - public boolean isDumping(int type) { - if (mTypes == 0 && type != DUMP_PREFERRED_XML) { - return true; - } - - return (mTypes & type) != 0; - } - - public void setDump(int type) { - mTypes |= type; - } - - public boolean isOptionEnabled(int option) { - return (mOptions & option) != 0; - } - - public void setOptionEnabled(int option) { - mOptions |= option; - } - - public boolean onTitlePrinted() { - final boolean printed = mTitlePrinted; - mTitlePrinted = true; - return printed; - } - - public boolean getTitlePrinted() { - return mTitlePrinted; - } - - public void setTitlePrinted(boolean enabled) { - mTitlePrinted = enabled; - } - - public SharedUserSetting getSharedUser() { - return mSharedUser; - } - - public void setSharedUser(SharedUserSetting user) { - mSharedUser = user; - } - } - @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, @@ -23401,135 +22185,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } } - /* - * Update media status on PackageManager. - */ - @Override - public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) { - enforceSystemOrRoot("Media status can only be updated by the system"); - // reader; this apparently protects mMediaMounted, but should probably - // be a different lock in that case. - synchronized (mPackages) { - Log.i(TAG, "Updating external media status from " - + (mMediaMounted ? "mounted" : "unmounted") + " to " - + (mediaStatus ? "mounted" : "unmounted")); - if (DEBUG_SD_INSTALL) - Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" + mediaStatus - + ", mMediaMounted=" + mMediaMounted); - if (mediaStatus == mMediaMounted) { - final Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, reportStatus ? 1 - : 0, -1); - mHandler.sendMessage(msg); - return; - } - mMediaMounted = mediaStatus; - } - // Queue up an async operation since the package installation may take a - // little while. - mHandler.post(new Runnable() { - public void run() { - updateExternalMediaStatusInner(mediaStatus, reportStatus, true); - } - }); - } - - /** - * Called by StorageManagerService when the initial ASECs to scan are available. - * Should block until all the ASEC containers are finished being scanned. - */ - public void scanAvailableAsecs() { - updateExternalMediaStatusInner(true, false, false); - } - - /* - * Collect information of applications on external media, map them against - * existing containers and update information based on current mount status. - * Please note that we always have to report status if reportStatus has been - * set to true especially when unloading packages. - */ - private void updateExternalMediaStatusInner(boolean isMounted, boolean reportStatus, - boolean externalStorage) { - ArrayMap<AsecInstallArgs, String> processCids = new ArrayMap<>(); - int[] uidArr = EmptyArray.INT; - - final String[] list = PackageHelper.getSecureContainerList(); - if (ArrayUtils.isEmpty(list)) { - Log.i(TAG, "No secure containers found"); - } else { - // Process list of secure containers and categorize them - // as active or stale based on their package internal state. - - // reader - synchronized (mPackages) { - for (String cid : list) { - // Leave stages untouched for now; installer service owns them - if (PackageInstallerService.isStageName(cid)) continue; - - if (DEBUG_SD_INSTALL) - Log.i(TAG, "Processing container " + cid); - String pkgName = getAsecPackageName(cid); - if (pkgName == null) { - Slog.i(TAG, "Found stale container " + cid + " with no package name"); - continue; - } - if (DEBUG_SD_INSTALL) - Log.i(TAG, "Looking for pkg : " + pkgName); - - final PackageSetting ps = mSettings.mPackages.get(pkgName); - if (ps == null) { - Slog.i(TAG, "Found stale container " + cid + " with no matching settings"); - continue; - } - - /* - * Skip packages that are not external if we're unmounting - * external storage. - */ - if (externalStorage && !isMounted && !isExternal(ps)) { - continue; - } - - final AsecInstallArgs args = new AsecInstallArgs(cid, - getAppDexInstructionSets(ps), ps.isForwardLocked()); - // The package status is changed only if the code path - // matches between settings and the container id. - if (ps.codePathString != null - && ps.codePathString.startsWith(args.getCodePath())) { - if (DEBUG_SD_INSTALL) { - Log.i(TAG, "Container : " + cid + " corresponds to pkg : " + pkgName - + " at code path: " + ps.codePathString); - } - - // We do have a valid package installed on sdcard - processCids.put(args, ps.codePathString); - final int uid = ps.appId; - if (uid != -1) { - uidArr = ArrayUtils.appendInt(uidArr, uid); - } - } else { - Slog.i(TAG, "Found stale container " + cid + ": expected codePath=" - + ps.codePathString); - } - } - } - - Arrays.sort(uidArr); - } - - // Process packages with valid entries. - if (isMounted) { - if (DEBUG_SD_INSTALL) - Log.i(TAG, "Loading packages"); - loadMediaPackages(processCids, uidArr, externalStorage); - startCleaningPackages(); - mInstallerService.onSecureContainersAvailable(); - } else { - if (DEBUG_SD_INSTALL) - Log.i(TAG, "Unloading packages"); - unloadMediaPackages(processCids, uidArr, reportStatus); - } - } - private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing, ArrayList<ApplicationInfo> infos, IIntentReceiver finishedReceiver) { final int size = infos.size(); @@ -23569,193 +22224,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } } - /* - * Look at potentially valid container ids from processCids If package - * information doesn't match the one on record or package scanning fails, - * the cid is added to list of removeCids. We currently don't delete stale - * containers. - */ - private void loadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int[] uidArr, - boolean externalStorage) { - ArrayList<String> pkgList = new ArrayList<String>(); - Set<AsecInstallArgs> keys = processCids.keySet(); - - for (AsecInstallArgs args : keys) { - String codePath = processCids.get(args); - if (DEBUG_SD_INSTALL) - Log.i(TAG, "Loading container : " + args.cid); - int retCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR; - try { - // Make sure there are no container errors first. - if (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED) != PackageManager.INSTALL_SUCCEEDED) { - Slog.e(TAG, "Failed to mount cid : " + args.cid - + " when installing from sdcard"); - continue; - } - // Check code path here. - if (codePath == null || !codePath.startsWith(args.getCodePath())) { - Slog.e(TAG, "Container " + args.cid + " cachepath " + args.getCodePath() - + " does not match one in settings " + codePath); - continue; - } - // Parse package - int parseFlags = mDefParseFlags; - if (args.isExternalAsec()) { - parseFlags |= PackageParser.PARSE_EXTERNAL_STORAGE; - } - if (args.isFwdLocked()) { - parseFlags |= PackageParser.PARSE_FORWARD_LOCK; - } - - synchronized (mInstallLock) { - PackageParser.Package pkg = null; - try { - // Sadly we don't know the package name yet to freeze it - pkg = scanPackageTracedLI(new File(codePath), parseFlags, - SCAN_IGNORE_FROZEN, 0, null); - } catch (PackageManagerException e) { - Slog.w(TAG, "Failed to scan " + codePath + ": " + e.getMessage()); - } - // Scan the package - if (pkg != null) { - /* - * TODO why is the lock being held? doPostInstall is - * called in other places without the lock. This needs - * to be straightened out. - */ - // writer - synchronized (mPackages) { - retCode = PackageManager.INSTALL_SUCCEEDED; - pkgList.add(pkg.packageName); - // Post process args - args.doPostInstall(PackageManager.INSTALL_SUCCEEDED, - pkg.applicationInfo.uid); - } - } else { - Slog.i(TAG, "Failed to install pkg from " + codePath + " from sdcard"); - } - } - - } finally { - if (retCode != PackageManager.INSTALL_SUCCEEDED) { - Log.w(TAG, "Container " + args.cid + " is stale, retCode=" + retCode); - } - } - } - // writer - synchronized (mPackages) { - // If the platform SDK has changed since the last time we booted, - // we need to re-grant app permission to catch any new ones that - // appear. This is really a hack, and means that apps can in some - // cases get permissions that the user didn't initially explicitly - // allow... it would be nice to have some better way to handle - // this situation. - final VersionInfo ver = externalStorage ? mSettings.getExternalVersion() - : mSettings.getInternalVersion(); - final String volumeUuid = externalStorage ? StorageManager.UUID_PRIMARY_PHYSICAL - : StorageManager.UUID_PRIVATE_INTERNAL; - - int updateFlags = UPDATE_PERMISSIONS_ALL; - if (ver.sdkVersion != mSdkVersion) { - logCriticalInfo(Log.INFO, "Platform changed from " + ver.sdkVersion + " to " - + mSdkVersion + "; regranting permissions for external"); - updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL; - } - updatePermissionsLPw(null, null, volumeUuid, updateFlags); - - // Yay, everything is now upgraded - ver.forceCurrent(); - - // can downgrade to reader - // Persist settings - mSettings.writeLPr(); - } - // Send a broadcast to let everyone know we are done processing - if (pkgList.size() > 0) { - sendResourcesChangedBroadcast(true, false, pkgList, uidArr, null); - } - } - - /* - * Utility method to unload a list of specified containers - */ - private void unloadAllContainers(Set<AsecInstallArgs> cidArgs) { - // Just unmount all valid containers. - for (AsecInstallArgs arg : cidArgs) { - synchronized (mInstallLock) { - arg.doPostDeleteLI(false); - } - } - } - - /* - * Unload packages mounted on external media. This involves deleting package - * data from internal structures, sending broadcasts about disabled packages, - * gc'ing to free up references, unmounting all secure containers - * corresponding to packages on external media, and posting a - * UPDATED_MEDIA_STATUS message if status has been requested. Please note - * that we always have to post this message if status has been requested no - * matter what. - */ - private void unloadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int uidArr[], - final boolean reportStatus) { - if (DEBUG_SD_INSTALL) - Log.i(TAG, "unloading media packages"); - ArrayList<String> pkgList = new ArrayList<String>(); - ArrayList<AsecInstallArgs> failedList = new ArrayList<AsecInstallArgs>(); - final Set<AsecInstallArgs> keys = processCids.keySet(); - for (AsecInstallArgs args : keys) { - String pkgName = args.getPackageName(); - if (DEBUG_SD_INSTALL) - Log.i(TAG, "Trying to unload pkg : " + pkgName); - // Delete package internally - PackageRemovedInfo outInfo = new PackageRemovedInfo(this); - synchronized (mInstallLock) { - final int deleteFlags = PackageManager.DELETE_KEEP_DATA; - final boolean res; - try (PackageFreezer freezer = freezePackageForDelete(pkgName, deleteFlags, - "unloadMediaPackages")) { - res = deletePackageLIF(pkgName, null, false, null, deleteFlags, outInfo, false, - null); - } - if (res) { - pkgList.add(pkgName); - } else { - Slog.e(TAG, "Failed to delete pkg from sdcard : " + pkgName); - failedList.add(args); - } - } - } - - // reader - synchronized (mPackages) { - // We didn't update the settings after removing each package; - // write them now for all packages. - mSettings.writeLPr(); - } - - // We have to absolutely send UPDATED_MEDIA_STATUS only - // after confirming that all the receivers processed the ordered - // broadcast when packages get disabled, force a gc to clean things up. - // and unload all the containers. - if (pkgList.size() > 0) { - sendResourcesChangedBroadcast(false, false, pkgList, uidArr, - new IIntentReceiver.Stub() { - public void performReceive(Intent intent, int resultCode, String data, - Bundle extras, boolean ordered, boolean sticky, - int sendingUser) throws RemoteException { - Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, - reportStatus ? 1 : 0, 1, keys); - mHandler.sendMessage(msg); - } - }); - } else { - Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS, reportStatus ? 1 : 0, -1, - keys); - mHandler.sendMessage(msg); - } - } - private void loadPrivatePackages(final VolumeInfo vol) { mHandler.post(new Runnable() { @Override @@ -24841,7 +23309,9 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } void onNewUserCreated(final int userId) { - mDefaultPermissionPolicy.grantDefaultPermissions(userId); + synchronized(mPackages) { + mDefaultPermissionPolicy.grantDefaultPermissions(mPackages.values(), userId); + } // If permission review for legacy apps is required, we represent // dagerous permissions for such apps as always granted runtime // permissions to keep per user flag state whether review is needed. @@ -24873,7 +23343,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); synchronized (mPackages) { if (mSettings.mReadExternalStorageEnforced == null || mSettings.mReadExternalStorageEnforced != enforced) { - mSettings.mReadExternalStorageEnforced = enforced; + mSettings.mReadExternalStorageEnforced = + enforced ? Boolean.TRUE : Boolean.FALSE; mSettings.writeLPr(); } } @@ -25245,74 +23716,164 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } return results; } + + // NB: this differentiates between preloads and sideloads + @Override + public String getInstallerForPackage(String packageName) throws RemoteException { + final String installerName = getInstallerPackageName(packageName); + if (!TextUtils.isEmpty(installerName)) { + return installerName; + } + // differentiate between preload and sideload + int callingUser = UserHandle.getUserId(Binder.getCallingUid()); + ApplicationInfo appInfo = getApplicationInfo(packageName, + /*flags*/ 0, + /*userId*/ callingUser); + if (appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + return "preload"; + } + return ""; + } + + @Override + public int getVersionCodeForPackage(String packageName) throws RemoteException { + try { + int callingUser = UserHandle.getUserId(Binder.getCallingUid()); + PackageInfo pInfo = getPackageInfo(packageName, 0, callingUser); + if (pInfo != null) { + return pInfo.versionCode; + } + } catch (Exception e) { + } + return 0; + } } private class PackageManagerInternalImpl extends PackageManagerInternal { @Override - public void setLocationPackagesProvider(PackagesProvider provider) { + public void updatePermissionFlagsTEMP(String permName, String packageName, int flagMask, + int flagValues, int userId) { + PackageManagerService.this.updatePermissionFlags( + permName, packageName, flagMask, flagValues, userId); + } + + @Override + public int getPermissionFlagsTEMP(String permName, String packageName, int userId) { + return PackageManagerService.this.getPermissionFlags(permName, packageName, userId); + } + + @Override + public Object enforcePermissionTreeTEMP(String permName, int callingUid) { synchronized (mPackages) { - mDefaultPermissionPolicy.setLocationPackagesProviderLPw(provider); + return BasePermission.enforcePermissionTreeLP( + mSettings.mPermissionTrees, permName, callingUid); } } + @Override + public boolean isInstantApp(String packageName, int userId) { + return PackageManagerService.this.isInstantApp(packageName, userId); + } @Override - public void setVoiceInteractionPackagesProvider(PackagesProvider provider) { + public String getInstantAppPackageName(int uid) { + return PackageManagerService.this.getInstantAppPackageName(uid); + } + + @Override + public boolean filterAppAccess(PackageParser.Package pkg, int callingUid, int userId) { synchronized (mPackages) { - mDefaultPermissionPolicy.setVoiceInteractionPackagesProviderLPw(provider); + return PackageManagerService.this.filterAppAccessLPr( + (PackageSetting) pkg.mExtras, callingUid, userId); } } @Override - public void setSmsAppPackagesProvider(PackagesProvider provider) { + public PackageParser.Package getPackage(String packageName) { synchronized (mPackages) { - mDefaultPermissionPolicy.setSmsAppPackagesProviderLPw(provider); + packageName = resolveInternalPackageNameLPr( + packageName, PackageManager.VERSION_CODE_HIGHEST); + return mPackages.get(packageName); } } @Override - public void setDialerAppPackagesProvider(PackagesProvider provider) { + public PackageParser.Package getDisabledPackage(String packageName) { synchronized (mPackages) { - mDefaultPermissionPolicy.setDialerAppPackagesProviderLPw(provider); + final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName); + return (ps != null) ? ps.pkg : null; } } @Override - public void setSimCallManagerPackagesProvider(PackagesProvider provider) { - synchronized (mPackages) { - mDefaultPermissionPolicy.setSimCallManagerPackagesProviderLPw(provider); + public String getKnownPackageName(int knownPackage, int userId) { + switch(knownPackage) { + case PackageManagerInternal.PACKAGE_BROWSER: + return getDefaultBrowserPackageName(userId); + case PackageManagerInternal.PACKAGE_INSTALLER: + return mRequiredInstallerPackage; + case PackageManagerInternal.PACKAGE_SETUP_WIZARD: + return mSetupWizardPackage; + case PackageManagerInternal.PACKAGE_SYSTEM: + return "android"; + case PackageManagerInternal.PACKAGE_VERIFIER: + return mRequiredVerifierPackage; } + return null; + } + + @Override + public boolean isResolveActivityComponent(ComponentInfo component) { + return mResolveActivity.packageName.equals(component.packageName) + && mResolveActivity.name.equals(component.name); + } + + @Override + public void setLocationPackagesProvider(PackagesProvider provider) { + mDefaultPermissionPolicy.setLocationPackagesProvider(provider); + } + + @Override + public void setVoiceInteractionPackagesProvider(PackagesProvider provider) { + mDefaultPermissionPolicy.setVoiceInteractionPackagesProvider(provider); + } + + @Override + public void setSmsAppPackagesProvider(PackagesProvider provider) { + mDefaultPermissionPolicy.setSmsAppPackagesProvider(provider); + } + + @Override + public void setDialerAppPackagesProvider(PackagesProvider provider) { + mDefaultPermissionPolicy.setDialerAppPackagesProvider(provider); + } + + @Override + public void setSimCallManagerPackagesProvider(PackagesProvider provider) { + mDefaultPermissionPolicy.setSimCallManagerPackagesProvider(provider); } @Override public void setSyncAdapterPackagesprovider(SyncAdapterPackagesProvider provider) { - synchronized (mPackages) { - mDefaultPermissionPolicy.setSyncAdapterPackagesProviderLPw(provider); - } + mDefaultPermissionPolicy.setSyncAdapterPackagesProvider(provider); } @Override public void grantDefaultPermissionsToDefaultSmsApp(String packageName, int userId) { - synchronized (mPackages) { - mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultSmsAppLPr( - packageName, userId); - } + mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultSmsApp(packageName, userId); } @Override public void grantDefaultPermissionsToDefaultDialerApp(String packageName, int userId) { synchronized (mPackages) { mSettings.setDefaultDialerPackageNameLPw(packageName, userId); - mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultDialerAppLPr( - packageName, userId); } + mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultDialerApp(packageName, userId); } @Override public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) { - synchronized (mPackages) { - mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultSimCallManagerLPr( - packageName, userId); - } + mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultSimCallManager( + packageName, userId); } @Override @@ -25399,6 +23960,15 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } @Override + public List<ResolveInfo> queryIntentServices( + Intent intent, int flags, int callingUid, int userId) { + final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver()); + return PackageManagerService.this + .queryIntentServicesInternal(intent, resolvedType, flags, userId, callingUid, + false); + } + + @Override public ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates, int userId) { return PackageManagerService.this.getHomeActivitiesAsUser(allHomeCandidates, userId); @@ -25433,17 +24003,19 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } @Override - public void grantRuntimePermission(String packageName, String name, int userId, + public void grantRuntimePermission(String packageName, String permName, int userId, boolean overridePolicy) { - PackageManagerService.this.grantRuntimePermission(packageName, name, userId, - overridePolicy); + PackageManagerService.this.mPermissionManager.grantRuntimePermission( + permName, packageName, overridePolicy, getCallingUid(), userId, + mPermissionCallback); } @Override - public void revokeRuntimePermission(String packageName, String name, int userId, + public void revokeRuntimePermission(String packageName, String permName, int userId, boolean overridePolicy) { - PackageManagerService.this.revokeRuntimePermission(packageName, name, userId, - overridePolicy); + mPermissionManager.revokeRuntimePermission( + permName, packageName, overridePolicy, getCallingUid(), userId, + mPermissionCallback); } @Override @@ -25565,9 +24137,9 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); @Override public ResolveInfo resolveIntent(Intent intent, String resolvedType, - int flags, int userId) { + int flags, int userId, boolean resolveForStart) { return resolveIntentInternal( - intent, resolvedType, flags, userId, true /*resolveForStart*/); + intent, resolvedType, flags, userId, resolveForStart); } @Override @@ -25577,6 +24149,12 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } @Override + public ProviderInfo resolveContentProvider(String name, int flags, int userId) { + return PackageManagerService.this.resolveContentProviderInternal( + name, flags, userId); + } + + @Override public void addIsolatedUid(int isolatedUid, int ownerUid) { synchronized (mPackages) { mIsolatedOwners.put(isolatedUid, ownerUid); @@ -25623,7 +24201,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); synchronized (mPackages) { final long identity = Binder.clearCallingIdentity(); try { - mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledCarrierAppsLPr( + mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledCarrierApps( packageNames, userId); } finally { Binder.restoreCallingIdentity(identity); @@ -25637,7 +24215,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); synchronized (mPackages) { final long identity = Binder.clearCallingIdentity(); try { - mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledImsServicesLPr( + mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledImsServices( packageNames, userId); } finally { Binder.restoreCallingIdentity(identity); @@ -25712,7 +24290,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); @Override public int getInstallReason(String packageName, int userId) { final int callingUid = Binder.getCallingUid(); - enforceCrossUserPermission(callingUid, userId, + mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "get install reason"); synchronized (mPackages) { @@ -25790,7 +24368,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); public String getInstantAppAndroidId(String packageName, int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_INSTANT_APPS, "getInstantAppAndroidId"); - enforceCrossUserPermission(Binder.getCallingUid(), userId, + mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getInstantAppAndroidId"); // Make sure the target is an Instant App. diff --git a/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/com/android/server/pm/PackageManagerServiceCompilerMapping.java index 1a97a72c..19b0d9bc 100644 --- a/com/android/server/pm/PackageManagerServiceCompilerMapping.java +++ b/com/android/server/pm/PackageManagerServiceCompilerMapping.java @@ -26,14 +26,19 @@ import dalvik.system.DexFile; public class PackageManagerServiceCompilerMapping { // Names for compilation reasons. static final String REASON_STRINGS[] = { - "first-boot", "boot", "install", "bg-dexopt", "ab-ota", "inactive" + "first-boot", "boot", "install", "bg-dexopt", "ab-ota", "inactive", "shared" }; + static final int REASON_SHARED_INDEX = 6; + // Static block to ensure the strings array is of the right length. static { if (PackageManagerService.REASON_LAST + 1 != REASON_STRINGS.length) { throw new IllegalStateException("REASON_STRINGS not correct"); } + if (!"shared".equals(REASON_STRINGS[REASON_SHARED_INDEX])) { + throw new IllegalStateException("REASON_STRINGS not correct because of shared index"); + } } private static String getSystemPropertyName(int reason) { @@ -52,11 +57,18 @@ public class PackageManagerServiceCompilerMapping { !DexFile.isValidCompilerFilter(sysPropValue)) { throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid " + "(reason " + REASON_STRINGS[reason] + ")"); + } else if (!isFilterAllowedForReason(reason, sysPropValue)) { + throw new IllegalStateException("Value \"" + sysPropValue +"\" not allowed " + + "(reason " + REASON_STRINGS[reason] + ")"); } return sysPropValue; } + private static boolean isFilterAllowedForReason(int reason, String filter) { + return reason != REASON_SHARED_INDEX || !DexFile.isProfileGuidedCompilerFilter(filter); + } + // Check that the properties are set and valid. // Note: this is done in a separate method so this class can be statically initialized. static void checkProperties() { diff --git a/com/android/server/pm/PackageManagerServiceUtils.java b/com/android/server/pm/PackageManagerServiceUtils.java index 25fef0a0..8f7971e1 100644 --- a/com/android/server/pm/PackageManagerServiceUtils.java +++ b/com/android/server/pm/PackageManagerServiceUtils.java @@ -22,17 +22,23 @@ import com.android.server.pm.dex.PackageDexUsage; import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT; import static com.android.server.pm.PackageManagerService.TAG; +import com.android.internal.util.ArrayUtils; + import android.annotation.NonNull; import android.app.AppGlobals; import android.content.Intent; import android.content.pm.PackageParser; import android.content.pm.ResolveInfo; import android.os.Build; +import android.os.Debug; +import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.system.ErrnoException; import android.util.ArraySet; import android.util.Log; +import android.util.Slog; +import android.util.jar.StrictJarFile; import dalvik.system.VMRuntime; import libcore.io.Libcore; @@ -41,9 +47,11 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.function.Predicate; +import java.util.zip.ZipEntry; /** * Class containing helper methods for the PackageManagerService. @@ -253,4 +261,73 @@ public class PackageManagerServiceUtils { } return false; } + + /** + * Checks that the archive located at {@code fileName} has uncompressed dex file and so + * files that can be direclty mapped. + */ + public static void logApkHasUncompressedCode(String fileName) { + StrictJarFile jarFile = null; + try { + jarFile = new StrictJarFile(fileName, + false /*verify*/, false /*signatureSchemeRollbackProtectionsEnforced*/); + Iterator<ZipEntry> it = jarFile.iterator(); + while (it.hasNext()) { + ZipEntry entry = it.next(); + if (entry.getName().endsWith(".dex")) { + if (entry.getMethod() != ZipEntry.STORED) { + Slog.wtf(TAG, "APK " + fileName + " has compressed dex code " + + entry.getName()); + } else if ((entry.getDataOffset() & 0x3) != 0) { + Slog.wtf(TAG, "APK " + fileName + " has unaligned dex code " + + entry.getName()); + } + } else if (entry.getName().endsWith(".so")) { + if (entry.getMethod() != ZipEntry.STORED) { + Slog.wtf(TAG, "APK " + fileName + " has compressed native code " + + entry.getName()); + } else if ((entry.getDataOffset() & (0x1000 - 1)) != 0) { + Slog.wtf(TAG, "APK " + fileName + " has unaligned native code " + + entry.getName()); + } + } + } + } catch (IOException ignore) { + Slog.wtf(TAG, "Error when parsing APK " + fileName); + } finally { + try { + if (jarFile != null) { + jarFile.close(); + } + } catch (IOException ignore) {} + } + return; + } + + /** + * Checks that the APKs in the given package have uncompressed dex file and so + * files that can be direclty mapped. + */ + public static void logPackageHasUncompressedCode(PackageParser.Package pkg) { + logApkHasUncompressedCode(pkg.baseCodePath); + if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { + for (int i = 0; i < pkg.splitCodePaths.length; i++) { + logApkHasUncompressedCode(pkg.splitCodePaths[i]); + } + } + } + + public static void enforceShellRestriction(String restriction, int callingUid, int userHandle) { + if (callingUid == Process.SHELL_UID) { + if (userHandle >= 0 + && PackageManagerService.sUserManager.hasUserRestriction( + restriction, userHandle)) { + throw new SecurityException("Shell does not have permission to access user " + + userHandle); + } else if (userHandle < 0) { + Slog.e(PackageManagerService.TAG, "Unable to check shell permission for user " + + userHandle + "\n\t" + Debug.getCallers(3)); + } + } + } } diff --git a/com/android/server/pm/PackageManagerShellCommand.java b/com/android/server/pm/PackageManagerShellCommand.java index 930e4f09..1fea003a 100644 --- a/com/android/server/pm/PackageManagerShellCommand.java +++ b/com/android/server/pm/PackageManagerShellCommand.java @@ -183,7 +183,7 @@ class PackageManagerShellCommand extends ShellCommand { PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null, null, null); params.sessionParams.setSize(PackageHelper.calculateInstalledSize( - pkgLite, false, params.sessionParams.abiOverride)); + pkgLite, params.sessionParams.abiOverride)); } catch (PackageParserException | IOException e) { pw.println("Error: Failed to parse APK file: " + file); throw new IllegalArgumentException( @@ -1441,7 +1441,7 @@ class PackageManagerShellCommand extends ShellCommand { out = session.openWrite(splitName, 0, sizeBytes); int total = 0; - byte[] buffer = new byte[65536]; + byte[] buffer = new byte[1024 * 1024]; int c; while ((c = in.read(buffer)) != -1) { total += c; diff --git a/com/android/server/pm/PackageSetting.java b/com/android/server/pm/PackageSetting.java index 52bf6410..83cb2db2 100644 --- a/com/android/server/pm/PackageSetting.java +++ b/com/android/server/pm/PackageSetting.java @@ -23,13 +23,15 @@ import android.content.pm.UserInfo; import android.service.pm.PackageProto; import android.util.proto.ProtoOutputStream; +import com.android.server.pm.permission.PermissionsState; + import java.io.File; import java.util.List; /** * Settings data for a particular package we know about. */ -final class PackageSetting extends PackageSettingBase { +public final class PackageSetting extends PackageSettingBase { int appId; PackageParser.Package pkg; /** @@ -103,12 +105,21 @@ final class PackageSetting extends PackageSettingBase { sharedUserId = orig.sharedUserId; } + @Override public PermissionsState getPermissionsState() { return (sharedUser != null) ? sharedUser.getPermissionsState() : super.getPermissionsState(); } + public PackageParser.Package getPackage() { + return pkg; + } + + public int getAppId() { + return appId; + } + public boolean isPrivileged() { return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0; } @@ -125,6 +136,7 @@ final class PackageSetting extends PackageSettingBase { return (pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0; } + @Override public boolean isSharedUser() { return sharedUser != null; } @@ -136,6 +148,10 @@ final class PackageSetting extends PackageSettingBase { return true; } + public boolean hasChildPackages() { + return childPackageNames != null && !childPackageNames.isEmpty(); + } + public void writeToProto(ProtoOutputStream proto, long fieldId, List<UserInfo> users) { final long packageToken = proto.start(fieldId); proto.write(PackageProto.NAME, (realName != null ? realName : name)); diff --git a/com/android/server/pm/PackageSettingBase.java b/com/android/server/pm/PackageSettingBase.java index d3ca1fda..e19e83fc 100644 --- a/com/android/server/pm/PackageSettingBase.java +++ b/com/android/server/pm/PackageSettingBase.java @@ -24,14 +24,12 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IntentFilterVerificationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageUserState; -import android.os.storage.VolumeInfo; import android.service.pm.PackageProto; import android.util.ArraySet; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; -import com.google.android.collect.Lists; import java.io.File; import java.util.ArrayList; @@ -42,7 +40,7 @@ import java.util.Set; /** * Settings base class for pending and resolved classes. */ -abstract class PackageSettingBase extends SettingBase { +public abstract class PackageSettingBase extends SettingBase { private static final int[] EMPTY_INT_ARRAY = new int[0]; @@ -230,6 +228,9 @@ abstract class PackageSettingBase extends SettingBase { return updateAvailable; } + public boolean isSharedUser() { + return false; + } /** * Makes a shallow copy of the given package settings. * @@ -412,7 +413,7 @@ abstract class PackageSettingBase extends SettingBase { modifyUserState(userId).suspended = suspended; } - boolean getInstantApp(int userId) { + public boolean getInstantApp(int userId) { return readUserState(userId).instantApp; } diff --git a/com/android/server/pm/SettingBase.java b/com/android/server/pm/SettingBase.java index e17cec02..c97f5e54 100644 --- a/com/android/server/pm/SettingBase.java +++ b/com/android/server/pm/SettingBase.java @@ -18,6 +18,8 @@ package com.android.server.pm; import android.content.pm.ApplicationInfo; +import com.android.server.pm.permission.PermissionsState; + abstract class SettingBase { int pkgFlags; int pkgPrivateFlags; diff --git a/com/android/server/pm/Settings.java b/com/android/server/pm/Settings.java index 51d3e102..00844114 100644 --- a/com/android/server/pm/Settings.java +++ b/com/android/server/pm/Settings.java @@ -16,7 +16,6 @@ package com.android.server.pm; -import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; @@ -64,7 +63,6 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; -import android.os.storage.VolumeInfo; import android.service.pm.PackageServiceDumpProto; import android.text.TextUtils; import android.util.ArrayMap; @@ -87,10 +85,11 @@ import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.JournaledFile; import com.android.internal.util.XmlUtils; -import com.android.server.backup.PreferredActivityBackupHelper; import com.android.server.pm.Installer.InstallerException; -import com.android.server.pm.PackageManagerService.DumpState; -import com.android.server.pm.PermissionsState.PermissionState; +import com.android.server.pm.permission.BasePermission; +import com.android.server.pm.permission.PermissionSettings; +import com.android.server.pm.permission.PermissionsState; +import com.android.server.pm.permission.PermissionsState.PermissionState; import libcore.io.IoUtils; @@ -127,7 +126,7 @@ import java.util.Set; /** * Holds information about dynamic settings. */ -final class Settings { +public final class Settings { private static final String TAG = "PackageSettings"; /** @@ -176,7 +175,7 @@ final class Settings { private static final String TAG_READ_EXTERNAL_STORAGE = "read-external-storage"; private static final String ATTR_ENFORCEMENT = "enforcement"; - private static final String TAG_ITEM = "item"; + public static final String TAG_ITEM = "item"; private static final String TAG_DISABLED_COMPONENTS = "disabled-components"; private static final String TAG_ENABLED_COMPONENTS = "enabled-components"; private static final String TAG_PACKAGE_RESTRICTIONS = "package-restrictions"; @@ -201,7 +200,8 @@ final class Settings { private static final String TAG_DEFAULT_DIALER = "default-dialer"; private static final String TAG_VERSION = "version"; - private static final String ATTR_NAME = "name"; + public static final String ATTR_NAME = "name"; + public static final String ATTR_PACKAGE = "package"; private static final String ATTR_USER = "user"; private static final String ATTR_CODE = "code"; private static final String ATTR_GRANTED = "granted"; @@ -233,7 +233,6 @@ final class Settings { private static final String ATTR_VOLUME_UUID = "volumeUuid"; private static final String ATTR_SDK_VERSION = "sdkVersion"; private static final String ATTR_DATABASE_VERSION = "databaseVersion"; - private static final String ATTR_DONE = "done"; // Bookkeeping for restored permission grants private static final String TAG_RESTORED_RUNTIME_PERMISSIONS = "restored-perms"; @@ -379,10 +378,6 @@ final class Settings { private final ArrayMap<Long, Integer> mKeySetRefs = new ArrayMap<Long, Integer>(); - // Mapping from permission names to info about them. - final ArrayMap<String, BasePermission> mPermissions = - new ArrayMap<String, BasePermission>(); - // Mapping from permission tree names to info about them. final ArrayMap<String, BasePermission> mPermissionTrees = new ArrayMap<String, BasePermission>(); @@ -420,14 +415,16 @@ final class Settings { private final File mSystemDir; public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages); + /** Settings and other information about permissions */ + private final PermissionSettings mPermissions; - Settings(Object lock) { - this(Environment.getDataDirectory(), lock); + Settings(PermissionSettings permissions, Object lock) { + this(Environment.getDataDirectory(), permissions, lock); } - Settings(File dataDir, Object lock) { + Settings(File dataDir, PermissionSettings permission, Object lock) { mLock = lock; - + mPermissions = permission; mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock); mSystemDir = new File(dataDir, "system"); @@ -490,7 +487,7 @@ final class Settings { final PermissionsState perms = ps.getPermissionsState(); for (RestoredPermissionGrant grant : grants) { - BasePermission bp = mPermissions.get(grant.permissionName); + BasePermission bp = mPermissions.getPermission(grant.permissionName); if (bp != null) { if (grant.granted) { perms.grantRuntimePermission(bp, userId); @@ -507,6 +504,10 @@ final class Settings { writeRuntimePermissionsForUserLPr(userId, false); } + public boolean canPropagatePermissionToInstantApp(String permName) { + return mPermissions.canPropagatePermissionToInstantApp(permName); + } + void setInstallerPackageName(String pkgName, String installerPkgName) { PackageSetting p = mPackages.get(pkgName); if (p != null) { @@ -664,29 +665,11 @@ final class Settings { } } - // Transfer ownership of permissions from one package to another. - void transferPermissionsLPw(String origPkg, String newPkg) { - // Transfer ownership of permissions to the new package. - for (int i=0; i<2; i++) { - ArrayMap<String, BasePermission> permissions = - i == 0 ? mPermissionTrees : mPermissions; - for (BasePermission bp : permissions.values()) { - if (origPkg.equals(bp.sourcePackage)) { - if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, - "Moving permission " + bp.name - + " from pkg " + bp.sourcePackage - + " to " + newPkg); - bp.sourcePackage = newPkg; - bp.packageSetting = null; - bp.perm = null; - if (bp.pendingInfo != null) { - bp.pendingInfo.packageName = newPkg; - } - bp.uid = 0; - bp.setGids(null, false); - } - } - } + /** + * Transfers ownership of permissions from one package to another. + */ + void transferPermissionsLPw(String origPackageName, String newPackageName) { + mPermissions.transferPermissions(origPackageName, newPackageName, mPermissionTrees); } /** @@ -1074,7 +1057,7 @@ final class Settings { // Update permissions for (String eachPerm : deletedPs.pkg.requestedPermissions) { - BasePermission bp = mPermissions.get(eachPerm); + BasePermission bp = mPermissions.getPermission(eachPerm); if (bp == null) { continue; } @@ -2022,8 +2005,7 @@ final class Settings { // Specifically for backup/restore public void processRestoredPermissionGrantLPr(String pkgName, String permission, - boolean isGranted, int restoredFlagSet, int userId) - throws IOException, XmlPullParserException { + boolean isGranted, int restoredFlagSet, int userId) { mRuntimePermissionsPersistence.rememberRestoredUserGrantLPr( pkgName, permission, isGranted, restoredFlagSet, userId); } @@ -2225,7 +2207,7 @@ final class Settings { if (tagName.equals(TAG_ITEM)) { String name = parser.getAttributeValue(null, ATTR_NAME); - BasePermission bp = mPermissions.get(name); + BasePermission bp = mPermissions.getPermission(name); if (bp == null) { Slog.w(PackageManagerService.TAG, "Unknown permission: " + name); XmlUtils.skipCurrentTag(parser); @@ -2520,9 +2502,7 @@ final class Settings { serializer.endTag(null, "permission-trees"); serializer.startTag(null, "permissions"); - for (BasePermission bp : mPermissions.values()) { - writePermissionLPr(serializer, bp); - } + mPermissions.writePermissions(serializer); serializer.endTag(null, "permissions"); for (final PackageSetting pkg : mPackages.values()) { @@ -2605,9 +2585,6 @@ final class Settings { writeAllRuntimePermissionsLPr(); return; - } catch(XmlPullParserException e) { - Slog.wtf(PackageManagerService.TAG, "Unable to write package manager settings, " - + "current changes will be lost at reboot", e); } catch(java.io.IOException e) { Slog.wtf(PackageManagerService.TAG, "Unable to write package manager settings, " + "current changes will be lost at reboot", e); @@ -2951,7 +2928,6 @@ final class Settings { void writeUpgradeKeySetsLPr(XmlSerializer serializer, PackageKeySetData data) throws IOException { - long properSigning = data.getProperSigningKeySet(); if (data.isUsingUpgradeKeySets()) { for (long id : data.getUpgradeKeySets()) { serializer.startTag(null, "upgrade-keyset"); @@ -2971,32 +2947,8 @@ final class Settings { } } - void writePermissionLPr(XmlSerializer serializer, BasePermission bp) - throws XmlPullParserException, java.io.IOException { - if (bp.sourcePackage != null) { - serializer.startTag(null, TAG_ITEM); - serializer.attribute(null, ATTR_NAME, bp.name); - serializer.attribute(null, "package", bp.sourcePackage); - if (bp.protectionLevel != PermissionInfo.PROTECTION_NORMAL) { - serializer.attribute(null, "protection", Integer.toString(bp.protectionLevel)); - } - if (PackageManagerService.DEBUG_SETTINGS) - Log.v(PackageManagerService.TAG, "Writing perm: name=" + bp.name + " type=" - + bp.type); - if (bp.type == BasePermission.TYPE_DYNAMIC) { - final PermissionInfo pi = bp.perm != null ? bp.perm.info : bp.pendingInfo; - if (pi != null) { - serializer.attribute(null, "type", "dynamic"); - if (pi.icon != 0) { - serializer.attribute(null, "icon", Integer.toString(pi.icon)); - } - if (pi.nonLocalizedLabel != null) { - serializer.attribute(null, "label", pi.nonLocalizedLabel.toString()); - } - } - } - serializer.endTag(null, TAG_ITEM); - } + void writePermissionLPr(XmlSerializer serializer, BasePermission bp) throws IOException { + bp.writeLPr(serializer); } ArrayList<PackageSetting> getListOfIncompleteInstallPackagesLPr() { @@ -3088,9 +3040,9 @@ final class Settings { if (tagName.equals("package")) { readPackageLPw(parser); } else if (tagName.equals("permissions")) { - readPermissionsLPw(mPermissions, parser); + mPermissions.readPermissions(parser); } else if (tagName.equals("permission-trees")) { - readPermissionsLPw(mPermissionTrees, parser); + PermissionSettings.readPermissions(mPermissionTrees, parser); } else if (tagName.equals("shared-user")) { readSharedUserLPw(parser); } else if (tagName.equals("preferred-packages")) { @@ -3169,7 +3121,8 @@ final class Settings { } } else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) { final String enforcement = parser.getAttributeValue(null, ATTR_ENFORCEMENT); - mReadExternalStorageEnforced = "1".equals(enforcement); + mReadExternalStorageEnforced = + "1".equals(enforcement) ? Boolean.TRUE : Boolean.FALSE; } else if (tagName.equals("keyset-settings")) { mKeySetManagerService.readKeySetsLPw(parser, mKeySetRefs); } else if (TAG_VERSION.equals(tagName)) { @@ -3593,72 +3546,6 @@ final class Settings { } } - private int readInt(XmlPullParser parser, String ns, String name, int defValue) { - String v = parser.getAttributeValue(ns, name); - try { - if (v == null) { - return defValue; - } - return Integer.parseInt(v); - } catch (NumberFormatException e) { - PackageManagerService.reportSettingsProblem(Log.WARN, - "Error in package manager settings: attribute " + name - + " has bad integer value " + v + " at " - + parser.getPositionDescription()); - } - return defValue; - } - - private void readPermissionsLPw(ArrayMap<String, BasePermission> out, XmlPullParser parser) - throws IOException, XmlPullParserException { - int outerDepth = parser.getDepth(); - int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue; - } - - final String tagName = parser.getName(); - if (tagName.equals(TAG_ITEM)) { - final String name = parser.getAttributeValue(null, ATTR_NAME); - final String sourcePackage = parser.getAttributeValue(null, "package"); - final String ptype = parser.getAttributeValue(null, "type"); - if (name != null && sourcePackage != null) { - final boolean dynamic = "dynamic".equals(ptype); - BasePermission bp = out.get(name); - // If the permission is builtin, do not clobber it. - if (bp == null || bp.type != BasePermission.TYPE_BUILTIN) { - bp = new BasePermission(name.intern(), sourcePackage, - dynamic ? BasePermission.TYPE_DYNAMIC : BasePermission.TYPE_NORMAL); - } - bp.protectionLevel = readInt(parser, null, "protection", - PermissionInfo.PROTECTION_NORMAL); - bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel); - if (dynamic) { - PermissionInfo pi = new PermissionInfo(); - pi.packageName = sourcePackage.intern(); - pi.name = name.intern(); - pi.icon = readInt(parser, null, "icon", 0); - pi.nonLocalizedLabel = parser.getAttributeValue(null, "label"); - pi.protectionLevel = bp.protectionLevel; - bp.pendingInfo = pi; - } - out.put(bp.name, bp); - } else { - PackageManagerService.reportSettingsProblem(Log.WARN, - "Error in package manager settings: permissions has" + " no name at " - + parser.getPositionDescription()); - } - } else { - PackageManagerService.reportSettingsProblem(Log.WARN, - "Unknown element reading permissions: " + parser.getName() + " at " - + parser.getPositionDescription()); - } - XmlUtils.skipCurrentTag(parser); - } - } - private void readDisabledSysPackageLPw(XmlPullParser parser) throws XmlPullParserException, IOException { String name = parser.getAttributeValue(null, ATTR_NAME); @@ -4385,10 +4272,6 @@ final class Settings { return ps; } - private String compToString(ArraySet<String> cmp) { - return cmp != null ? Arrays.toString(cmp.toArray()) : "[]"; - } - boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, int flags, int userId) { final PackageSetting ps = mPackages.get(componentInfo.packageName); if (ps == null) return false; @@ -5001,45 +4884,8 @@ final class Settings { void dumpPermissionsLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames, DumpState dumpState) { - boolean printedSomething = false; - for (BasePermission p : mPermissions.values()) { - if (packageName != null && !packageName.equals(p.sourcePackage)) { - continue; - } - if (permissionNames != null && !permissionNames.contains(p.name)) { - continue; - } - if (!printedSomething) { - if (dumpState.onTitlePrinted()) - pw.println(); - pw.println("Permissions:"); - printedSomething = true; - } - pw.print(" Permission ["); pw.print(p.name); pw.print("] ("); - pw.print(Integer.toHexString(System.identityHashCode(p))); - pw.println("):"); - pw.print(" sourcePackage="); pw.println(p.sourcePackage); - pw.print(" uid="); pw.print(p.uid); - pw.print(" gids="); pw.print(Arrays.toString( - p.computeGids(UserHandle.USER_SYSTEM))); - pw.print(" type="); pw.print(p.type); - pw.print(" prot="); - pw.println(PermissionInfo.protectionToString(p.protectionLevel)); - if (p.perm != null) { - pw.print(" perm="); pw.println(p.perm); - if ((p.perm.info.flags & PermissionInfo.FLAG_INSTALLED) == 0 - || (p.perm.info.flags & PermissionInfo.FLAG_REMOVED) != 0) { - pw.print(" flags=0x"); pw.println(Integer.toHexString(p.perm.info.flags)); - } - } - if (p.packageSetting != null) { - pw.print(" packageSetting="); pw.println(p.packageSetting); - } - if (READ_EXTERNAL_STORAGE.equals(p.name)) { - pw.print(" enforced="); - pw.println(mReadExternalStorageEnforced); - } - } + mPermissions.dumpPermissions(pw, packageName, permissionNames, + (mReadExternalStorageEnforced == Boolean.TRUE), dumpState); } void dumpSharedUsersLPr(PrintWriter pw, String packageName, ArraySet<String> permissionNames, @@ -5248,7 +5094,7 @@ final class Settings { private final Handler mHandler = new MyHandler(); - private final Object mLock; + private final Object mPersistenceLock; @GuardedBy("mLock") private final SparseBooleanArray mWriteScheduled = new SparseBooleanArray(); @@ -5265,8 +5111,8 @@ final class Settings { // The mapping keys are user ids. private final SparseBooleanArray mDefaultPermissionsGranted = new SparseBooleanArray(); - public RuntimePermissionPersistence(Object lock) { - mLock = lock; + public RuntimePermissionPersistence(Object persistenceLock) { + mPersistenceLock = persistenceLock; } public boolean areDefaultRuntimPermissionsGrantedLPr(int userId) { @@ -5321,7 +5167,7 @@ final class Settings { ArrayMap<String, List<PermissionState>> permissionsForPackage = new ArrayMap<>(); ArrayMap<String, List<PermissionState>> permissionsForSharedUser = new ArrayMap<>(); - synchronized (mLock) { + synchronized (mPersistenceLock) { mWriteScheduled.delete(userId); final int packageCount = mPackages.size(); @@ -5470,7 +5316,7 @@ final class Settings { PermissionsState permissionsState = sb.getPermissionsState(); for (PermissionState permissionState : permissionsState.getRuntimePermissionStates(userId)) { - BasePermission bp = mPermissions.get(permissionState.getName()); + BasePermission bp = mPermissions.getPermission(permissionState.getName()); if (bp != null) { permissionsState.revokeRuntimePermission(bp, userId); permissionsState.updatePermissionFlags(bp, userId, @@ -5631,7 +5477,7 @@ final class Settings { switch (parser.getName()) { case TAG_ITEM: { String name = parser.getAttributeValue(null, ATTR_NAME); - BasePermission bp = mPermissions.get(name); + BasePermission bp = mPermissions.getPermission(name); if (bp == null) { Slog.w(PackageManagerService.TAG, "Unknown permission:" + name); XmlUtils.skipCurrentTag(parser); diff --git a/com/android/server/pm/SharedUserSetting.java b/com/android/server/pm/SharedUserSetting.java index 06e020a0..a0dadae3 100644 --- a/com/android/server/pm/SharedUserSetting.java +++ b/com/android/server/pm/SharedUserSetting.java @@ -16,12 +16,18 @@ package com.android.server.pm; +import android.annotation.Nullable; +import android.content.pm.PackageParser; import android.util.ArraySet; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + /** * Settings data for a particular shared user ID we know about. */ -final class SharedUserSetting extends SettingBase { +public final class SharedUserSetting extends SettingBase { final String name; int userId; @@ -73,4 +79,18 @@ final class SharedUserSetting extends SettingBase { setPrivateFlags(this.pkgPrivateFlags | packageSetting.pkgPrivateFlags); } } + + public @Nullable List<PackageParser.Package> getPackages() { + if (packages == null || packages.size() == 0) { + return null; + } + final ArrayList<PackageParser.Package> pkgList = new ArrayList<>(packages.size()); + for (PackageSetting ps : packages) { + if (ps == null) { + continue; + } + pkgList.add(ps.pkg); + } + return pkgList; + } } diff --git a/com/android/server/pm/ShortcutLauncher.java b/com/android/server/pm/ShortcutLauncher.java index 30608403..f922ad19 100644 --- a/com/android/server/pm/ShortcutLauncher.java +++ b/com/android/server/pm/ShortcutLauncher.java @@ -25,6 +25,7 @@ import android.util.ArraySet; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.pm.ShortcutService.DumpFilter; import com.android.server.pm.ShortcutUser.PackageWithUser; import org.json.JSONException; @@ -293,7 +294,7 @@ class ShortcutLauncher extends ShortcutPackageItem { return ret; } - public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { + public void dump(@NonNull PrintWriter pw, @NonNull String prefix, DumpFilter filter) { pw.println(); pw.print(prefix); diff --git a/com/android/server/pm/ShortcutPackage.java b/com/android/server/pm/ShortcutPackage.java index 6f70f4c8..6fc1e738 100644 --- a/com/android/server/pm/ShortcutPackage.java +++ b/com/android/server/pm/ShortcutPackage.java @@ -33,6 +33,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; +import com.android.server.pm.ShortcutService.DumpFilter; import com.android.server.pm.ShortcutService.ShortcutOperation; import com.android.server.pm.ShortcutService.Stats; @@ -1144,7 +1145,7 @@ class ShortcutPackage extends ShortcutPackageItem { return false; } - public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { + public void dump(@NonNull PrintWriter pw, @NonNull String prefix, DumpFilter filter) { pw.println(); pw.print(prefix); @@ -1186,9 +1187,7 @@ class ShortcutPackage extends ShortcutPackageItem { final int size = shortcuts.size(); for (int i = 0; i < size; i++) { final ShortcutInfo si = shortcuts.valueAt(i); - pw.print(prefix); - pw.print(" "); - pw.println(si.toInsecureString()); + pw.println(si.toDumpString(prefix + " ")); if (si.getBitmapPath() != null) { final long len = new File(si.getBitmapPath()).length(); pw.print(prefix); diff --git a/com/android/server/pm/ShortcutService.java b/com/android/server/pm/ShortcutService.java index 0e572d82..27560c5f 100644 --- a/com/android/server/pm/ShortcutService.java +++ b/com/android/server/pm/ShortcutService.java @@ -40,8 +40,8 @@ import android.content.pm.LauncherApps; import android.content.pm.LauncherApps.ShortcutQuery; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; import android.content.pm.ShortcutInfo; @@ -134,6 +134,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import java.util.function.Predicate; +import java.util.regex.Pattern; /** * TODO: @@ -484,12 +485,13 @@ public class ShortcutService extends IShortcutService.Stub { final private IUidObserver mUidObserver = new IUidObserver.Stub() { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) { - handleOnUidStateChanged(uid, procState); + injectPostToHandler(() -> handleOnUidStateChanged(uid, procState)); } @Override public void onUidGone(int uid, boolean disabled) { - handleOnUidStateChanged(uid, ActivityManager.PROCESS_STATE_NONEXISTENT); + injectPostToHandler(() -> + handleOnUidStateChanged(uid, ActivityManager.PROCESS_STATE_NONEXISTENT)); } @Override @@ -3454,121 +3456,265 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting void dumpNoCheck(FileDescriptor fd, PrintWriter pw, String[] args) { + final DumpFilter filter = parseDumpArgs(args); - boolean dumpMain = true; - boolean checkin = false; - boolean clear = false; - boolean dumpUid = false; - boolean dumpFiles = false; + if (filter.shouldDumpCheckIn()) { + // Other flags are not supported for checkin. + dumpCheckin(pw, filter.shouldCheckInClear()); + } else { + if (filter.shouldDumpMain()) { + dumpInner(pw, filter); + pw.println(); + } + if (filter.shouldDumpUid()) { + dumpUid(pw); + pw.println(); + } + if (filter.shouldDumpFiles()) { + dumpDumpFiles(pw); + pw.println(); + } + } + } - if (args != null) { - for (String arg : args) { - if ("-c".equals(arg)) { - checkin = true; + private static DumpFilter parseDumpArgs(String[] args) { + final DumpFilter filter = new DumpFilter(); + if (args == null) { + return filter; + } - } else if ("--checkin".equals(arg)) { - checkin = true; - clear = true; + int argIndex = 0; + while (argIndex < args.length) { + final String arg = args[argIndex++]; - } else if ("-a".equals(arg) || "--all".equals(arg)) { - dumpUid = true; - dumpFiles = true; + if ("-c".equals(arg)) { + filter.setDumpCheckIn(true); + continue; + } + if ("--checkin".equals(arg)) { + filter.setDumpCheckIn(true); + filter.setCheckInClear(true); + continue; + } + if ("-a".equals(arg) || "--all".equals(arg)) { + filter.setDumpUid(true); + filter.setDumpFiles(true); + continue; + } + if ("-u".equals(arg) || "--uid".equals(arg)) { + filter.setDumpUid(true); + continue; + } + if ("-f".equals(arg) || "--files".equals(arg)) { + filter.setDumpFiles(true); + continue; + } + if ("-n".equals(arg) || "--no-main".equals(arg)) { + filter.setDumpMain(false); + continue; + } + if ("--user".equals(arg)) { + if (argIndex >= args.length) { + throw new IllegalArgumentException("Missing user ID for --user"); + } + try { + filter.addUser(Integer.parseInt(args[argIndex++])); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid user ID", e); + } + continue; + } + if ("-p".equals(arg) || "--package".equals(arg)) { + if (argIndex >= args.length) { + throw new IllegalArgumentException("Missing package name for --package"); + } + filter.addPackageRegex(args[argIndex++]); + filter.setDumpDetails(false); + continue; + } + if (arg.startsWith("-")) { + throw new IllegalArgumentException("Unknown option " + arg); + } + break; + } + while (argIndex < args.length) { + filter.addPackage(args[argIndex++]); + } + return filter; + } - } else if ("-u".equals(arg) || "--uid".equals(arg)) { - dumpUid = true; + static class DumpFilter { + private boolean mDumpCheckIn = false; + private boolean mCheckInClear = false; - } else if ("-f".equals(arg) || "--files".equals(arg)) { - dumpFiles = true; + private boolean mDumpMain = true; + private boolean mDumpUid = false; + private boolean mDumpFiles = false; - } else if ("-n".equals(arg) || "--no-main".equals(arg)) { - dumpMain = false; + private boolean mDumpDetails = true; + private List<Pattern> mPackagePatterns = new ArrayList<>(); + private List<Integer> mUsers = new ArrayList<>(); + + void addPackageRegex(String regex) { + mPackagePatterns.add(Pattern.compile(regex)); + } + + public void addPackage(String packageName) { + addPackageRegex(Pattern.quote(packageName)); + } + + void addUser(int userId) { + mUsers.add(userId); + } + + boolean isPackageMatch(String packageName) { + if (mPackagePatterns.size() == 0) { + return true; + } + for (int i = 0; i < mPackagePatterns.size(); i++) { + if (mPackagePatterns.get(i).matcher(packageName).find()) { + return true; } } + return false; } - if (checkin) { - // Other flags are not supported for checkin. - dumpCheckin(pw, clear); - } else { - if (dumpMain) { - dumpInner(pw); - pw.println(); - } - if (dumpUid) { - dumpUid(pw); - pw.println(); + boolean isUserMatch(int userId) { + if (mUsers.size() == 0) { + return true; } - if (dumpFiles) { - dumpDumpFiles(pw); - pw.println(); + for (int i = 0; i < mUsers.size(); i++) { + if (mUsers.get(i) == userId) { + return true; + } } + return false; + } + + public boolean shouldDumpCheckIn() { + return mDumpCheckIn; + } + + public void setDumpCheckIn(boolean dumpCheckIn) { + mDumpCheckIn = dumpCheckIn; + } + + public boolean shouldCheckInClear() { + return mCheckInClear; + } + + public void setCheckInClear(boolean checkInClear) { + mCheckInClear = checkInClear; + } + + public boolean shouldDumpMain() { + return mDumpMain; + } + + public void setDumpMain(boolean dumpMain) { + mDumpMain = dumpMain; + } + + public boolean shouldDumpUid() { + return mDumpUid; + } + + public void setDumpUid(boolean dumpUid) { + mDumpUid = dumpUid; + } + + public boolean shouldDumpFiles() { + return mDumpFiles; + } + + public void setDumpFiles(boolean dumpFiles) { + mDumpFiles = dumpFiles; + } + + public boolean shouldDumpDetails() { + return mDumpDetails; + } + + public void setDumpDetails(boolean dumpDetails) { + mDumpDetails = dumpDetails; } } private void dumpInner(PrintWriter pw) { + dumpInner(pw, new DumpFilter()); + } + + private void dumpInner(PrintWriter pw, DumpFilter filter) { synchronized (mLock) { - final long now = injectCurrentTimeMillis(); - pw.print("Now: ["); - pw.print(now); - pw.print("] "); - pw.print(formatTime(now)); - - pw.print(" Raw last reset: ["); - pw.print(mRawLastResetTime); - pw.print("] "); - pw.print(formatTime(mRawLastResetTime)); - - final long last = getLastResetTimeLocked(); - pw.print(" Last reset: ["); - pw.print(last); - pw.print("] "); - pw.print(formatTime(last)); - - final long next = getNextResetTimeLocked(); - pw.print(" Next reset: ["); - pw.print(next); - pw.print("] "); - pw.print(formatTime(next)); - - pw.print(" Config:"); - pw.print(" Max icon dim: "); - pw.println(mMaxIconDimension); - pw.print(" Icon format: "); - pw.println(mIconPersistFormat); - pw.print(" Icon quality: "); - pw.println(mIconPersistQuality); - pw.print(" saveDelayMillis: "); - pw.println(mSaveDelayMillis); - pw.print(" resetInterval: "); - pw.println(mResetInterval); - pw.print(" maxUpdatesPerInterval: "); - pw.println(mMaxUpdatesPerInterval); - pw.print(" maxShortcutsPerActivity: "); - pw.println(mMaxShortcuts); - pw.println(); + if (filter.shouldDumpDetails()) { + final long now = injectCurrentTimeMillis(); + pw.print("Now: ["); + pw.print(now); + pw.print("] "); + pw.print(formatTime(now)); + + pw.print(" Raw last reset: ["); + pw.print(mRawLastResetTime); + pw.print("] "); + pw.print(formatTime(mRawLastResetTime)); + + final long last = getLastResetTimeLocked(); + pw.print(" Last reset: ["); + pw.print(last); + pw.print("] "); + pw.print(formatTime(last)); + + final long next = getNextResetTimeLocked(); + pw.print(" Next reset: ["); + pw.print(next); + pw.print("] "); + pw.print(formatTime(next)); + + pw.print(" Config:"); + pw.print(" Max icon dim: "); + pw.println(mMaxIconDimension); + pw.print(" Icon format: "); + pw.println(mIconPersistFormat); + pw.print(" Icon quality: "); + pw.println(mIconPersistQuality); + pw.print(" saveDelayMillis: "); + pw.println(mSaveDelayMillis); + pw.print(" resetInterval: "); + pw.println(mResetInterval); + pw.print(" maxUpdatesPerInterval: "); + pw.println(mMaxUpdatesPerInterval); + pw.print(" maxShortcutsPerActivity: "); + pw.println(mMaxShortcuts); + pw.println(); - pw.println(" Stats:"); - synchronized (mStatLock) { - for (int i = 0; i < Stats.COUNT; i++) { - dumpStatLS(pw, " ", i); + pw.println(" Stats:"); + synchronized (mStatLock) { + for (int i = 0; i < Stats.COUNT; i++) { + dumpStatLS(pw, " ", i); + } } - } - pw.println(); - pw.print(" #Failures: "); - pw.println(mWtfCount); + pw.println(); + pw.print(" #Failures: "); + pw.println(mWtfCount); - if (mLastWtfStacktrace != null) { - pw.print(" Last failure stack trace: "); - pw.println(Log.getStackTraceString(mLastWtfStacktrace)); - } + if (mLastWtfStacktrace != null) { + pw.print(" Last failure stack trace: "); + pw.println(Log.getStackTraceString(mLastWtfStacktrace)); + } - pw.println(); - mShortcutBitmapSaver.dumpLocked(pw, " "); + pw.println(); + mShortcutBitmapSaver.dumpLocked(pw, " "); - for (int i = 0; i < mUsers.size(); i++) { pw.println(); - mUsers.valueAt(i).dump(pw, " "); + } + + for (int i = 0; i < mUsers.size(); i++) { + final ShortcutUser user = mUsers.valueAt(i); + if (filter.isUserMatch(user.getUserId())) { + user.dump(pw, " ", filter); + pw.println(); + } } } } diff --git a/com/android/server/pm/ShortcutUser.java b/com/android/server/pm/ShortcutUser.java index 2c388c4a..55e6d28a 100644 --- a/com/android/server/pm/ShortcutUser.java +++ b/com/android/server/pm/ShortcutUser.java @@ -28,6 +28,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; +import com.android.server.pm.ShortcutService.DumpFilter; import com.android.server.pm.ShortcutService.InvalidFileFormatException; import libcore.util.Objects; @@ -531,44 +532,54 @@ class ShortcutUser { + " S=" + restoredShortcuts[0]); } - public void dump(@NonNull PrintWriter pw, @NonNull String prefix) { - pw.print(prefix); - pw.print("User: "); - pw.print(mUserId); - pw.print(" Known locales: "); - pw.print(mKnownLocales); - pw.print(" Last app scan: ["); - pw.print(mLastAppScanTime); - pw.print("] "); - pw.print(ShortcutService.formatTime(mLastAppScanTime)); - pw.print(" Last app scan FP: "); - pw.print(mLastAppScanOsFingerprint); - pw.println(); - - prefix += prefix + " "; - - pw.print(prefix); - pw.print("Cached launcher: "); - pw.print(mCachedLauncher); - pw.println(); - - pw.print(prefix); - pw.print("Last known launcher: "); - pw.print(mLastKnownLauncher); - pw.println(); + public void dump(@NonNull PrintWriter pw, @NonNull String prefix, DumpFilter filter) { + if (filter.shouldDumpDetails()) { + pw.print(prefix); + pw.print("User: "); + pw.print(mUserId); + pw.print(" Known locales: "); + pw.print(mKnownLocales); + pw.print(" Last app scan: ["); + pw.print(mLastAppScanTime); + pw.print("] "); + pw.print(ShortcutService.formatTime(mLastAppScanTime)); + pw.print(" Last app scan FP: "); + pw.print(mLastAppScanOsFingerprint); + pw.println(); + + prefix += prefix + " "; + + pw.print(prefix); + pw.print("Cached launcher: "); + pw.print(mCachedLauncher); + pw.println(); + + pw.print(prefix); + pw.print("Last known launcher: "); + pw.print(mLastKnownLauncher); + pw.println(); + } for (int i = 0; i < mLaunchers.size(); i++) { - mLaunchers.valueAt(i).dump(pw, prefix); + ShortcutLauncher launcher = mLaunchers.valueAt(i); + if (filter.isPackageMatch(launcher.getPackageName())) { + launcher.dump(pw, prefix, filter); + } } for (int i = 0; i < mPackages.size(); i++) { - mPackages.valueAt(i).dump(pw, prefix); + ShortcutPackage pkg = mPackages.valueAt(i); + if (filter.isPackageMatch(pkg.getPackageName())) { + pkg.dump(pw, prefix, filter); + } } - pw.println(); - pw.print(prefix); - pw.println("Bitmap directories: "); - dumpDirectorySize(pw, prefix + " ", mService.getUserBitmapFilePath(mUserId)); + if (filter.shouldDumpDetails()) { + pw.println(); + pw.print(prefix); + pw.println("Bitmap directories: "); + dumpDirectorySize(pw, prefix + " ", mService.getUserBitmapFilePath(mUserId)); + } } private void dumpDirectorySize(@NonNull PrintWriter pw, diff --git a/com/android/server/pm/UserManagerService.java b/com/android/server/pm/UserManagerService.java index f2d527b2..1e5245cf 100644 --- a/com/android/server/pm/UserManagerService.java +++ b/com/android/server/pm/UserManagerService.java @@ -1055,7 +1055,7 @@ public class UserManagerService extends IUserManager.Stub { /** Called by PackageManagerService */ public boolean exists(int userId) { - return getUserInfoNoChecks(userId) != null; + return mLocalService.exists(userId); } @Override @@ -3502,8 +3502,8 @@ public class UserManagerService extends IUserManager.Stub { * @param userId * @return whether the user has been initialized yet */ - boolean isInitialized(int userId) { - return (getUserInfo(userId).flags & UserInfo.FLAG_INITIALIZED) != 0; + boolean isUserInitialized(int userId) { + return mLocalService.isUserInitialized(userId); } private class LocalService extends UserManagerInternal { @@ -3715,6 +3715,16 @@ public class UserManagerService extends IUserManager.Stub { } return state == UserState.STATE_RUNNING_UNLOCKED; } + + @Override + public boolean isUserInitialized(int userId) { + return (getUserInfo(userId).flags & UserInfo.FLAG_INITIALIZED) != 0; + } + + @Override + public boolean exists(int userId) { + return getUserInfoNoChecks(userId) != null; + } } /* Remove all the users except of the system one. */ diff --git a/com/android/server/pm/permission/BasePermission.java b/com/android/server/pm/permission/BasePermission.java new file mode 100644 index 00000000..09a6e9c0 --- /dev/null +++ b/com/android/server/pm/permission/BasePermission.java @@ -0,0 +1,564 @@ +/* + * Copyright (C) 2006 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.pm.permission; + +import static android.Manifest.permission.READ_EXTERNAL_STORAGE; +import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS; +import static android.content.pm.PermissionInfo.PROTECTION_NORMAL; +import static android.content.pm.PermissionInfo.PROTECTION_SIGNATURE; +import static android.content.pm.PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM; + +import static com.android.server.pm.Settings.ATTR_NAME; +import static com.android.server.pm.Settings.ATTR_PACKAGE; +import static com.android.server.pm.Settings.TAG_ITEM; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.PackageParser; +import android.content.pm.PackageParser.Permission; +import android.content.pm.PermissionInfo; +import android.os.UserHandle; +import android.util.Log; +import android.util.Slog; + +import com.android.server.pm.DumpState; +import com.android.server.pm.PackageManagerService; +import com.android.server.pm.PackageSettingBase; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public final class BasePermission { + static final String TAG = "PackageManager"; + + public static final int TYPE_NORMAL = 0; + public static final int TYPE_BUILTIN = 1; + public static final int TYPE_DYNAMIC = 2; + @IntDef(value = { + TYPE_NORMAL, + TYPE_BUILTIN, + TYPE_DYNAMIC, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PermissionType {} + + @IntDef(value = { + PROTECTION_DANGEROUS, + PROTECTION_NORMAL, + PROTECTION_SIGNATURE, + PROTECTION_SIGNATURE_OR_SYSTEM, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ProtectionLevel {} + + final String name; + + @PermissionType final int type; + + String sourcePackageName; + + // TODO: Can we get rid of this? Seems we only use some signature info from the setting + PackageSettingBase sourcePackageSetting; + + int protectionLevel; + + PackageParser.Permission perm; + + PermissionInfo pendingPermissionInfo; + + /** UID that owns the definition of this permission */ + int uid; + + /** Additional GIDs given to apps granted this permission */ + private int[] gids; + + /** + * Flag indicating that {@link #gids} should be adjusted based on the + * {@link UserHandle} the granted app is running as. + */ + private boolean perUser; + + public BasePermission(String _name, String _sourcePackageName, @PermissionType int _type) { + name = _name; + sourcePackageName = _sourcePackageName; + type = _type; + // Default to most conservative protection level. + protectionLevel = PermissionInfo.PROTECTION_SIGNATURE; + } + + @Override + public String toString() { + return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + name + + "}"; + } + + public String getName() { + return name; + } + public int getProtectionLevel() { + return protectionLevel; + } + public String getSourcePackageName() { + return sourcePackageName; + } + public PackageSettingBase getSourcePackageSetting() { + return sourcePackageSetting; + } + public int getType() { + return type; + } + public int getUid() { + return uid; + } + public void setGids(int[] gids, boolean perUser) { + this.gids = gids; + this.perUser = perUser; + } + public void setPermission(@Nullable Permission perm) { + this.perm = perm; + } + public void setSourcePackageSetting(PackageSettingBase sourcePackageSetting) { + this.sourcePackageSetting = sourcePackageSetting; + } + + public int[] computeGids(int userId) { + if (perUser) { + final int[] userGids = new int[gids.length]; + for (int i = 0; i < gids.length; i++) { + userGids[i] = UserHandle.getUid(userId, gids[i]); + } + return userGids; + } else { + return gids; + } + } + + public int calculateFootprint(BasePermission perm) { + if (uid == perm.uid) { + return perm.name.length() + perm.perm.info.calculateFootprint(); + } + return 0; + } + + public boolean isPermission(Permission perm) { + return this.perm == perm; + } + + public boolean isDynamic() { + return type == TYPE_DYNAMIC; + } + + + public boolean isNormal() { + return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) + == PermissionInfo.PROTECTION_NORMAL; + } + public boolean isRuntime() { + return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) + == PermissionInfo.PROTECTION_DANGEROUS; + } + public boolean isSignature() { + return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) == + PermissionInfo.PROTECTION_SIGNATURE; + } + + public boolean isAppOp() { + return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0; + } + public boolean isDevelopment() { + return isSignature() + && (protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0; + } + public boolean isInstaller() { + return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0; + } + public boolean isInstant() { + return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0; + } + public boolean isOEM() { + return (protectionLevel & PermissionInfo.PROTECTION_FLAG_OEM) != 0; + } + public boolean isPre23() { + return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PRE23) != 0; + } + public boolean isPreInstalled() { + return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0; + } + public boolean isPrivileged() { + return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0; + } + public boolean isRuntimeOnly() { + return (protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0; + } + public boolean isSetup() { + return (protectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0; + } + public boolean isVerifier() { + return (protectionLevel & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0; + } + + public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) { + if (!origPackageName.equals(sourcePackageName)) { + return; + } + sourcePackageName = newPackageName; + sourcePackageSetting = null; + perm = null; + if (pendingPermissionInfo != null) { + pendingPermissionInfo.packageName = newPackageName; + } + uid = 0; + setGids(null, false); + } + + public boolean addToTree(@ProtectionLevel int protectionLevel, + @NonNull PermissionInfo info, @NonNull BasePermission tree) { + final boolean changed = + (this.protectionLevel != protectionLevel + || perm == null + || uid != tree.uid + || !perm.owner.equals(tree.perm.owner) + || !comparePermissionInfos(perm.info, info)); + this.protectionLevel = protectionLevel; + info = new PermissionInfo(info); + info.protectionLevel = protectionLevel; + perm = new PackageParser.Permission(tree.perm.owner, info); + perm.info.packageName = tree.perm.info.packageName; + uid = tree.uid; + return changed; + } + + public void updateDynamicPermission(Map<String, BasePermission> permissionTrees) { + if (PackageManagerService.DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name=" + + getName() + " pkg=" + getSourcePackageName() + + " info=" + pendingPermissionInfo); + if (sourcePackageSetting == null && pendingPermissionInfo != null) { + final BasePermission tree = findPermissionTreeLP(permissionTrees, name); + if (tree != null && tree.perm != null) { + sourcePackageSetting = tree.sourcePackageSetting; + perm = new PackageParser.Permission(tree.perm.owner, + new PermissionInfo(pendingPermissionInfo)); + perm.info.packageName = tree.perm.info.packageName; + perm.info.name = name; + uid = tree.uid; + } + } + } + + public static BasePermission createOrUpdate(@Nullable BasePermission bp, @NonNull Permission p, + @NonNull PackageParser.Package pkg, Map<String, BasePermission> permissionTrees, + boolean chatty) { + final PackageSettingBase pkgSetting = (PackageSettingBase) pkg.mExtras; + // Allow system apps to redefine non-system permissions + if (bp != null && !Objects.equals(bp.sourcePackageName, p.info.packageName)) { + final boolean currentOwnerIsSystem = (bp.perm != null + && bp.perm.owner.isSystemApp()); + if (p.owner.isSystemApp()) { + if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) { + // It's a built-in permission and no owner, take ownership now + bp.sourcePackageSetting = pkgSetting; + bp.perm = p; + bp.uid = pkg.applicationInfo.uid; + bp.sourcePackageName = p.info.packageName; + p.info.flags |= PermissionInfo.FLAG_INSTALLED; + } else if (!currentOwnerIsSystem) { + String msg = "New decl " + p.owner + " of permission " + + p.info.name + " is system; overriding " + bp.sourcePackageName; + PackageManagerService.reportSettingsProblem(Log.WARN, msg); + bp = null; + } + } + } + if (bp == null) { + bp = new BasePermission(p.info.name, p.info.packageName, TYPE_NORMAL); + } + StringBuilder r = null; + if (bp.perm == null) { + if (bp.sourcePackageName == null + || bp.sourcePackageName.equals(p.info.packageName)) { + final BasePermission tree = findPermissionTreeLP(permissionTrees, p.info.name); + if (tree == null + || tree.sourcePackageName.equals(p.info.packageName)) { + bp.sourcePackageSetting = pkgSetting; + bp.perm = p; + bp.uid = pkg.applicationInfo.uid; + bp.sourcePackageName = p.info.packageName; + p.info.flags |= PermissionInfo.FLAG_INSTALLED; + if (chatty) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append(p.info.name); + } + } else { + Slog.w(TAG, "Permission " + p.info.name + " from package " + + p.info.packageName + " ignored: base tree " + + tree.name + " is from package " + + tree.sourcePackageName); + } + } else { + Slog.w(TAG, "Permission " + p.info.name + " from package " + + p.info.packageName + " ignored: original from " + + bp.sourcePackageName); + } + } else if (chatty) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append("DUP:"); + r.append(p.info.name); + } + if (bp.perm == p) { + bp.protectionLevel = p.info.protectionLevel; + } + if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) { + Log.d(TAG, " Permissions: " + r); + } + return bp; + } + + public static BasePermission enforcePermissionTreeLP( + Map<String, BasePermission> permissionTrees, String permName, int callingUid) { + if (permName != null) { + BasePermission bp = findPermissionTreeLP(permissionTrees, permName); + if (bp != null) { + if (bp.uid == UserHandle.getAppId(callingUid)) {//UserHandle.getAppId(Binder.getCallingUid())) { + return bp; + } + throw new SecurityException("Calling uid " + callingUid + + " is not allowed to add to permission tree " + + bp.name + " owned by uid " + bp.uid); + } + } + throw new SecurityException("No permission tree found for " + permName); + } + + public void enforceDeclaredUsedAndRuntimeOrDevelopment(PackageParser.Package pkg) { + int index = pkg.requestedPermissions.indexOf(name); + if (index == -1) { + throw new SecurityException("Package " + pkg.packageName + + " has not requested permission " + name); + } + if (!isRuntime() && !isDevelopment()) { + throw new SecurityException("Permission " + name + + " is not a changeable permission type"); + } + } + + private static BasePermission findPermissionTreeLP( + Map<String, BasePermission> permissionTrees, String permName) { + for (BasePermission bp : permissionTrees.values()) { + if (permName.startsWith(bp.name) && + permName.length() > bp.name.length() && + permName.charAt(bp.name.length()) == '.') { + return bp; + } + } + return null; + } + + public @Nullable PermissionInfo generatePermissionInfo(@NonNull String groupName, int flags) { + if (groupName == null) { + if (perm == null || perm.info.group == null) { + return generatePermissionInfo(protectionLevel, flags); + } + } else { + if (perm != null && groupName.equals(perm.info.group)) { + return PackageParser.generatePermissionInfo(perm, flags); + } + } + return null; + } + + public @NonNull PermissionInfo generatePermissionInfo(int adjustedProtectionLevel, int flags) { + final boolean protectionLevelChanged = protectionLevel != adjustedProtectionLevel; + // if we return different protection level, don't use the cached info + if (perm != null && !protectionLevelChanged) { + return PackageParser.generatePermissionInfo(perm, flags); + } + final PermissionInfo pi = new PermissionInfo(); + pi.name = name; + pi.packageName = sourcePackageName; + pi.nonLocalizedLabel = name; + pi.protectionLevel = protectionLevelChanged ? adjustedProtectionLevel : protectionLevel; + return pi; + } + + public static boolean readLPw(@NonNull Map<String, BasePermission> out, + @NonNull XmlPullParser parser) { + final String tagName = parser.getName(); + if (!tagName.equals(TAG_ITEM)) { + return false; + } + final String name = parser.getAttributeValue(null, ATTR_NAME); + final String sourcePackage = parser.getAttributeValue(null, ATTR_PACKAGE); + final String ptype = parser.getAttributeValue(null, "type"); + if (name == null || sourcePackage == null) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: permissions has" + " no name at " + + parser.getPositionDescription()); + return false; + } + final boolean dynamic = "dynamic".equals(ptype); + BasePermission bp = out.get(name); + // If the permission is builtin, do not clobber it. + if (bp == null || bp.type != TYPE_BUILTIN) { + bp = new BasePermission(name.intern(), sourcePackage, + dynamic ? TYPE_DYNAMIC : TYPE_NORMAL); + } + bp.protectionLevel = readInt(parser, null, "protection", + PermissionInfo.PROTECTION_NORMAL); + bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel); + if (dynamic) { + final PermissionInfo pi = new PermissionInfo(); + pi.packageName = sourcePackage.intern(); + pi.name = name.intern(); + pi.icon = readInt(parser, null, "icon", 0); + pi.nonLocalizedLabel = parser.getAttributeValue(null, "label"); + pi.protectionLevel = bp.protectionLevel; + bp.pendingPermissionInfo = pi; + } + out.put(bp.name, bp); + return true; + } + + private static int readInt(XmlPullParser parser, String ns, String name, int defValue) { + String v = parser.getAttributeValue(ns, name); + try { + if (v == null) { + return defValue; + } + return Integer.parseInt(v); + } catch (NumberFormatException e) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: attribute " + name + + " has bad integer value " + v + " at " + + parser.getPositionDescription()); + } + return defValue; + } + + public void writeLPr(@NonNull XmlSerializer serializer) throws IOException { + if (sourcePackageName == null) { + return; + } + serializer.startTag(null, TAG_ITEM); + serializer.attribute(null, ATTR_NAME, name); + serializer.attribute(null, ATTR_PACKAGE, sourcePackageName); + if (protectionLevel != PermissionInfo.PROTECTION_NORMAL) { + serializer.attribute(null, "protection", Integer.toString(protectionLevel)); + } + if (type == BasePermission.TYPE_DYNAMIC) { + final PermissionInfo pi = perm != null ? perm.info : pendingPermissionInfo; + if (pi != null) { + serializer.attribute(null, "type", "dynamic"); + if (pi.icon != 0) { + serializer.attribute(null, "icon", Integer.toString(pi.icon)); + } + if (pi.nonLocalizedLabel != null) { + serializer.attribute(null, "label", pi.nonLocalizedLabel.toString()); + } + } + } + serializer.endTag(null, TAG_ITEM); + } + + private static boolean compareStrings(CharSequence s1, CharSequence s2) { + if (s1 == null) { + return s2 == null; + } + if (s2 == null) { + return false; + } + if (s1.getClass() != s2.getClass()) { + return false; + } + return s1.equals(s2); + } + + private static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) { + if (pi1.icon != pi2.icon) return false; + if (pi1.logo != pi2.logo) return false; + if (pi1.protectionLevel != pi2.protectionLevel) return false; + if (!compareStrings(pi1.name, pi2.name)) return false; + if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false; + // We'll take care of setting this one. + if (!compareStrings(pi1.packageName, pi2.packageName)) return false; + // These are not currently stored in settings. + //if (!compareStrings(pi1.group, pi2.group)) return false; + //if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false; + //if (pi1.labelRes != pi2.labelRes) return false; + //if (pi1.descriptionRes != pi2.descriptionRes) return false; + return true; + } + + public boolean dumpPermissionsLPr(@NonNull PrintWriter pw, @NonNull String packageName, + @NonNull Set<String> permissionNames, boolean readEnforced, + boolean printedSomething, @NonNull DumpState dumpState) { + if (packageName != null && !packageName.equals(sourcePackageName)) { + return false; + } + if (permissionNames != null && !permissionNames.contains(name)) { + return false; + } + if (!printedSomething) { + if (dumpState.onTitlePrinted()) + pw.println(); + pw.println("Permissions:"); + printedSomething = true; + } + pw.print(" Permission ["); pw.print(name); pw.print("] ("); + pw.print(Integer.toHexString(System.identityHashCode(this))); + pw.println("):"); + pw.print(" sourcePackage="); pw.println(sourcePackageName); + pw.print(" uid="); pw.print(uid); + pw.print(" gids="); pw.print(Arrays.toString( + computeGids(UserHandle.USER_SYSTEM))); + pw.print(" type="); pw.print(type); + pw.print(" prot="); + pw.println(PermissionInfo.protectionToString(protectionLevel)); + if (perm != null) { + pw.print(" perm="); pw.println(perm); + if ((perm.info.flags & PermissionInfo.FLAG_INSTALLED) == 0 + || (perm.info.flags & PermissionInfo.FLAG_REMOVED) != 0) { + pw.print(" flags=0x"); pw.println(Integer.toHexString(perm.info.flags)); + } + } + if (sourcePackageSetting != null) { + pw.print(" packageSetting="); pw.println(sourcePackageSetting); + } + if (READ_EXTERNAL_STORAGE.equals(name)) { + pw.print(" enforced="); + pw.println(readEnforced); + } + return true; + } +} diff --git a/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java new file mode 100644 index 00000000..161efd38 --- /dev/null +++ b/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -0,0 +1,1296 @@ +/* + * Copyright (C) 2015 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.pm.permission; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.DownloadManager; +import android.app.admin.DevicePolicyManager; +import android.companion.CompanionDeviceManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; +import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; +import android.content.pm.PackageParser; +import android.content.pm.ProviderInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.PackageManagerInternal.PackagesProvider; +import android.content.pm.PackageManagerInternal.SyncAdapterPackagesProvider; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.Binder; +import android.os.Build; +import android.os.Environment; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.UserHandle; +import android.os.storage.StorageManager; +import android.print.PrintManager; +import android.provider.CalendarContract; +import android.provider.ContactsContract; +import android.provider.MediaStore; +import android.provider.Telephony.Sms.Intents; +import android.telephony.TelephonyManager; +import android.security.Credentials; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Log; +import android.util.Slog; +import android.util.Xml; +import com.android.internal.util.XmlUtils; +import com.android.server.LocalServices; +import com.android.server.pm.PackageManagerService; +import com.android.server.pm.PackageSetting; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static android.os.Process.FIRST_APPLICATION_UID; + +/** + * This class is the policy for granting runtime permissions to + * platform components and default handlers in the system such + * that the device is usable out-of-the-box. For example, the + * shell UID is a part of the system and the Phone app should + * have phone related permission by default. + * <p> + * NOTE: This class is at the wrong abstraction level. It is a part of the package manager + * service but knows about lots of higher level subsystems. The correct way to do this is + * to have an interface defined in the package manager but have the impl next to other + * policy stuff like PhoneWindowManager + */ +public final class DefaultPermissionGrantPolicy { + private static final String TAG = "DefaultPermGrantPolicy"; // must be <= 23 chars + private static final boolean DEBUG = false; + + private static final int DEFAULT_FLAGS = + PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.MATCH_UNINSTALLED_PACKAGES; + + private static final String AUDIO_MIME_TYPE = "audio/mpeg"; + + private static final String TAG_EXCEPTIONS = "exceptions"; + private static final String TAG_EXCEPTION = "exception"; + private static final String TAG_PERMISSION = "permission"; + private static final String ATTR_PACKAGE = "package"; + private static final String ATTR_NAME = "name"; + private static final String ATTR_FIXED = "fixed"; + + private static final Set<String> PHONE_PERMISSIONS = new ArraySet<>(); + static { + PHONE_PERMISSIONS.add(Manifest.permission.READ_PHONE_STATE); + PHONE_PERMISSIONS.add(Manifest.permission.CALL_PHONE); + PHONE_PERMISSIONS.add(Manifest.permission.READ_CALL_LOG); + PHONE_PERMISSIONS.add(Manifest.permission.WRITE_CALL_LOG); + PHONE_PERMISSIONS.add(Manifest.permission.ADD_VOICEMAIL); + PHONE_PERMISSIONS.add(Manifest.permission.USE_SIP); + PHONE_PERMISSIONS.add(Manifest.permission.PROCESS_OUTGOING_CALLS); + } + + private static final Set<String> CONTACTS_PERMISSIONS = new ArraySet<>(); + static { + CONTACTS_PERMISSIONS.add(Manifest.permission.READ_CONTACTS); + CONTACTS_PERMISSIONS.add(Manifest.permission.WRITE_CONTACTS); + CONTACTS_PERMISSIONS.add(Manifest.permission.GET_ACCOUNTS); + } + + private static final Set<String> LOCATION_PERMISSIONS = new ArraySet<>(); + static { + LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_FINE_LOCATION); + LOCATION_PERMISSIONS.add(Manifest.permission.ACCESS_COARSE_LOCATION); + } + + private static final Set<String> CALENDAR_PERMISSIONS = new ArraySet<>(); + static { + CALENDAR_PERMISSIONS.add(Manifest.permission.READ_CALENDAR); + CALENDAR_PERMISSIONS.add(Manifest.permission.WRITE_CALENDAR); + } + + private static final Set<String> SMS_PERMISSIONS = new ArraySet<>(); + static { + SMS_PERMISSIONS.add(Manifest.permission.SEND_SMS); + SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_SMS); + SMS_PERMISSIONS.add(Manifest.permission.READ_SMS); + SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_WAP_PUSH); + SMS_PERMISSIONS.add(Manifest.permission.RECEIVE_MMS); + SMS_PERMISSIONS.add(Manifest.permission.READ_CELL_BROADCASTS); + } + + private static final Set<String> MICROPHONE_PERMISSIONS = new ArraySet<>(); + static { + MICROPHONE_PERMISSIONS.add(Manifest.permission.RECORD_AUDIO); + } + + private static final Set<String> CAMERA_PERMISSIONS = new ArraySet<>(); + static { + CAMERA_PERMISSIONS.add(Manifest.permission.CAMERA); + } + + private static final Set<String> SENSORS_PERMISSIONS = new ArraySet<>(); + static { + SENSORS_PERMISSIONS.add(Manifest.permission.BODY_SENSORS); + } + + private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>(); + static { + STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE); + STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); + } + + private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1; + + private static final String ACTION_TRACK = "com.android.fitness.TRACK"; + + private final Handler mHandler; + + private PackagesProvider mLocationPackagesProvider; + private PackagesProvider mVoiceInteractionPackagesProvider; + private PackagesProvider mSmsAppPackagesProvider; + private PackagesProvider mDialerAppPackagesProvider; + private PackagesProvider mSimCallManagerPackagesProvider; + private SyncAdapterPackagesProvider mSyncAdapterPackagesProvider; + + private ArrayMap<String, List<DefaultPermissionGrant>> mGrantExceptions; + private final Context mContext; + private final Object mLock = new Object(); + private final PackageManagerInternal mServiceInternal; + private final PermissionManagerService mPermissionManager; + private final DefaultPermissionGrantedCallback mPermissionGrantedCallback; + public interface DefaultPermissionGrantedCallback { + /** Callback when permissions have been granted */ + public void onDefaultRuntimePermissionsGranted(int userId); + } + + public DefaultPermissionGrantPolicy(Context context, Looper looper, + @Nullable DefaultPermissionGrantedCallback callback, + @NonNull PermissionManagerService permissionManager) { + mContext = context; + mHandler = new Handler(looper) { + @Override + public void handleMessage(Message msg) { + if (msg.what == MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS) { + synchronized (mLock) { + if (mGrantExceptions == null) { + mGrantExceptions = readDefaultPermissionExceptionsLocked(); + } + } + } + } + }; + mPermissionGrantedCallback = callback; + mPermissionManager = permissionManager; + mServiceInternal = LocalServices.getService(PackageManagerInternal.class); + } + + public void setLocationPackagesProvider(PackagesProvider provider) { + synchronized (mLock) { + mLocationPackagesProvider = provider; + } + } + + public void setVoiceInteractionPackagesProvider(PackagesProvider provider) { + synchronized (mLock) { + mVoiceInteractionPackagesProvider = provider; + } + } + + public void setSmsAppPackagesProvider(PackagesProvider provider) { + synchronized (mLock) { + mSmsAppPackagesProvider = provider; + } + } + + public void setDialerAppPackagesProvider(PackagesProvider provider) { + synchronized (mLock) { + mDialerAppPackagesProvider = provider; + } + } + + public void setSimCallManagerPackagesProvider(PackagesProvider provider) { + synchronized (mLock) { + mSimCallManagerPackagesProvider = provider; + } + } + + public void setSyncAdapterPackagesProvider(SyncAdapterPackagesProvider provider) { + synchronized (mLock) { + mSyncAdapterPackagesProvider = provider; + } + } + + public void grantDefaultPermissions(Collection<PackageParser.Package> packages, int userId) { + if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) { + grantAllRuntimePermissions(packages, userId); + } else { + grantPermissionsToSysComponentsAndPrivApps(packages, userId); + grantDefaultSystemHandlerPermissions(userId); + grantDefaultPermissionExceptions(userId); + } + } + + private void grantRuntimePermissionsForPackage(int userId, PackageParser.Package pkg) { + Set<String> permissions = new ArraySet<>(); + for (String permission : pkg.requestedPermissions) { + final BasePermission bp = mPermissionManager.getPermission(permission); + if (bp == null) { + continue; + } + if (bp.isRuntime()) { + permissions.add(permission); + } + } + if (!permissions.isEmpty()) { + grantRuntimePermissions(pkg, permissions, true, userId); + } + } + + private void grantAllRuntimePermissions( + Collection<PackageParser.Package> packages, int userId) { + Log.i(TAG, "Granting all runtime permissions for user " + userId); + for (PackageParser.Package pkg : packages) { + grantRuntimePermissionsForPackage(userId, pkg); + } + } + + public void scheduleReadDefaultPermissionExceptions() { + mHandler.sendEmptyMessage(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS); + } + + private void grantPermissionsToSysComponentsAndPrivApps( + Collection<PackageParser.Package> packages, int userId) { + Log.i(TAG, "Granting permissions to platform components for user " + userId); + for (PackageParser.Package pkg : packages) { + if (!isSysComponentOrPersistentPlatformSignedPrivApp(pkg) + || !doesPackageSupportRuntimePermissions(pkg) + || pkg.requestedPermissions.isEmpty()) { + continue; + } + grantRuntimePermissionsForPackage(userId, pkg); + } + } + + private void grantDefaultSystemHandlerPermissions(int userId) { + Log.i(TAG, "Granting permissions to default platform handlers for user " + userId); + + final PackagesProvider locationPackagesProvider; + final PackagesProvider voiceInteractionPackagesProvider; + final PackagesProvider smsAppPackagesProvider; + final PackagesProvider dialerAppPackagesProvider; + final PackagesProvider simCallManagerPackagesProvider; + final SyncAdapterPackagesProvider syncAdapterPackagesProvider; + + synchronized (mLock) { + locationPackagesProvider = mLocationPackagesProvider; + voiceInteractionPackagesProvider = mVoiceInteractionPackagesProvider; + smsAppPackagesProvider = mSmsAppPackagesProvider; + dialerAppPackagesProvider = mDialerAppPackagesProvider; + simCallManagerPackagesProvider = mSimCallManagerPackagesProvider; + syncAdapterPackagesProvider = mSyncAdapterPackagesProvider; + } + + String[] voiceInteractPackageNames = (voiceInteractionPackagesProvider != null) + ? voiceInteractionPackagesProvider.getPackages(userId) : null; + String[] locationPackageNames = (locationPackagesProvider != null) + ? locationPackagesProvider.getPackages(userId) : null; + String[] smsAppPackageNames = (smsAppPackagesProvider != null) + ? smsAppPackagesProvider.getPackages(userId) : null; + String[] dialerAppPackageNames = (dialerAppPackagesProvider != null) + ? dialerAppPackagesProvider.getPackages(userId) : null; + String[] simCallManagerPackageNames = (simCallManagerPackagesProvider != null) + ? simCallManagerPackagesProvider.getPackages(userId) : null; + String[] contactsSyncAdapterPackages = (syncAdapterPackagesProvider != null) ? + syncAdapterPackagesProvider.getPackages(ContactsContract.AUTHORITY, userId) : null; + String[] calendarSyncAdapterPackages = (syncAdapterPackagesProvider != null) ? + syncAdapterPackagesProvider.getPackages(CalendarContract.AUTHORITY, userId) : null; + + // Installer + final String installerPackageName = mServiceInternal.getKnownPackageName( + PackageManagerInternal.PACKAGE_INSTALLER, userId); + PackageParser.Package installerPackage = getSystemPackage(installerPackageName); + if (installerPackage != null + && doesPackageSupportRuntimePermissions(installerPackage)) { + grantRuntimePermissions(installerPackage, STORAGE_PERMISSIONS, true, userId); + } + + // Verifier + final String verifierPackageName = mServiceInternal.getKnownPackageName( + PackageManagerInternal.PACKAGE_VERIFIER, userId); + PackageParser.Package verifierPackage = getSystemPackage(verifierPackageName); + if (verifierPackage != null + && doesPackageSupportRuntimePermissions(verifierPackage)) { + grantRuntimePermissions(verifierPackage, STORAGE_PERMISSIONS, true, userId); + grantRuntimePermissions(verifierPackage, PHONE_PERMISSIONS, false, userId); + grantRuntimePermissions(verifierPackage, SMS_PERMISSIONS, false, userId); + } + + // SetupWizard + final String setupWizardPackageName = mServiceInternal.getKnownPackageName( + PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId); + PackageParser.Package setupPackage = getSystemPackage(setupWizardPackageName); + if (setupPackage != null + && doesPackageSupportRuntimePermissions(setupPackage)) { + grantRuntimePermissions(setupPackage, PHONE_PERMISSIONS, userId); + grantRuntimePermissions(setupPackage, CONTACTS_PERMISSIONS, userId); + grantRuntimePermissions(setupPackage, LOCATION_PERMISSIONS, userId); + grantRuntimePermissions(setupPackage, CAMERA_PERMISSIONS, userId); + } + + // Camera + Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + PackageParser.Package cameraPackage = getDefaultSystemHandlerActivityPackage( + cameraIntent, userId); + if (cameraPackage != null + && doesPackageSupportRuntimePermissions(cameraPackage)) { + grantRuntimePermissions(cameraPackage, CAMERA_PERMISSIONS, userId); + grantRuntimePermissions(cameraPackage, MICROPHONE_PERMISSIONS, userId); + grantRuntimePermissions(cameraPackage, STORAGE_PERMISSIONS, userId); + } + + // Media provider + PackageParser.Package mediaStorePackage = getDefaultProviderAuthorityPackage( + MediaStore.AUTHORITY, userId); + if (mediaStorePackage != null) { + grantRuntimePermissions(mediaStorePackage, STORAGE_PERMISSIONS, true, userId); + } + + // Downloads provider + PackageParser.Package downloadsPackage = getDefaultProviderAuthorityPackage( + "downloads", userId); + if (downloadsPackage != null) { + grantRuntimePermissions(downloadsPackage, STORAGE_PERMISSIONS, true, userId); + } + + // Downloads UI + Intent downloadsUiIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS); + PackageParser.Package downloadsUiPackage = getDefaultSystemHandlerActivityPackage( + downloadsUiIntent, userId); + if (downloadsUiPackage != null + && doesPackageSupportRuntimePermissions(downloadsUiPackage)) { + grantRuntimePermissions(downloadsUiPackage, STORAGE_PERMISSIONS, true, userId); + } + + // Storage provider + PackageParser.Package storagePackage = getDefaultProviderAuthorityPackage( + "com.android.externalstorage.documents", userId); + if (storagePackage != null) { + grantRuntimePermissions(storagePackage, STORAGE_PERMISSIONS, true, userId); + } + + // CertInstaller + Intent certInstallerIntent = new Intent(Credentials.INSTALL_ACTION); + PackageParser.Package certInstallerPackage = getDefaultSystemHandlerActivityPackage( + certInstallerIntent, userId); + if (certInstallerPackage != null + && doesPackageSupportRuntimePermissions(certInstallerPackage)) { + grantRuntimePermissions(certInstallerPackage, STORAGE_PERMISSIONS, true, userId); + } + + // Dialer + if (dialerAppPackageNames == null) { + Intent dialerIntent = new Intent(Intent.ACTION_DIAL); + PackageParser.Package dialerPackage = getDefaultSystemHandlerActivityPackage( + dialerIntent, userId); + if (dialerPackage != null) { + grantDefaultPermissionsToDefaultSystemDialerApp(dialerPackage, userId); + } + } else { + for (String dialerAppPackageName : dialerAppPackageNames) { + PackageParser.Package dialerPackage = getSystemPackage(dialerAppPackageName); + if (dialerPackage != null) { + grantDefaultPermissionsToDefaultSystemDialerApp(dialerPackage, userId); + } + } + } + + // Sim call manager + if (simCallManagerPackageNames != null) { + for (String simCallManagerPackageName : simCallManagerPackageNames) { + PackageParser.Package simCallManagerPackage = + getSystemPackage(simCallManagerPackageName); + if (simCallManagerPackage != null) { + grantDefaultPermissionsToDefaultSimCallManager(simCallManagerPackage, + userId); + } + } + } + + // SMS + if (smsAppPackageNames == null) { + Intent smsIntent = new Intent(Intent.ACTION_MAIN); + smsIntent.addCategory(Intent.CATEGORY_APP_MESSAGING); + PackageParser.Package smsPackage = getDefaultSystemHandlerActivityPackage( + smsIntent, userId); + if (smsPackage != null) { + grantDefaultPermissionsToDefaultSystemSmsApp(smsPackage, userId); + } + } else { + for (String smsPackageName : smsAppPackageNames) { + PackageParser.Package smsPackage = getSystemPackage(smsPackageName); + if (smsPackage != null) { + grantDefaultPermissionsToDefaultSystemSmsApp(smsPackage, userId); + } + } + } + + // Cell Broadcast Receiver + Intent cbrIntent = new Intent(Intents.SMS_CB_RECEIVED_ACTION); + PackageParser.Package cbrPackage = + getDefaultSystemHandlerActivityPackage(cbrIntent, userId); + if (cbrPackage != null && doesPackageSupportRuntimePermissions(cbrPackage)) { + grantRuntimePermissions(cbrPackage, SMS_PERMISSIONS, userId); + } + + // Carrier Provisioning Service + Intent carrierProvIntent = new Intent(Intents.SMS_CARRIER_PROVISION_ACTION); + PackageParser.Package carrierProvPackage = + getDefaultSystemHandlerServicePackage(carrierProvIntent, userId); + if (carrierProvPackage != null + && doesPackageSupportRuntimePermissions(carrierProvPackage)) { + grantRuntimePermissions(carrierProvPackage, SMS_PERMISSIONS, false, userId); + } + + // Calendar + Intent calendarIntent = new Intent(Intent.ACTION_MAIN); + calendarIntent.addCategory(Intent.CATEGORY_APP_CALENDAR); + PackageParser.Package calendarPackage = getDefaultSystemHandlerActivityPackage( + calendarIntent, userId); + if (calendarPackage != null + && doesPackageSupportRuntimePermissions(calendarPackage)) { + grantRuntimePermissions(calendarPackage, CALENDAR_PERMISSIONS, userId); + grantRuntimePermissions(calendarPackage, CONTACTS_PERMISSIONS, userId); + } + + // Calendar provider + PackageParser.Package calendarProviderPackage = getDefaultProviderAuthorityPackage( + CalendarContract.AUTHORITY, userId); + if (calendarProviderPackage != null) { + grantRuntimePermissions(calendarProviderPackage, CONTACTS_PERMISSIONS, userId); + grantRuntimePermissions(calendarProviderPackage, CALENDAR_PERMISSIONS, + true, userId); + grantRuntimePermissions(calendarProviderPackage, STORAGE_PERMISSIONS, userId); + } + + // Calendar provider sync adapters + List<PackageParser.Package> calendarSyncAdapters = getHeadlessSyncAdapterPackages( + calendarSyncAdapterPackages, userId); + final int calendarSyncAdapterCount = calendarSyncAdapters.size(); + for (int i = 0; i < calendarSyncAdapterCount; i++) { + PackageParser.Package calendarSyncAdapter = calendarSyncAdapters.get(i); + if (doesPackageSupportRuntimePermissions(calendarSyncAdapter)) { + grantRuntimePermissions(calendarSyncAdapter, CALENDAR_PERMISSIONS, userId); + } + } + + // Contacts + Intent contactsIntent = new Intent(Intent.ACTION_MAIN); + contactsIntent.addCategory(Intent.CATEGORY_APP_CONTACTS); + PackageParser.Package contactsPackage = getDefaultSystemHandlerActivityPackage( + contactsIntent, userId); + if (contactsPackage != null + && doesPackageSupportRuntimePermissions(contactsPackage)) { + grantRuntimePermissions(contactsPackage, CONTACTS_PERMISSIONS, userId); + grantRuntimePermissions(contactsPackage, PHONE_PERMISSIONS, userId); + } + + // Contacts provider sync adapters + List<PackageParser.Package> contactsSyncAdapters = getHeadlessSyncAdapterPackages( + contactsSyncAdapterPackages, userId); + final int contactsSyncAdapterCount = contactsSyncAdapters.size(); + for (int i = 0; i < contactsSyncAdapterCount; i++) { + PackageParser.Package contactsSyncAdapter = contactsSyncAdapters.get(i); + if (doesPackageSupportRuntimePermissions(contactsSyncAdapter)) { + grantRuntimePermissions(contactsSyncAdapter, CONTACTS_PERMISSIONS, userId); + } + } + + // Contacts provider + PackageParser.Package contactsProviderPackage = getDefaultProviderAuthorityPackage( + ContactsContract.AUTHORITY, userId); + if (contactsProviderPackage != null) { + grantRuntimePermissions(contactsProviderPackage, CONTACTS_PERMISSIONS, + true, userId); + grantRuntimePermissions(contactsProviderPackage, PHONE_PERMISSIONS, + true, userId); + grantRuntimePermissions(contactsProviderPackage, STORAGE_PERMISSIONS, userId); + } + + // Device provisioning + Intent deviceProvisionIntent = new Intent( + DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE); + PackageParser.Package deviceProvisionPackage = + getDefaultSystemHandlerActivityPackage(deviceProvisionIntent, userId); + if (deviceProvisionPackage != null + && doesPackageSupportRuntimePermissions(deviceProvisionPackage)) { + grantRuntimePermissions(deviceProvisionPackage, CONTACTS_PERMISSIONS, userId); + } + + // Maps + Intent mapsIntent = new Intent(Intent.ACTION_MAIN); + mapsIntent.addCategory(Intent.CATEGORY_APP_MAPS); + PackageParser.Package mapsPackage = getDefaultSystemHandlerActivityPackage( + mapsIntent, userId); + if (mapsPackage != null + && doesPackageSupportRuntimePermissions(mapsPackage)) { + grantRuntimePermissions(mapsPackage, LOCATION_PERMISSIONS, userId); + } + + // Gallery + Intent galleryIntent = new Intent(Intent.ACTION_MAIN); + galleryIntent.addCategory(Intent.CATEGORY_APP_GALLERY); + PackageParser.Package galleryPackage = getDefaultSystemHandlerActivityPackage( + galleryIntent, userId); + if (galleryPackage != null + && doesPackageSupportRuntimePermissions(galleryPackage)) { + grantRuntimePermissions(galleryPackage, STORAGE_PERMISSIONS, userId); + } + + // Email + Intent emailIntent = new Intent(Intent.ACTION_MAIN); + emailIntent.addCategory(Intent.CATEGORY_APP_EMAIL); + PackageParser.Package emailPackage = getDefaultSystemHandlerActivityPackage( + emailIntent, userId); + if (emailPackage != null + && doesPackageSupportRuntimePermissions(emailPackage)) { + grantRuntimePermissions(emailPackage, CONTACTS_PERMISSIONS, userId); + grantRuntimePermissions(emailPackage, CALENDAR_PERMISSIONS, userId); + } + + // Browser + PackageParser.Package browserPackage = null; + String defaultBrowserPackage = mServiceInternal.getKnownPackageName( + PackageManagerInternal.PACKAGE_BROWSER, userId); + if (defaultBrowserPackage != null) { + browserPackage = getPackage(defaultBrowserPackage); + } + if (browserPackage == null) { + Intent browserIntent = new Intent(Intent.ACTION_MAIN); + browserIntent.addCategory(Intent.CATEGORY_APP_BROWSER); + browserPackage = getDefaultSystemHandlerActivityPackage( + browserIntent, userId); + } + if (browserPackage != null + && doesPackageSupportRuntimePermissions(browserPackage)) { + grantRuntimePermissions(browserPackage, LOCATION_PERMISSIONS, userId); + } + + // Voice interaction + if (voiceInteractPackageNames != null) { + for (String voiceInteractPackageName : voiceInteractPackageNames) { + PackageParser.Package voiceInteractPackage = getSystemPackage( + voiceInteractPackageName); + if (voiceInteractPackage != null + && doesPackageSupportRuntimePermissions(voiceInteractPackage)) { + grantRuntimePermissions(voiceInteractPackage, + CONTACTS_PERMISSIONS, userId); + grantRuntimePermissions(voiceInteractPackage, + CALENDAR_PERMISSIONS, userId); + grantRuntimePermissions(voiceInteractPackage, + MICROPHONE_PERMISSIONS, userId); + grantRuntimePermissions(voiceInteractPackage, + PHONE_PERMISSIONS, userId); + grantRuntimePermissions(voiceInteractPackage, + SMS_PERMISSIONS, userId); + grantRuntimePermissions(voiceInteractPackage, + LOCATION_PERMISSIONS, userId); + } + } + } + + if (ActivityManager.isLowRamDeviceStatic()) { + // Allow voice search on low-ram devices + Intent globalSearchIntent = new Intent("android.search.action.GLOBAL_SEARCH"); + PackageParser.Package globalSearchPickerPackage = + getDefaultSystemHandlerActivityPackage(globalSearchIntent, userId); + + if (globalSearchPickerPackage != null + && doesPackageSupportRuntimePermissions(globalSearchPickerPackage)) { + grantRuntimePermissions(globalSearchPickerPackage, + MICROPHONE_PERMISSIONS, true, userId); + grantRuntimePermissions(globalSearchPickerPackage, + LOCATION_PERMISSIONS, true, userId); + } + } + + // Voice recognition + Intent voiceRecoIntent = new Intent("android.speech.RecognitionService"); + voiceRecoIntent.addCategory(Intent.CATEGORY_DEFAULT); + PackageParser.Package voiceRecoPackage = getDefaultSystemHandlerServicePackage( + voiceRecoIntent, userId); + if (voiceRecoPackage != null + && doesPackageSupportRuntimePermissions(voiceRecoPackage)) { + grantRuntimePermissions(voiceRecoPackage, MICROPHONE_PERMISSIONS, userId); + } + + // Location + if (locationPackageNames != null) { + for (String packageName : locationPackageNames) { + PackageParser.Package locationPackage = getSystemPackage(packageName); + if (locationPackage != null + && doesPackageSupportRuntimePermissions(locationPackage)) { + grantRuntimePermissions(locationPackage, CONTACTS_PERMISSIONS, userId); + grantRuntimePermissions(locationPackage, CALENDAR_PERMISSIONS, userId); + grantRuntimePermissions(locationPackage, MICROPHONE_PERMISSIONS, userId); + grantRuntimePermissions(locationPackage, PHONE_PERMISSIONS, userId); + grantRuntimePermissions(locationPackage, SMS_PERMISSIONS, userId); + grantRuntimePermissions(locationPackage, LOCATION_PERMISSIONS, + true, userId); + grantRuntimePermissions(locationPackage, CAMERA_PERMISSIONS, userId); + grantRuntimePermissions(locationPackage, SENSORS_PERMISSIONS, userId); + grantRuntimePermissions(locationPackage, STORAGE_PERMISSIONS, userId); + } + } + } + + // Music + Intent musicIntent = new Intent(Intent.ACTION_VIEW); + musicIntent.addCategory(Intent.CATEGORY_DEFAULT); + musicIntent.setDataAndType(Uri.fromFile(new File("foo.mp3")), + AUDIO_MIME_TYPE); + PackageParser.Package musicPackage = getDefaultSystemHandlerActivityPackage( + musicIntent, userId); + if (musicPackage != null + && doesPackageSupportRuntimePermissions(musicPackage)) { + grantRuntimePermissions(musicPackage, STORAGE_PERMISSIONS, userId); + } + + // Home + Intent homeIntent = new Intent(Intent.ACTION_MAIN); + homeIntent.addCategory(Intent.CATEGORY_HOME); + homeIntent.addCategory(Intent.CATEGORY_LAUNCHER_APP); + PackageParser.Package homePackage = getDefaultSystemHandlerActivityPackage( + homeIntent, userId); + if (homePackage != null + && doesPackageSupportRuntimePermissions(homePackage)) { + grantRuntimePermissions(homePackage, LOCATION_PERMISSIONS, false, userId); + } + + // Watches + if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0)) { + // Home application on watches + Intent wearHomeIntent = new Intent(Intent.ACTION_MAIN); + wearHomeIntent.addCategory(Intent.CATEGORY_HOME_MAIN); + + PackageParser.Package wearHomePackage = getDefaultSystemHandlerActivityPackage( + wearHomeIntent, userId); + + if (wearHomePackage != null + && doesPackageSupportRuntimePermissions(wearHomePackage)) { + grantRuntimePermissions(wearHomePackage, CONTACTS_PERMISSIONS, false, + userId); + grantRuntimePermissions(wearHomePackage, PHONE_PERMISSIONS, true, userId); + grantRuntimePermissions(wearHomePackage, MICROPHONE_PERMISSIONS, false, + userId); + grantRuntimePermissions(wearHomePackage, LOCATION_PERMISSIONS, false, + userId); + } + + // Fitness tracking on watches + Intent trackIntent = new Intent(ACTION_TRACK); + PackageParser.Package trackPackage = getDefaultSystemHandlerActivityPackage( + trackIntent, userId); + if (trackPackage != null + && doesPackageSupportRuntimePermissions(trackPackage)) { + grantRuntimePermissions(trackPackage, SENSORS_PERMISSIONS, false, userId); + grantRuntimePermissions(trackPackage, LOCATION_PERMISSIONS, false, userId); + } + } + + // Print Spooler + PackageParser.Package printSpoolerPackage = getSystemPackage( + PrintManager.PRINT_SPOOLER_PACKAGE_NAME); + if (printSpoolerPackage != null + && doesPackageSupportRuntimePermissions(printSpoolerPackage)) { + grantRuntimePermissions(printSpoolerPackage, LOCATION_PERMISSIONS, true, userId); + } + + // EmergencyInfo + Intent emergencyInfoIntent = new Intent(TelephonyManager.ACTION_EMERGENCY_ASSISTANCE); + PackageParser.Package emergencyInfoPckg = getDefaultSystemHandlerActivityPackage( + emergencyInfoIntent, userId); + if (emergencyInfoPckg != null + && doesPackageSupportRuntimePermissions(emergencyInfoPckg)) { + grantRuntimePermissions(emergencyInfoPckg, CONTACTS_PERMISSIONS, true, userId); + grantRuntimePermissions(emergencyInfoPckg, PHONE_PERMISSIONS, true, userId); + } + + // NFC Tag viewer + Intent nfcTagIntent = new Intent(Intent.ACTION_VIEW); + nfcTagIntent.setType("vnd.android.cursor.item/ndef_msg"); + PackageParser.Package nfcTagPkg = getDefaultSystemHandlerActivityPackage( + nfcTagIntent, userId); + if (nfcTagPkg != null + && doesPackageSupportRuntimePermissions(nfcTagPkg)) { + grantRuntimePermissions(nfcTagPkg, CONTACTS_PERMISSIONS, false, userId); + grantRuntimePermissions(nfcTagPkg, PHONE_PERMISSIONS, false, userId); + } + + // Storage Manager + Intent storageManagerIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE); + PackageParser.Package storageManagerPckg = getDefaultSystemHandlerActivityPackage( + storageManagerIntent, userId); + if (storageManagerPckg != null + && doesPackageSupportRuntimePermissions(storageManagerPckg)) { + grantRuntimePermissions(storageManagerPckg, STORAGE_PERMISSIONS, true, userId); + } + + // Companion devices + PackageParser.Package companionDeviceDiscoveryPackage = getSystemPackage( + CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME); + if (companionDeviceDiscoveryPackage != null + && doesPackageSupportRuntimePermissions(companionDeviceDiscoveryPackage)) { + grantRuntimePermissions(companionDeviceDiscoveryPackage, + LOCATION_PERMISSIONS, true, userId); + } + + // Ringtone Picker + Intent ringtonePickerIntent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER); + PackageParser.Package ringtonePickerPackage = + getDefaultSystemHandlerActivityPackage(ringtonePickerIntent, userId); + if (ringtonePickerPackage != null + && doesPackageSupportRuntimePermissions(ringtonePickerPackage)) { + grantRuntimePermissions(ringtonePickerPackage, + STORAGE_PERMISSIONS, true, userId); + } + + if (mPermissionGrantedCallback != null) { + mPermissionGrantedCallback.onDefaultRuntimePermissionsGranted(userId); + } + } + + private void grantDefaultPermissionsToDefaultSystemDialerApp( + PackageParser.Package dialerPackage, int userId) { + if (doesPackageSupportRuntimePermissions(dialerPackage)) { + boolean isPhonePermFixed = + mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0); + grantRuntimePermissions( + dialerPackage, PHONE_PERMISSIONS, isPhonePermFixed, userId); + grantRuntimePermissions(dialerPackage, CONTACTS_PERMISSIONS, userId); + grantRuntimePermissions(dialerPackage, SMS_PERMISSIONS, userId); + grantRuntimePermissions(dialerPackage, MICROPHONE_PERMISSIONS, userId); + grantRuntimePermissions(dialerPackage, CAMERA_PERMISSIONS, userId); + } + } + + private void grantDefaultPermissionsToDefaultSystemSmsApp( + PackageParser.Package smsPackage, int userId) { + if (doesPackageSupportRuntimePermissions(smsPackage)) { + grantRuntimePermissions(smsPackage, PHONE_PERMISSIONS, userId); + grantRuntimePermissions(smsPackage, CONTACTS_PERMISSIONS, userId); + grantRuntimePermissions(smsPackage, SMS_PERMISSIONS, userId); + grantRuntimePermissions(smsPackage, STORAGE_PERMISSIONS, userId); + grantRuntimePermissions(smsPackage, MICROPHONE_PERMISSIONS, userId); + grantRuntimePermissions(smsPackage, CAMERA_PERMISSIONS, userId); + } + } + + public void grantDefaultPermissionsToDefaultSmsApp(String packageName, int userId) { + Log.i(TAG, "Granting permissions to default sms app for user:" + userId); + if (packageName == null) { + return; + } + PackageParser.Package smsPackage = getPackage(packageName); + if (smsPackage != null && doesPackageSupportRuntimePermissions(smsPackage)) { + grantRuntimePermissions(smsPackage, PHONE_PERMISSIONS, false, true, userId); + grantRuntimePermissions(smsPackage, CONTACTS_PERMISSIONS, false, true, userId); + grantRuntimePermissions(smsPackage, SMS_PERMISSIONS, false, true, userId); + grantRuntimePermissions(smsPackage, STORAGE_PERMISSIONS, false, true, userId); + grantRuntimePermissions(smsPackage, MICROPHONE_PERMISSIONS, false, true, userId); + grantRuntimePermissions(smsPackage, CAMERA_PERMISSIONS, false, true, userId); + } + } + + public void grantDefaultPermissionsToDefaultDialerApp(String packageName, int userId) { + Log.i(TAG, "Granting permissions to default dialer app for user:" + userId); + if (packageName == null) { + return; + } + PackageParser.Package dialerPackage = getPackage(packageName); + if (dialerPackage != null + && doesPackageSupportRuntimePermissions(dialerPackage)) { + grantRuntimePermissions(dialerPackage, PHONE_PERMISSIONS, false, true, userId); + grantRuntimePermissions(dialerPackage, CONTACTS_PERMISSIONS, false, true, userId); + grantRuntimePermissions(dialerPackage, SMS_PERMISSIONS, false, true, userId); + grantRuntimePermissions(dialerPackage, MICROPHONE_PERMISSIONS, false, true, userId); + grantRuntimePermissions(dialerPackage, CAMERA_PERMISSIONS, false, true, userId); + } + } + + private void grantDefaultPermissionsToDefaultSimCallManager( + PackageParser.Package simCallManagerPackage, int userId) { + Log.i(TAG, "Granting permissions to sim call manager for user:" + userId); + if (doesPackageSupportRuntimePermissions(simCallManagerPackage)) { + grantRuntimePermissions(simCallManagerPackage, PHONE_PERMISSIONS, userId); + grantRuntimePermissions(simCallManagerPackage, MICROPHONE_PERMISSIONS, userId); + } + } + + public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) { + if (packageName == null) { + return; + } + PackageParser.Package simCallManagerPackage = getPackage(packageName); + if (simCallManagerPackage != null) { + grantDefaultPermissionsToDefaultSimCallManager(simCallManagerPackage, userId); + } + } + + public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId) { + Log.i(TAG, "Granting permissions to enabled carrier apps for user:" + userId); + if (packageNames == null) { + return; + } + for (String packageName : packageNames) { + PackageParser.Package carrierPackage = getSystemPackage(packageName); + if (carrierPackage != null + && doesPackageSupportRuntimePermissions(carrierPackage)) { + grantRuntimePermissions(carrierPackage, PHONE_PERMISSIONS, userId); + grantRuntimePermissions(carrierPackage, LOCATION_PERMISSIONS, userId); + grantRuntimePermissions(carrierPackage, SMS_PERMISSIONS, userId); + } + } + } + + public void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId) { + Log.i(TAG, "Granting permissions to enabled ImsServices for user:" + userId); + if (packageNames == null) { + return; + } + for (String packageName : packageNames) { + PackageParser.Package imsServicePackage = getSystemPackage(packageName); + if (imsServicePackage != null + && doesPackageSupportRuntimePermissions(imsServicePackage)) { + grantRuntimePermissions(imsServicePackage, PHONE_PERMISSIONS, userId); + grantRuntimePermissions(imsServicePackage, MICROPHONE_PERMISSIONS, userId); + grantRuntimePermissions(imsServicePackage, LOCATION_PERMISSIONS, userId); + grantRuntimePermissions(imsServicePackage, CAMERA_PERMISSIONS, userId); + } + } + } + + public void grantDefaultPermissionsToDefaultBrowser(String packageName, int userId) { + Log.i(TAG, "Granting permissions to default browser for user:" + userId); + if (packageName == null) { + return; + } + PackageParser.Package browserPackage = getSystemPackage(packageName); + if (browserPackage != null + && doesPackageSupportRuntimePermissions(browserPackage)) { + grantRuntimePermissions(browserPackage, LOCATION_PERMISSIONS, false, false, userId); + } + } + + private PackageParser.Package getDefaultSystemHandlerActivityPackage( + Intent intent, int userId) { + ResolveInfo handler = mServiceInternal.resolveIntent(intent, + intent.resolveType(mContext.getContentResolver()), DEFAULT_FLAGS, userId, false); + if (handler == null || handler.activityInfo == null) { + return null; + } + if (mServiceInternal.isResolveActivityComponent(handler.activityInfo)) { + return null; + } + return getSystemPackage(handler.activityInfo.packageName); + } + + private PackageParser.Package getDefaultSystemHandlerServicePackage( + Intent intent, int userId) { + List<ResolveInfo> handlers = mServiceInternal.queryIntentServices( + intent, DEFAULT_FLAGS, Binder.getCallingUid(), userId); + if (handlers == null) { + return null; + } + final int handlerCount = handlers.size(); + for (int i = 0; i < handlerCount; i++) { + ResolveInfo handler = handlers.get(i); + PackageParser.Package handlerPackage = getSystemPackage( + handler.serviceInfo.packageName); + if (handlerPackage != null) { + return handlerPackage; + } + } + return null; + } + + private List<PackageParser.Package> getHeadlessSyncAdapterPackages( + String[] syncAdapterPackageNames, int userId) { + List<PackageParser.Package> syncAdapterPackages = new ArrayList<>(); + + Intent homeIntent = new Intent(Intent.ACTION_MAIN); + homeIntent.addCategory(Intent.CATEGORY_LAUNCHER); + + for (String syncAdapterPackageName : syncAdapterPackageNames) { + homeIntent.setPackage(syncAdapterPackageName); + + ResolveInfo homeActivity = mServiceInternal.resolveIntent(homeIntent, + homeIntent.resolveType(mContext.getContentResolver()), DEFAULT_FLAGS, + userId, false); + if (homeActivity != null) { + continue; + } + + PackageParser.Package syncAdapterPackage = getSystemPackage(syncAdapterPackageName); + if (syncAdapterPackage != null) { + syncAdapterPackages.add(syncAdapterPackage); + } + } + + return syncAdapterPackages; + } + + private PackageParser.Package getDefaultProviderAuthorityPackage( + String authority, int userId) { + ProviderInfo provider = + mServiceInternal.resolveContentProvider(authority, DEFAULT_FLAGS, userId); + if (provider != null) { + return getSystemPackage(provider.packageName); + } + return null; + } + + private PackageParser.Package getPackage(String packageName) { + return mServiceInternal.getPackage(packageName); + } + + private PackageParser.Package getSystemPackage(String packageName) { + PackageParser.Package pkg = getPackage(packageName); + if (pkg != null && pkg.isSystemApp()) { + return !isSysComponentOrPersistentPlatformSignedPrivApp(pkg) ? pkg : null; + } + return null; + } + + private void grantRuntimePermissions(PackageParser.Package pkg, Set<String> permissions, + int userId) { + grantRuntimePermissions(pkg, permissions, false, false, userId); + } + + private void grantRuntimePermissions(PackageParser.Package pkg, Set<String> permissions, + boolean systemFixed, int userId) { + grantRuntimePermissions(pkg, permissions, systemFixed, false, userId); + } + + private void grantRuntimePermissions(PackageParser.Package pkg, Set<String> permissions, + boolean systemFixed, boolean isDefaultPhoneOrSms, int userId) { + if (pkg.requestedPermissions.isEmpty()) { + return; + } + + List<String> requestedPermissions = pkg.requestedPermissions; + Set<String> grantablePermissions = null; + + // If this is the default Phone or SMS app we grant permissions regardless + // whether the version on the system image declares the permission as used since + // selecting the app as the default Phone or SMS the user makes a deliberate + // choice to grant this app the permissions needed to function. For all other + // apps, (default grants on first boot and user creation) we don't grant default + // permissions if the version on the system image does not declare them. + if (!isDefaultPhoneOrSms && pkg.isUpdatedSystemApp()) { + final PackageParser.Package disabledPkg = + mServiceInternal.getDisabledPackage(pkg.packageName); + if (disabledPkg != null) { + if (disabledPkg.requestedPermissions.isEmpty()) { + return; + } + if (!requestedPermissions.equals(disabledPkg.requestedPermissions)) { + grantablePermissions = new ArraySet<>(requestedPermissions); + requestedPermissions = disabledPkg.requestedPermissions; + } + } + } + + final int grantablePermissionCount = requestedPermissions.size(); + for (int i = 0; i < grantablePermissionCount; i++) { + String permission = requestedPermissions.get(i); + + // If there is a disabled system app it may request a permission the updated + // version ot the data partition doesn't, In this case skip the permission. + if (grantablePermissions != null && !grantablePermissions.contains(permission)) { + continue; + } + + if (permissions.contains(permission)) { + final int flags = mServiceInternal.getPermissionFlagsTEMP( + permission, pkg.packageName, userId); + + // If any flags are set to the permission, then it is either set in + // its current state by the system or device/profile owner or the user. + // In all these cases we do not want to clobber the current state. + // Unless the caller wants to override user choices. The override is + // to make sure we can grant the needed permission to the default + // sms and phone apps after the user chooses this in the UI. + if (flags == 0 || isDefaultPhoneOrSms) { + // Never clobber policy or system. + final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED + | PackageManager.FLAG_PERMISSION_POLICY_FIXED; + if ((flags & fixedFlags) != 0) { + continue; + } + + mServiceInternal.grantRuntimePermission( + pkg.packageName, permission, userId, false); + if (DEBUG) { + Log.i(TAG, "Granted " + (systemFixed ? "fixed " : "not fixed ") + + permission + " to default handler " + pkg.packageName); + } + + int newFlags = PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; + if (systemFixed) { + newFlags |= PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; + } + + mServiceInternal.updatePermissionFlagsTEMP(permission, pkg.packageName, + newFlags, newFlags, userId); + } + + // If a component gets a permission for being the default handler A + // and also default handler B, we grant the weaker grant form. + if ((flags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0 + && (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0 + && !systemFixed) { + if (DEBUG) { + Log.i(TAG, "Granted not fixed " + permission + " to default handler " + + pkg.packageName); + } + mServiceInternal.updatePermissionFlagsTEMP(permission, pkg.packageName, + PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, userId); + } + } + } + } + + private boolean isSysComponentOrPersistentPlatformSignedPrivApp(PackageParser.Package pkg) { + if (UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) { + return true; + } + if (!pkg.isPrivilegedApp()) { + return false; + } + final PackageParser.Package disabledPkg = + mServiceInternal.getDisabledPackage(pkg.packageName); + if (disabledPkg != null) { + if ((disabledPkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) { + return false; + } + } else if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) { + return false; + } + final String systemPackageName = mServiceInternal.getKnownPackageName( + PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM); + final PackageParser.Package systemPackage = getPackage(systemPackageName); + return PackageManagerService.compareSignatures(systemPackage.mSignatures, + pkg.mSignatures) == PackageManager.SIGNATURE_MATCH; + } + + private void grantDefaultPermissionExceptions(int userId) { + mHandler.removeMessages(MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS); + + synchronized (mLock) { + // mGrantExceptions is null only before the first read and then + // it serves as a cache of the default grants that should be + // performed for every user. If there is an entry then the app + // is on the system image and supports runtime permissions. + if (mGrantExceptions == null) { + mGrantExceptions = readDefaultPermissionExceptionsLocked(); + } + } + + Set<String> permissions = null; + final int exceptionCount = mGrantExceptions.size(); + for (int i = 0; i < exceptionCount; i++) { + String packageName = mGrantExceptions.keyAt(i); + PackageParser.Package pkg = getSystemPackage(packageName); + List<DefaultPermissionGrant> permissionGrants = mGrantExceptions.valueAt(i); + final int permissionGrantCount = permissionGrants.size(); + for (int j = 0; j < permissionGrantCount; j++) { + DefaultPermissionGrant permissionGrant = permissionGrants.get(j); + if (permissions == null) { + permissions = new ArraySet<>(); + } else { + permissions.clear(); + } + permissions.add(permissionGrant.name); + grantRuntimePermissions(pkg, permissions, + permissionGrant.fixed, userId); + } + } + } + + private File[] getDefaultPermissionFiles() { + ArrayList<File> ret = new ArrayList<File>(); + File dir = new File(Environment.getRootDirectory(), "etc/default-permissions"); + if (dir.isDirectory() && dir.canRead()) { + Collections.addAll(ret, dir.listFiles()); + } + dir = new File(Environment.getVendorDirectory(), "etc/default-permissions"); + if (dir.isDirectory() && dir.canRead()) { + Collections.addAll(ret, dir.listFiles()); + } + return ret.isEmpty() ? null : ret.toArray(new File[0]); + } + + private @NonNull ArrayMap<String, List<DefaultPermissionGrant>> + readDefaultPermissionExceptionsLocked() { + File[] files = getDefaultPermissionFiles(); + if (files == null) { + return new ArrayMap<>(0); + } + + ArrayMap<String, List<DefaultPermissionGrant>> grantExceptions = new ArrayMap<>(); + + // Iterate over the files in the directory and scan .xml files + for (File file : files) { + if (!file.getPath().endsWith(".xml")) { + Slog.i(TAG, "Non-xml file " + file + + " in " + file.getParent() + " directory, ignoring"); + continue; + } + if (!file.canRead()) { + Slog.w(TAG, "Default permissions file " + file + " cannot be read"); + continue; + } + try ( + InputStream str = new BufferedInputStream(new FileInputStream(file)) + ) { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(str, null); + parse(parser, grantExceptions); + } catch (XmlPullParserException | IOException e) { + Slog.w(TAG, "Error reading default permissions file " + file, e); + } + } + + return grantExceptions; + } + + private void parse(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>> + outGrantExceptions) throws IOException, XmlPullParserException { + final int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + if (TAG_EXCEPTIONS.equals(parser.getName())) { + parseExceptions(parser, outGrantExceptions); + } else { + Log.e(TAG, "Unknown tag " + parser.getName()); + } + } + } + + private void parseExceptions(XmlPullParser parser, Map<String, List<DefaultPermissionGrant>> + outGrantExceptions) throws IOException, XmlPullParserException { + final int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + if (TAG_EXCEPTION.equals(parser.getName())) { + String packageName = parser.getAttributeValue(null, ATTR_PACKAGE); + + List<DefaultPermissionGrant> packageExceptions = + outGrantExceptions.get(packageName); + if (packageExceptions == null) { + // The package must be on the system image + PackageParser.Package pkg = getSystemPackage(packageName); + if (pkg == null) { + Log.w(TAG, "Unknown package:" + packageName); + XmlUtils.skipCurrentTag(parser); + continue; + } + + // The package must support runtime permissions + if (!doesPackageSupportRuntimePermissions(pkg)) { + Log.w(TAG, "Skipping non supporting runtime permissions package:" + + packageName); + XmlUtils.skipCurrentTag(parser); + continue; + } + packageExceptions = new ArrayList<>(); + outGrantExceptions.put(packageName, packageExceptions); + } + + parsePermission(parser, packageExceptions); + } else { + Log.e(TAG, "Unknown tag " + parser.getName() + "under <exceptions>"); + } + } + } + + private void parsePermission(XmlPullParser parser, List<DefaultPermissionGrant> + outPackageExceptions) throws IOException, XmlPullParserException { + final int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + if (TAG_PERMISSION.contains(parser.getName())) { + String name = parser.getAttributeValue(null, ATTR_NAME); + if (name == null) { + Log.w(TAG, "Mandatory name attribute missing for permission tag"); + XmlUtils.skipCurrentTag(parser); + continue; + } + + final boolean fixed = XmlUtils.readBooleanAttribute(parser, ATTR_FIXED); + + DefaultPermissionGrant exception = new DefaultPermissionGrant(name, fixed); + outPackageExceptions.add(exception); + } else { + Log.e(TAG, "Unknown tag " + parser.getName() + "under <exception>"); + } + } + } + + private static boolean doesPackageSupportRuntimePermissions(PackageParser.Package pkg) { + return pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1; + } + + private static final class DefaultPermissionGrant { + final String name; + final boolean fixed; + + public DefaultPermissionGrant(String name, boolean fixed) { + this.name = name; + this.fixed = fixed; + } + } +} diff --git a/com/android/server/pm/permission/PermissionManagerInternal.java b/com/android/server/pm/permission/PermissionManagerInternal.java new file mode 100644 index 00000000..3b20b42b --- /dev/null +++ b/com/android/server/pm/permission/PermissionManagerInternal.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2017 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.pm.permission; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.PackageParser; +import android.content.pm.PermissionInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.PackageManager.PermissionInfoFlags; +import android.content.pm.PackageParser.Permission; + +import com.android.server.pm.SharedUserSetting; +import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Internal interfaces to be used by other components within the system server. + */ +public abstract class PermissionManagerInternal { + /** + * Callbacks invoked when interesting actions have been taken on a permission. + * <p> + * NOTE: The current arguments are merely to support the existing use cases. This + * needs to be properly thought out with appropriate arguments for each of the + * callback methods. + */ + public static class PermissionCallback { + public void onGidsChanged(int appId, int userId) { + } + public void onPermissionChanged() { + } + public void onPermissionGranted(int uid, int userId) { + } + public void onInstallPermissionGranted() { + } + public void onPermissionRevoked(int uid, int userId) { + } + public void onInstallPermissionRevoked() { + } + public void onPermissionUpdated(int userId) { + } + public void onPermissionRemoved() { + } + public void onInstallPermissionUpdated() { + } + } + + public abstract void grantRuntimePermission( + @NonNull String permName, @NonNull String packageName, boolean overridePolicy, + int callingUid, int userId, @Nullable PermissionCallback callback); + public abstract void grantRuntimePermissionsGrantedToDisabledPackage( + @NonNull PackageParser.Package pkg, int callingUid, + @Nullable PermissionCallback callback); + public abstract void grantRequestedRuntimePermissions( + @NonNull PackageParser.Package pkg, @NonNull int[] userIds, + @NonNull String[] grantedPermissions, int callingUid, + @Nullable PermissionCallback callback); + public abstract void revokeRuntimePermission(@NonNull String permName, + @NonNull String packageName, boolean overridePolicy, int callingUid, int userId, + @Nullable PermissionCallback callback); + public abstract int[] revokeUnusedSharedUserPermissions(@NonNull SharedUserSetting suSetting, + @NonNull int[] allUserIds); + + + public abstract boolean addPermission(@NonNull PermissionInfo info, boolean async, + int callingUid, @Nullable PermissionCallback callback); + public abstract void removePermission(@NonNull String permName, int callingUid, + @Nullable PermissionCallback callback); + + public abstract int getPermissionFlags(@NonNull String permName, + @NonNull String packageName, int callingUid, int userId); + /** + * Retrieve all of the information we know about a particular permission. + */ + public abstract @Nullable PermissionInfo getPermissionInfo(@NonNull String permName, + @NonNull String packageName, @PermissionInfoFlags int flags, int callingUid); + /** + * Retrieve all of the permissions associated with a particular group. + */ + public abstract @Nullable List<PermissionInfo> getPermissionInfoByGroup(@NonNull String group, + @PermissionInfoFlags int flags, int callingUid); + public abstract boolean isPermissionAppOp(@NonNull String permName); + public abstract boolean isPermissionInstant(@NonNull String permName); + + /** + * Updates the flags associated with a permission by replacing the flags in + * the specified mask with the provided flag values. + */ + public abstract void updatePermissionFlags(@NonNull String permName, + @NonNull String packageName, int flagMask, int flagValues, int callingUid, int userId, + @Nullable PermissionCallback callback); + /** + * Updates the flags for all applications by replacing the flags in the specified mask + * with the provided flag values. + */ + public abstract boolean updatePermissionFlagsForAllApps(int flagMask, int flagValues, + int callingUid, int userId, @NonNull Collection<PackageParser.Package> packages, + @Nullable PermissionCallback callback); + + public abstract int checkPermission(@NonNull String permName, @NonNull String packageName, + int callingUid, int userId); + + /** + * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS + * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userid} is not for the caller. + * @param checkShell whether to prevent shell from access if there's a debugging restriction + * @param message the message to log on security exception + */ + public abstract void enforceCrossUserPermission(int callingUid, int userId, + boolean requireFullPermission, boolean checkShell, @NonNull String message); + public abstract void enforceGrantRevokeRuntimePermissionPermissions(@NonNull String message); + + public abstract @NonNull PermissionSettings getPermissionSettings(); + public abstract @NonNull DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy(); + + /** HACK HACK methods to allow for partial migration of data to the PermissionManager class */ + public abstract Iterator<BasePermission> getPermissionIteratorTEMP(); + public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName); + public abstract void putPermissionTEMP(@NonNull String permName, + @NonNull BasePermission permission); +}
\ No newline at end of file diff --git a/com/android/server/pm/permission/PermissionManagerService.java b/com/android/server/pm/permission/PermissionManagerService.java new file mode 100644 index 00000000..6c031a6a --- /dev/null +++ b/com/android/server/pm/permission/PermissionManagerService.java @@ -0,0 +1,1081 @@ +/* + * Copyright (C) 2017 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.pm.permission; + +import static android.Manifest.permission.READ_EXTERNAL_STORAGE; +import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.AppOpsManager; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; +import android.content.pm.PackageParser; +import android.content.pm.ParceledListSlice; +import android.content.pm.PermissionInfo; +import android.content.pm.PackageParser.Package; +import android.os.Binder; +import android.os.Build; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Process; +import android.os.UserHandle; +import android.os.UserManager; +import android.os.UserManagerInternal; +import android.os.storage.StorageManagerInternal; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Log; +import android.util.Slog; + +import com.android.internal.R; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.util.ArrayUtils; +import com.android.server.FgThread; +import com.android.server.LocalServices; +import com.android.server.ServiceThread; +import com.android.server.SystemConfig; +import com.android.server.Watchdog; +import com.android.server.pm.PackageManagerService; +import com.android.server.pm.PackageManagerServiceUtils; +import com.android.server.pm.PackageSetting; +import com.android.server.pm.ProcessLoggingHandler; +import com.android.server.pm.SharedUserSetting; +import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback; +import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback; +import com.android.server.pm.permission.PermissionsState.PermissionState; + +import libcore.util.EmptyArray; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +/** + * Manages all permissions and handles permissions related tasks. + */ +public class PermissionManagerService { + private static final String TAG = "PackageManager"; + + /** All dangerous permission names in the same order as the events in MetricsEvent */ + private static final List<String> ALL_DANGEROUS_PERMISSIONS = Arrays.asList( + Manifest.permission.READ_CALENDAR, + Manifest.permission.WRITE_CALENDAR, + Manifest.permission.CAMERA, + Manifest.permission.READ_CONTACTS, + Manifest.permission.WRITE_CONTACTS, + Manifest.permission.GET_ACCOUNTS, + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION, + Manifest.permission.RECORD_AUDIO, + Manifest.permission.READ_PHONE_STATE, + Manifest.permission.CALL_PHONE, + Manifest.permission.READ_CALL_LOG, + Manifest.permission.WRITE_CALL_LOG, + Manifest.permission.ADD_VOICEMAIL, + Manifest.permission.USE_SIP, + Manifest.permission.PROCESS_OUTGOING_CALLS, + Manifest.permission.READ_CELL_BROADCASTS, + Manifest.permission.BODY_SENSORS, + Manifest.permission.SEND_SMS, + Manifest.permission.RECEIVE_SMS, + Manifest.permission.READ_SMS, + Manifest.permission.RECEIVE_WAP_PUSH, + Manifest.permission.RECEIVE_MMS, + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.READ_PHONE_NUMBERS, + Manifest.permission.ANSWER_PHONE_CALLS); + + /** Cap the size of permission trees that 3rd party apps can define */ + private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768; // characters of text + + /** Lock to protect internal data access */ + private final Object mLock; + + /** Internal connection to the package manager */ + private final PackageManagerInternal mPackageManagerInt; + + /** Internal connection to the user manager */ + private final UserManagerInternal mUserManagerInt; + + /** Default permission policy to provide proper behaviour out-of-the-box */ + private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy; + + /** Internal storage for permissions and related settings */ + private final PermissionSettings mSettings; + + private final HandlerThread mHandlerThread; + private final Handler mHandler; + private final Context mContext; + + PermissionManagerService(Context context, + @Nullable DefaultPermissionGrantedCallback defaultGrantCallback, + @NonNull Object externalLock) { + mContext = context; + mLock = externalLock; + mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class); + mUserManagerInt = LocalServices.getService(UserManagerInternal.class); + mSettings = new PermissionSettings(context, mLock); + + mHandlerThread = new ServiceThread(TAG, + Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); + mHandlerThread.start(); + mHandler = new Handler(mHandlerThread.getLooper()); + Watchdog.getInstance().addThread(mHandler); + + mDefaultPermissionGrantPolicy = new DefaultPermissionGrantPolicy( + context, mHandlerThread.getLooper(), defaultGrantCallback, this); + + // propagate permission configuration + final ArrayMap<String, SystemConfig.PermissionEntry> permConfig = + SystemConfig.getInstance().getPermissions(); + synchronized (mLock) { + for (int i=0; i<permConfig.size(); i++) { + final SystemConfig.PermissionEntry perm = permConfig.valueAt(i); + BasePermission bp = mSettings.getPermissionLocked(perm.name); + if (bp == null) { + bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN); + mSettings.putPermissionLocked(perm.name, bp); + } + if (perm.gids != null) { + bp.setGids(perm.gids, perm.perUser); + } + } + } + + LocalServices.addService( + PermissionManagerInternal.class, new PermissionManagerInternalImpl()); + } + + /** + * Creates and returns an initialized, internal service for use by other components. + * <p> + * The object returned is identical to the one returned by the LocalServices class using: + * {@code LocalServices.getService(PermissionManagerInternal.class);} + * <p> + * NOTE: The external lock is temporary and should be removed. This needs to be a + * lock created by the permission manager itself. + */ + public static PermissionManagerInternal create(Context context, + @Nullable DefaultPermissionGrantedCallback defaultGrantCallback, + @NonNull Object externalLock) { + final PermissionManagerInternal permMgrInt = + LocalServices.getService(PermissionManagerInternal.class); + if (permMgrInt != null) { + return permMgrInt; + } + new PermissionManagerService(context, defaultGrantCallback, externalLock); + return LocalServices.getService(PermissionManagerInternal.class); + } + + @Nullable BasePermission getPermission(String permName) { + synchronized (mLock) { + return mSettings.getPermissionLocked(permName); + } + } + + private int checkPermission(String permName, String pkgName, int callingUid, int userId) { + if (!mUserManagerInt.exists(userId)) { + return PackageManager.PERMISSION_DENIED; + } + + final PackageParser.Package pkg = mPackageManagerInt.getPackage(pkgName); + if (pkg != null && pkg.mExtras != null) { + if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { + return PackageManager.PERMISSION_DENIED; + } + final PackageSetting ps = (PackageSetting) pkg.mExtras; + final boolean instantApp = ps.getInstantApp(userId); + final PermissionsState permissionsState = ps.getPermissionsState(); + if (permissionsState.hasPermission(permName, userId)) { + if (instantApp) { + synchronized (mLock) { + BasePermission bp = mSettings.getPermissionLocked(permName); + if (bp != null && bp.isInstant()) { + return PackageManager.PERMISSION_GRANTED; + } + } + } else { + return PackageManager.PERMISSION_GRANTED; + } + } + // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION + if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState + .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) { + return PackageManager.PERMISSION_GRANTED; + } + } + + return PackageManager.PERMISSION_DENIED; + } + + private PermissionInfo getPermissionInfo(String name, String packageName, int flags, + int callingUid) { + if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { + return null; + } + // reader + synchronized (mLock) { + final BasePermission bp = mSettings.getPermissionLocked(name); + if (bp == null) { + return null; + } + final int adjustedProtectionLevel = adjustPermissionProtectionFlagsLocked( + bp.getProtectionLevel(), packageName, callingUid); + return bp.generatePermissionInfo(adjustedProtectionLevel, flags); + } + } + + private List<PermissionInfo> getPermissionInfoByGroup( + String groupName, int flags, int callingUid) { + if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { + return null; + } + // reader + synchronized (mLock) { + // TODO Uncomment when mPermissionGroups moves to this class +// if (groupName != null && !mPermissionGroups.containsKey(groupName)) { +// // This is thrown as NameNotFoundException +// return null; +// } + + final ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10); + for (BasePermission bp : mSettings.getAllPermissionsLocked()) { + final PermissionInfo pi = bp.generatePermissionInfo(groupName, flags); + if (pi != null) { + out.add(pi); + } + } + return out; + } + } + + private int adjustPermissionProtectionFlagsLocked( + int protectionLevel, String packageName, int uid) { + // Signature permission flags area always reported + final int protectionLevelMasked = protectionLevel + & (PermissionInfo.PROTECTION_NORMAL + | PermissionInfo.PROTECTION_DANGEROUS + | PermissionInfo.PROTECTION_SIGNATURE); + if (protectionLevelMasked == PermissionInfo.PROTECTION_SIGNATURE) { + return protectionLevel; + } + // System sees all flags. + final int appId = UserHandle.getAppId(uid); + if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID + || appId == Process.SHELL_UID) { + return protectionLevel; + } + // Normalize package name to handle renamed packages and static libs + final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName); + if (pkg == null) { + return protectionLevel; + } + if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) { + return protectionLevelMasked; + } + // Apps that target O see flags for all protection levels. + final PackageSetting ps = (PackageSetting) pkg.mExtras; + if (ps == null) { + return protectionLevel; + } + if (ps.getAppId() != appId) { + return protectionLevel; + } + return protectionLevel; + } + + private boolean addPermission( + PermissionInfo info, int callingUid, PermissionCallback callback) { + if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { + throw new SecurityException("Instant apps can't add permissions"); + } + if (info.labelRes == 0 && info.nonLocalizedLabel == null) { + throw new SecurityException("Label must be specified in permission"); + } + final BasePermission tree = (BasePermission) mPackageManagerInt.enforcePermissionTreeTEMP( + info.name, callingUid); + final boolean added; + final boolean changed; + synchronized (mLock) { + BasePermission bp = mSettings.getPermissionLocked(info.name); + added = bp == null; + int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel); + if (added) { + enforcePermissionCapLocked(info, tree); + bp = new BasePermission(info.name, tree.getSourcePackageName(), + BasePermission.TYPE_DYNAMIC); + } else if (bp.isDynamic()) { + throw new SecurityException( + "Not allowed to modify non-dynamic permission " + + info.name); + } + changed = bp.addToTree(fixedLevel, info, tree); + if (added) { + mSettings.putPermissionLocked(info.name, bp); + } + } + if (changed && callback != null) { + callback.onPermissionChanged(); + } + return added; + } + + private void removePermission( + String permName, int callingUid, PermissionCallback callback) { + if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { + throw new SecurityException("Instant applications don't have access to this method"); + } + final BasePermission tree = (BasePermission) mPackageManagerInt.enforcePermissionTreeTEMP( + permName, callingUid); + synchronized (mLock) { + final BasePermission bp = mSettings.getPermissionLocked(permName); + if (bp == null) { + return; + } + if (bp.isDynamic()) { + throw new SecurityException( + "Not allowed to modify non-dynamic permission " + + permName); + } + mSettings.removePermissionLocked(permName); + if (callback != null) { + callback.onPermissionRemoved(); + } + } + } + + private void grantRuntimePermissionsGrantedToDisabledPackageLocked( + PackageParser.Package pkg, int callingUid, PermissionCallback callback) { + if (pkg.parentPackage == null) { + return; + } + if (pkg.requestedPermissions == null) { + return; + } + final PackageParser.Package disabledPkg = + mPackageManagerInt.getDisabledPackage(pkg.parentPackage.packageName); + if (disabledPkg == null || disabledPkg.mExtras == null) { + return; + } + final PackageSetting disabledPs = (PackageSetting) disabledPkg.mExtras; + if (!disabledPs.isPrivileged() || disabledPs.hasChildPackages()) { + return; + } + final int permCount = pkg.requestedPermissions.size(); + for (int i = 0; i < permCount; i++) { + String permission = pkg.requestedPermissions.get(i); + BasePermission bp = mSettings.getPermissionLocked(permission); + if (bp == null || !(bp.isRuntime() || bp.isDevelopment())) { + continue; + } + for (int userId : mUserManagerInt.getUserIds()) { + if (disabledPs.getPermissionsState().hasRuntimePermission(permission, userId)) { + grantRuntimePermission( + permission, pkg.packageName, false, callingUid, userId, callback); + } + } + } + } + + private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds, + String[] grantedPermissions, int callingUid, PermissionCallback callback) { + for (int userId : userIds) { + grantRequestedRuntimePermissionsForUser(pkg, userId, grantedPermissions, callingUid, + callback); + } + } + + private void grantRequestedRuntimePermissionsForUser(PackageParser.Package pkg, int userId, + String[] grantedPermissions, int callingUid, PermissionCallback callback) { + PackageSetting ps = (PackageSetting) pkg.mExtras; + if (ps == null) { + return; + } + + PermissionsState permissionsState = ps.getPermissionsState(); + + final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED + | PackageManager.FLAG_PERMISSION_POLICY_FIXED; + + final boolean supportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion + >= Build.VERSION_CODES.M; + + final boolean instantApp = mPackageManagerInt.isInstantApp(pkg.packageName, userId); + + for (String permission : pkg.requestedPermissions) { + final BasePermission bp; + synchronized (mLock) { + bp = mSettings.getPermissionLocked(permission); + } + if (bp != null && (bp.isRuntime() || bp.isDevelopment()) + && (!instantApp || bp.isInstant()) + && (supportsRuntimePermissions || !bp.isRuntimeOnly()) + && (grantedPermissions == null + || ArrayUtils.contains(grantedPermissions, permission))) { + final int flags = permissionsState.getPermissionFlags(permission, userId); + if (supportsRuntimePermissions) { + // Installer cannot change immutable permissions. + if ((flags & immutableFlags) == 0) { + grantRuntimePermission(permission, pkg.packageName, false, callingUid, + userId, callback); + } + } else if (mSettings.mPermissionReviewRequired) { + // In permission review mode we clear the review flag when we + // are asked to install the app with all permissions granted. + if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { + updatePermissionFlags(permission, pkg.packageName, + PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, 0, callingUid, + userId, callback); + } + } + } + } + } + + private void grantRuntimePermission(String permName, String packageName, boolean overridePolicy, + int callingUid, final int userId, PermissionCallback callback) { + if (!mUserManagerInt.exists(userId)) { + Log.e(TAG, "No such user:" + userId); + return; + } + + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, + "grantRuntimePermission"); + + enforceCrossUserPermission(callingUid, userId, + true /* requireFullPermission */, true /* checkShell */, + "grantRuntimePermission"); + + final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName); + if (pkg == null || pkg.mExtras == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + final BasePermission bp; + synchronized(mLock) { + bp = mSettings.getPermissionLocked(permName); + } + if (bp == null) { + throw new IllegalArgumentException("Unknown permission: " + permName); + } + if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + + bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg); + + // If a permission review is required for legacy apps we represent + // their permissions as always granted runtime ones since we need + // to keep the review required permission flag per user while an + // install permission's state is shared across all users. + if (mSettings.mPermissionReviewRequired + && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M + && bp.isRuntime()) { + return; + } + + final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid); + + final PackageSetting ps = (PackageSetting) pkg.mExtras; + final PermissionsState permissionsState = ps.getPermissionsState(); + + final int flags = permissionsState.getPermissionFlags(permName, userId); + if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) { + throw new SecurityException("Cannot grant system fixed permission " + + permName + " for package " + packageName); + } + if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) { + throw new SecurityException("Cannot grant policy fixed permission " + + permName + " for package " + packageName); + } + + if (bp.isDevelopment()) { + // Development permissions must be handled specially, since they are not + // normal runtime permissions. For now they apply to all users. + if (permissionsState.grantInstallPermission(bp) != + PermissionsState.PERMISSION_OPERATION_FAILURE) { + if (callback != null) { + callback.onInstallPermissionGranted(); + } + } + return; + } + + if (ps.getInstantApp(userId) && !bp.isInstant()) { + throw new SecurityException("Cannot grant non-ephemeral permission" + + permName + " for package " + packageName); + } + + if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { + Slog.w(TAG, "Cannot grant runtime permission to a legacy app"); + return; + } + + final int result = permissionsState.grantRuntimePermission(bp, userId); + switch (result) { + case PermissionsState.PERMISSION_OPERATION_FAILURE: { + return; + } + + case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: { + if (callback != null) { + callback.onGidsChanged(UserHandle.getAppId(pkg.applicationInfo.uid), userId); + } + } + break; + } + + if (bp.isRuntime()) { + logPermissionGranted(mContext, permName, packageName); + } + + if (callback != null) { + callback.onPermissionGranted(uid, userId); + } + + // Only need to do this if user is initialized. Otherwise it's a new user + // and there are no processes running as the user yet and there's no need + // to make an expensive call to remount processes for the changed permissions. + if (READ_EXTERNAL_STORAGE.equals(permName) + || WRITE_EXTERNAL_STORAGE.equals(permName)) { + final long token = Binder.clearCallingIdentity(); + try { + if (mUserManagerInt.isUserInitialized(userId)) { + StorageManagerInternal storageManagerInternal = LocalServices.getService( + StorageManagerInternal.class); + storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + } + + private void revokeRuntimePermission(String permName, String packageName, + boolean overridePolicy, int callingUid, int userId, PermissionCallback callback) { + if (!mUserManagerInt.exists(userId)) { + Log.e(TAG, "No such user:" + userId); + return; + } + + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, + "revokeRuntimePermission"); + + enforceCrossUserPermission(Binder.getCallingUid(), userId, + true /* requireFullPermission */, true /* checkShell */, + "revokeRuntimePermission"); + + final int appId; + + final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName); + if (pkg == null || pkg.mExtras == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + if (mPackageManagerInt.filterAppAccess(pkg, Binder.getCallingUid(), userId)) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + final BasePermission bp = mSettings.getPermissionLocked(permName); + if (bp == null) { + throw new IllegalArgumentException("Unknown permission: " + permName); + } + + bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg); + + // If a permission review is required for legacy apps we represent + // their permissions as always granted runtime ones since we need + // to keep the review required permission flag per user while an + // install permission's state is shared across all users. + if (mSettings.mPermissionReviewRequired + && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M + && bp.isRuntime()) { + return; + } + + final PackageSetting ps = (PackageSetting) pkg.mExtras; + final PermissionsState permissionsState = ps.getPermissionsState(); + + final int flags = permissionsState.getPermissionFlags(permName, userId); + if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) { + throw new SecurityException("Cannot revoke system fixed permission " + + permName + " for package " + packageName); + } + if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) { + throw new SecurityException("Cannot revoke policy fixed permission " + + permName + " for package " + packageName); + } + + if (bp.isDevelopment()) { + // Development permissions must be handled specially, since they are not + // normal runtime permissions. For now they apply to all users. + if (permissionsState.revokeInstallPermission(bp) != + PermissionsState.PERMISSION_OPERATION_FAILURE) { + if (callback != null) { + callback.onInstallPermissionRevoked(); + } + } + return; + } + + if (permissionsState.revokeRuntimePermission(bp, userId) == + PermissionsState.PERMISSION_OPERATION_FAILURE) { + return; + } + + if (bp.isRuntime()) { + logPermissionRevoked(mContext, permName, packageName); + } + + if (callback != null) { + final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid); + callback.onPermissionRevoked(pkg.applicationInfo.uid, userId); + } + } + + private int[] revokeUnusedSharedUserPermissions(SharedUserSetting suSetting, int[] allUserIds) { + // Collect all used permissions in the UID + final ArraySet<String> usedPermissions = new ArraySet<>(); + final List<PackageParser.Package> pkgList = suSetting.getPackages(); + if (pkgList == null || pkgList.size() == 0) { + return EmptyArray.INT; + } + for (PackageParser.Package pkg : pkgList) { + final int requestedPermCount = pkg.requestedPermissions.size(); + for (int j = 0; j < requestedPermCount; j++) { + String permission = pkg.requestedPermissions.get(j); + BasePermission bp = mSettings.getPermissionLocked(permission); + if (bp != null) { + usedPermissions.add(permission); + } + } + } + + PermissionsState permissionsState = suSetting.getPermissionsState(); + // Prune install permissions + List<PermissionState> installPermStates = permissionsState.getInstallPermissionStates(); + final int installPermCount = installPermStates.size(); + for (int i = installPermCount - 1; i >= 0; i--) { + PermissionState permissionState = installPermStates.get(i); + if (!usedPermissions.contains(permissionState.getName())) { + BasePermission bp = mSettings.getPermissionLocked(permissionState.getName()); + if (bp != null) { + permissionsState.revokeInstallPermission(bp); + permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, + PackageManager.MASK_PERMISSION_FLAGS, 0); + } + } + } + + int[] runtimePermissionChangedUserIds = EmptyArray.INT; + + // Prune runtime permissions + for (int userId : allUserIds) { + List<PermissionState> runtimePermStates = permissionsState + .getRuntimePermissionStates(userId); + final int runtimePermCount = runtimePermStates.size(); + for (int i = runtimePermCount - 1; i >= 0; i--) { + PermissionState permissionState = runtimePermStates.get(i); + if (!usedPermissions.contains(permissionState.getName())) { + BasePermission bp = mSettings.getPermissionLocked(permissionState.getName()); + if (bp != null) { + permissionsState.revokeRuntimePermission(bp, userId); + permissionsState.updatePermissionFlags(bp, userId, + PackageManager.MASK_PERMISSION_FLAGS, 0); + runtimePermissionChangedUserIds = ArrayUtils.appendInt( + runtimePermissionChangedUserIds, userId); + } + } + } + } + + return runtimePermissionChangedUserIds; + } + + private int getPermissionFlags(String permName, String packageName, int callingUid, int userId) { + if (!mUserManagerInt.exists(userId)) { + return 0; + } + + enforceGrantRevokeRuntimePermissionPermissions("getPermissionFlags"); + + enforceCrossUserPermission(callingUid, userId, + true /* requireFullPermission */, false /* checkShell */, + "getPermissionFlags"); + + final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName); + if (pkg == null || pkg.mExtras == null) { + return 0; + } + synchronized (mLock) { + if (mSettings.getPermissionLocked(permName) == null) { + return 0; + } + } + if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { + return 0; + } + final PackageSetting ps = (PackageSetting) pkg.mExtras; + PermissionsState permissionsState = ps.getPermissionsState(); + return permissionsState.getPermissionFlags(permName, userId); + } + + private void updatePermissionFlags(String permName, String packageName, int flagMask, + int flagValues, int callingUid, int userId, PermissionCallback callback) { + if (!mUserManagerInt.exists(userId)) { + return; + } + + enforceGrantRevokeRuntimePermissionPermissions("updatePermissionFlags"); + + enforceCrossUserPermission(callingUid, userId, + true /* requireFullPermission */, true /* checkShell */, + "updatePermissionFlags"); + + // Only the system can change these flags and nothing else. + if (callingUid != Process.SYSTEM_UID) { + flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; + flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; + flagMask &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; + flagValues &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; + flagValues &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; + } + + final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName); + if (pkg == null || pkg.mExtras == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + + final BasePermission bp; + synchronized (mLock) { + bp = mSettings.getPermissionLocked(permName); + } + if (bp == null) { + throw new IllegalArgumentException("Unknown permission: " + permName); + } + + final PackageSetting ps = (PackageSetting) pkg.mExtras; + final PermissionsState permissionsState = ps.getPermissionsState(); + final boolean hadState = + permissionsState.getRuntimePermissionState(permName, userId) != null; + final boolean permissionUpdated = + permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues); + if (permissionUpdated && callback != null) { + // Install and runtime permissions are stored in different places, + // so figure out what permission changed and persist the change. + if (permissionsState.getInstallPermissionState(permName) != null) { + callback.onInstallPermissionUpdated(); + } else if (permissionsState.getRuntimePermissionState(permName, userId) != null + || hadState) { + callback.onPermissionUpdated(userId); + } + } + } + + private boolean updatePermissionFlagsForAllApps(int flagMask, int flagValues, int callingUid, + int userId, Collection<Package> packages, PermissionCallback callback) { + if (!mUserManagerInt.exists(userId)) { + return false; + } + + enforceGrantRevokeRuntimePermissionPermissions( + "updatePermissionFlagsForAllApps"); + enforceCrossUserPermission(callingUid, userId, + true /* requireFullPermission */, true /* checkShell */, + "updatePermissionFlagsForAllApps"); + + // Only the system can change system fixed flags. + if (callingUid != Process.SYSTEM_UID) { + flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; + flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; + } + + boolean changed = false; + for (PackageParser.Package pkg : packages) { + final PackageSetting ps = (PackageSetting) pkg.mExtras; + if (ps == null) { + continue; + } + PermissionsState permissionsState = ps.getPermissionsState(); + changed |= permissionsState.updatePermissionFlagsForAllPermissions( + userId, flagMask, flagValues); + } + return changed; + } + + private void enforceGrantRevokeRuntimePermissionPermissions(String message) { + if (mContext.checkCallingOrSelfPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS) + != PackageManager.PERMISSION_GRANTED + && mContext.checkCallingOrSelfPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException(message + " requires " + + Manifest.permission.GRANT_RUNTIME_PERMISSIONS + " or " + + Manifest.permission.REVOKE_RUNTIME_PERMISSIONS); + } + } + + /** + * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS + * or INTERACT_ACROSS_USERS_FULL permissions, if the userid is not for the caller. + * @param checkShell whether to prevent shell from access if there's a debugging restriction + * @param message the message to log on security exception + */ + private void enforceCrossUserPermission(int callingUid, int userId, + boolean requireFullPermission, boolean checkShell, String message) { + if (userId < 0) { + throw new IllegalArgumentException("Invalid userId " + userId); + } + if (checkShell) { + PackageManagerServiceUtils.enforceShellRestriction( + UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId); + } + if (userId == UserHandle.getUserId(callingUid)) return; + if (callingUid != Process.SYSTEM_UID && callingUid != 0) { + if (requireFullPermission) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message); + } else { + try { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message); + } catch (SecurityException se) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS, message); + } + } + } + } + + private int calculateCurrentPermissionFootprintLocked(BasePermission tree) { + int size = 0; + for (BasePermission perm : mSettings.getAllPermissionsLocked()) { + size += tree.calculateFootprint(perm); + } + return size; + } + + private void enforcePermissionCapLocked(PermissionInfo info, BasePermission tree) { + // We calculate the max size of permissions defined by this uid and throw + // if that plus the size of 'info' would exceed our stated maximum. + if (tree.getUid() != Process.SYSTEM_UID) { + final int curTreeSize = calculateCurrentPermissionFootprintLocked(tree); + if (curTreeSize + info.calculateFootprint() > MAX_PERMISSION_TREE_FOOTPRINT) { + throw new SecurityException("Permission tree size cap exceeded"); + } + } + } + + /** + * Get the first event id for the permission. + * + * <p>There are four events for each permission: <ul> + * <li>Request permission: first id + 0</li> + * <li>Grant permission: first id + 1</li> + * <li>Request for permission denied: first id + 2</li> + * <li>Revoke permission: first id + 3</li> + * </ul></p> + * + * @param name name of the permission + * + * @return The first event id for the permission + */ + private static int getBaseEventId(@NonNull String name) { + int eventIdIndex = ALL_DANGEROUS_PERMISSIONS.indexOf(name); + + if (eventIdIndex == -1) { + if (AppOpsManager.permissionToOpCode(name) == AppOpsManager.OP_NONE + || Build.IS_USER) { + Log.i(TAG, "Unknown permission " + name); + + return MetricsEvent.ACTION_PERMISSION_REQUEST_UNKNOWN; + } else { + // Most likely #ALL_DANGEROUS_PERMISSIONS needs to be updated. + // + // Also update + // - EventLogger#ALL_DANGEROUS_PERMISSIONS + // - metrics_constants.proto + throw new IllegalStateException("Unknown permission " + name); + } + } + + return MetricsEvent.ACTION_PERMISSION_REQUEST_READ_CALENDAR + eventIdIndex * 4; + } + + /** + * Log that a permission was revoked. + * + * @param context Context of the caller + * @param name name of the permission + * @param packageName package permission if for + */ + private static void logPermissionRevoked(@NonNull Context context, @NonNull String name, + @NonNull String packageName) { + MetricsLogger.action(context, getBaseEventId(name) + 3, packageName); + } + + /** + * Log that a permission request was granted. + * + * @param context Context of the caller + * @param name name of the permission + * @param packageName package permission if for + */ + private static void logPermissionGranted(@NonNull Context context, @NonNull String name, + @NonNull String packageName) { + MetricsLogger.action(context, getBaseEventId(name) + 1, packageName); + } + + private class PermissionManagerInternalImpl extends PermissionManagerInternal { + @Override + public boolean addPermission(PermissionInfo info, boolean async, int callingUid, + PermissionCallback callback) { + return PermissionManagerService.this.addPermission(info, callingUid, callback); + } + @Override + public void removePermission(String permName, int callingUid, + PermissionCallback callback) { + PermissionManagerService.this.removePermission(permName, callingUid, callback); + } + @Override + public void grantRuntimePermission(String permName, String packageName, + boolean overridePolicy, int callingUid, int userId, + PermissionCallback callback) { + PermissionManagerService.this.grantRuntimePermission( + permName, packageName, overridePolicy, callingUid, userId, callback); + } + @Override + public void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds, + String[] grantedPermissions, int callingUid, PermissionCallback callback) { + PermissionManagerService.this.grantRequestedRuntimePermissions( + pkg, userIds, grantedPermissions, callingUid, callback); + } + @Override + public void grantRuntimePermissionsGrantedToDisabledPackage(PackageParser.Package pkg, + int callingUid, PermissionCallback callback) { + PermissionManagerService.this.grantRuntimePermissionsGrantedToDisabledPackageLocked( + pkg, callingUid, callback); + } + @Override + public void revokeRuntimePermission(String permName, String packageName, + boolean overridePolicy, int callingUid, int userId, + PermissionCallback callback) { + PermissionManagerService.this.revokeRuntimePermission(permName, packageName, + overridePolicy, callingUid, userId, callback); + } + @Override + public int[] revokeUnusedSharedUserPermissions(SharedUserSetting suSetting, + int[] allUserIds) { + return PermissionManagerService.this.revokeUnusedSharedUserPermissions( + (SharedUserSetting) suSetting, allUserIds); + } + @Override + public int getPermissionFlags(String permName, String packageName, int callingUid, + int userId) { + return PermissionManagerService.this.getPermissionFlags(permName, packageName, + callingUid, userId); + } + @Override + public void updatePermissionFlags(String permName, String packageName, int flagMask, + int flagValues, int callingUid, int userId, PermissionCallback callback) { + PermissionManagerService.this.updatePermissionFlags( + permName, packageName, flagMask, flagValues, callingUid, userId, callback); + } + @Override + public boolean updatePermissionFlagsForAllApps(int flagMask, int flagValues, int callingUid, + int userId, Collection<Package> packages, PermissionCallback callback) { + return PermissionManagerService.this.updatePermissionFlagsForAllApps( + flagMask, flagValues, callingUid, userId, packages, callback); + } + @Override + public void enforceCrossUserPermission(int callingUid, int userId, + boolean requireFullPermission, boolean checkShell, String message) { + PermissionManagerService.this.enforceCrossUserPermission(callingUid, userId, + requireFullPermission, checkShell, message); + } + @Override + public void enforceGrantRevokeRuntimePermissionPermissions(String message) { + PermissionManagerService.this.enforceGrantRevokeRuntimePermissionPermissions(message); + } + @Override + public int checkPermission(String permName, String packageName, int callingUid, + int userId) { + return PermissionManagerService.this.checkPermission( + permName, packageName, callingUid, userId); + } + @Override + public PermissionInfo getPermissionInfo(String permName, String packageName, int flags, + int callingUid) { + return PermissionManagerService.this.getPermissionInfo( + permName, packageName, flags, callingUid); + } + @Override + public List<PermissionInfo> getPermissionInfoByGroup(String group, int flags, + int callingUid) { + return PermissionManagerService.this.getPermissionInfoByGroup(group, flags, callingUid); + } + @Override + public boolean isPermissionInstant(String permName) { + synchronized (PermissionManagerService.this.mLock) { + final BasePermission bp = mSettings.getPermissionLocked(permName); + return (bp != null && bp.isInstant()); + } + } + @Override + public boolean isPermissionAppOp(String permName) { + synchronized (PermissionManagerService.this.mLock) { + final BasePermission bp = mSettings.getPermissionLocked(permName); + return (bp != null && bp.isAppOp()); + } + } + @Override + public PermissionSettings getPermissionSettings() { + return mSettings; + } + @Override + public DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy() { + return mDefaultPermissionGrantPolicy; + } + @Override + public BasePermission getPermissionTEMP(String permName) { + synchronized (PermissionManagerService.this.mLock) { + return mSettings.getPermissionLocked(permName); + } + } + @Override + public void putPermissionTEMP(String permName, BasePermission permission) { + synchronized (PermissionManagerService.this.mLock) { + mSettings.putPermissionLocked(permName, (BasePermission) permission); + } + } + @Override + public Iterator<BasePermission> getPermissionIteratorTEMP() { + synchronized (PermissionManagerService.this.mLock) { + return mSettings.getAllPermissionsLocked().iterator(); + } + } + } +} diff --git a/com/android/server/pm/permission/PermissionSettings.java b/com/android/server/pm/permission/PermissionSettings.java new file mode 100644 index 00000000..7a2e5ecc --- /dev/null +++ b/com/android/server/pm/permission/PermissionSettings.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2017 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.pm.permission; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.Log; + +import com.android.internal.R; +import com.android.internal.util.XmlUtils; +import com.android.server.pm.DumpState; +import com.android.server.pm.PackageManagerService; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Collection; + +/** + * Permissions and other related data. This class is not meant for + * direct access outside of the permission package with the sole exception + * of package settings. Instead, it should be reference either from the + * permission manager or package settings. + */ +public class PermissionSettings { + + final boolean mPermissionReviewRequired; + /** + * All of the permissions known to the system. The mapping is from permission + * name to permission object. + */ + private final ArrayMap<String, BasePermission> mPermissions = + new ArrayMap<String, BasePermission>(); + private final Object mLock; + + PermissionSettings(@NonNull Context context, @NonNull Object lock) { + mPermissionReviewRequired = + context.getResources().getBoolean(R.bool.config_permissionReviewRequired); + mLock = lock; + } + + public @Nullable BasePermission getPermission(@NonNull String permName) { + synchronized (mLock) { + return getPermissionLocked(permName); + } + } + + /** + * Transfers ownership of permissions from one package to another. + */ + public void transferPermissions(String origPackageName, String newPackageName, + ArrayMap<String, BasePermission> permissionTrees) { + synchronized (mLock) { + for (int i=0; i<2; i++) { + ArrayMap<String, BasePermission> permissions = + i == 0 ? permissionTrees : mPermissions; + for (BasePermission bp : permissions.values()) { + bp.transfer(origPackageName, newPackageName); + } + } + } + } + + public boolean canPropagatePermissionToInstantApp(String permName) { + synchronized (mLock) { + final BasePermission bp = mPermissions.get(permName); + return (bp != null && (bp.isRuntime() || bp.isDevelopment()) && bp.isInstant()); + } + } + + public void readPermissions(XmlPullParser parser) throws IOException, XmlPullParserException { + synchronized (mLock) { + readPermissions(mPermissions, parser); + } + } + + public void writePermissions(XmlSerializer serializer) throws IOException { + for (BasePermission bp : mPermissions.values()) { + bp.writeLPr(serializer); + } + } + + public static void readPermissions(ArrayMap<String, BasePermission> out, XmlPullParser parser) + throws IOException, XmlPullParserException { + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + if (!BasePermission.readLPw(out, parser)) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element reading permissions: " + parser.getName() + " at " + + parser.getPositionDescription()); + } + XmlUtils.skipCurrentTag(parser); + } + } + + public void dumpPermissions(PrintWriter pw, String packageName, + ArraySet<String> permissionNames, boolean externalStorageEnforced, + DumpState dumpState) { + synchronized (mLock) { + boolean printedSomething = false; + for (BasePermission bp : mPermissions.values()) { + printedSomething = bp.dumpPermissionsLPr(pw, packageName, permissionNames, + externalStorageEnforced, printedSomething, dumpState); + } + } + } + + @Nullable BasePermission getPermissionLocked(@NonNull String permName) { + return mPermissions.get(permName); + } + + void putPermissionLocked(@NonNull String permName, @NonNull BasePermission permission) { + mPermissions.put(permName, permission); + } + + void removePermissionLocked(@NonNull String permName) { + mPermissions.remove(permName); + } + + Collection<BasePermission> getAllPermissionsLocked() { + return mPermissions.values(); + } +} diff --git a/com/android/server/pm/PermissionsState.java b/com/android/server/pm/permission/PermissionsState.java index f4d2ad2c..11df3804 100644 --- a/com/android/server/pm/PermissionsState.java +++ b/com/android/server/pm/permission/PermissionsState.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.pm; +package com.android.server.pm.permission; import android.content.pm.PackageManager; import android.os.UserHandle; @@ -406,7 +406,7 @@ public final class PermissionsState { ensurePermissionData(permission); } - PermissionData permissionData = mPermissions.get(permission.name); + PermissionData permissionData = mPermissions.get(permission.getName()); if (permissionData == null) { if (!mayChangeFlags) { return false; @@ -557,7 +557,7 @@ public final class PermissionsState { } private int grantPermission(BasePermission permission, int userId) { - if (hasPermission(permission.name, userId)) { + if (hasPermission(permission.getName(), userId)) { return PERMISSION_OPERATION_FAILURE; } @@ -581,21 +581,22 @@ public final class PermissionsState { } private int revokePermission(BasePermission permission, int userId) { - if (!hasPermission(permission.name, userId)) { + final String permName = permission.getName(); + if (!hasPermission(permName, userId)) { return PERMISSION_OPERATION_FAILURE; } final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId)); final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS; - PermissionData permissionData = mPermissions.get(permission.name); + PermissionData permissionData = mPermissions.get(permName); if (!permissionData.revoke(userId)) { return PERMISSION_OPERATION_FAILURE; } if (permissionData.isDefault()) { - ensureNoPermissionData(permission.name); + ensureNoPermissionData(permName); } if (hasGids) { @@ -625,13 +626,14 @@ public final class PermissionsState { } private PermissionData ensurePermissionData(BasePermission permission) { + final String permName = permission.getName(); if (mPermissions == null) { mPermissions = new ArrayMap<>(); } - PermissionData permissionData = mPermissions.get(permission.name); + PermissionData permissionData = mPermissions.get(permName); if (permissionData == null) { permissionData = new PermissionData(permission); - mPermissions.put(permission.name, permissionData); + mPermissions.put(permName, permissionData); } return permissionData; } @@ -692,7 +694,7 @@ public final class PermissionsState { PermissionState userState = mUserStates.get(userId); if (userState == null) { - userState = new PermissionState(mPerm.name); + userState = new PermissionState(mPerm.getName()); mUserStates.put(userId, userState); } @@ -760,7 +762,7 @@ public final class PermissionsState { } return userState.mFlags != oldFlags; } else if (newFlags != 0) { - userState = new PermissionState(mPerm.name); + userState = new PermissionState(mPerm.getName()); userState.mFlags = newFlags; mUserStates.put(userId, userState); return true; diff --git a/com/android/server/policy/PhoneWindowManager.java b/com/android/server/policy/PhoneWindowManager.java index a806af46..db7817ec 100644 --- a/com/android/server/policy/PhoneWindowManager.java +++ b/com/android/server/policy/PhoneWindowManager.java @@ -20,9 +20,12 @@ import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.Manifest.permission.SYSTEM_ALERT_WINDOW; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; import static android.app.AppOpsManager.OP_TOAST_WINDOW; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Context.CONTEXT_RESTRICTED; import static android.content.Context.DISPLAY_SERVICE; import static android.content.Context.WINDOW_SERVICE; @@ -198,6 +201,7 @@ import android.util.EventLog; import android.util.Log; import android.util.LongSparseArray; import android.util.MutableBoolean; +import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; @@ -296,13 +300,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final int LONG_PRESS_POWER_SHUT_OFF = 2; static final int LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM = 3; - static final int LONG_PRESS_BACK_NOTHING = 0; - static final int LONG_PRESS_BACK_GO_TO_VOICE_ASSIST = 1; - static final int MULTI_PRESS_POWER_NOTHING = 0; static final int MULTI_PRESS_POWER_THEATER_MODE = 1; static final int MULTI_PRESS_POWER_BRIGHTNESS_BOOST = 2; + static final int LONG_PRESS_BACK_NOTHING = 0; + static final int LONG_PRESS_BACK_GO_TO_VOICE_ASSIST = 1; + // Number of presses needed before we induce panic press behavior on the back button static final int PANIC_PRESS_BACK_COUNT = 4; static final int PANIC_PRESS_BACK_NOTHING = 0; @@ -565,7 +569,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mLongPressOnBackBehavior; int mPanicPressOnBackBehavior; int mShortPressOnSleepBehavior; - int mShortPressWindowBehavior; + int mShortPressOnWindowBehavior; volatile boolean mAwake; boolean mScreenOnEarly; boolean mScreenOnFully; @@ -2180,9 +2184,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { mDoubleTapOnHomeBehavior = LONG_PRESS_HOME_NOTHING; } - mShortPressWindowBehavior = SHORT_PRESS_WINDOW_NOTHING; + mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_NOTHING; if (mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { - mShortPressWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE; + mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE; } mNavBarOpacityMode = res.getInteger( @@ -2626,18 +2630,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; } - if (ActivityManager.isHighEndGfx()) { - if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) { - attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; - } - final boolean forceWindowDrawsStatusBarBackground = - (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) - != 0; - if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 - || forceWindowDrawsStatusBarBackground - && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT) { - attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; - } + if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) { + attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + } + final boolean forceWindowDrawsStatusBarBackground = + (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0; + if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 + || forceWindowDrawsStatusBarBackground + && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT) { + attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; } } @@ -3627,10 +3628,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { return -1; } - // If the device is in Vr mode, drop the volume keys and don't - // forward it to the application/dispatch the audio event. + // If the device is in VR mode and keys are "internal" (e.g. on the side of the + // device), then drop the volume keys and don't forward it to the application/dispatch + // the audio event. if (mPersistentVrModeEnabled) { - return -1; + final InputDevice d = event.getDevice(); + if (d != null && !d.isExternal()) { + return -1; + } } } else if (keyCode == KeyEvent.KEYCODE_TAB && event.isMetaPressed()) { // Pass through keyboard navigation keys. @@ -6272,7 +6277,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { break; } case KeyEvent.KEYCODE_WINDOW: { - if (mShortPressWindowBehavior == SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE) { + if (mShortPressOnWindowBehavior == SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE) { if (mPictureInPictureVisible) { // Consumes the key only if picture-in-picture is visible to show // picture-in-picture control menu. This gives a chance to the foreground @@ -7915,8 +7920,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState); final int dockedVisibility = updateLightStatusBarLw(0 /* vis */, mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState); - mWindowManagerFuncs.getStackBounds(HOME_STACK_ID, mNonDockedStackBounds); - mWindowManagerFuncs.getStackBounds(DOCKED_STACK_ID, mDockedStackBounds); + mWindowManagerFuncs.getStackBounds( + WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds); + mWindowManagerFuncs.getStackBounds( + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds); final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility); final int diff = visibility ^ mLastSystemUiFlags; final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags; @@ -8319,9 +8326,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(prefix); pw.print("mSafeMode="); pw.print(mSafeMode); pw.print(" mSystemReady="); pw.print(mSystemReady); pw.print(" mSystemBooted="); pw.println(mSystemBooted); - pw.print(prefix); pw.print("mLidState="); pw.print(mLidState); - pw.print(" mLidOpenRotation="); pw.print(mLidOpenRotation); - pw.print(" mCameraLensCoverState="); pw.print(mCameraLensCoverState); + pw.print(prefix); pw.print("mLidState="); + pw.print(WindowManagerFuncs.lidStateToString(mLidState)); + pw.print(" mLidOpenRotation="); + pw.println(Surface.rotationToString(mLidOpenRotation)); + pw.print(prefix); pw.print("mCameraLensCoverState="); + pw.print(WindowManagerFuncs.cameraLensStateToString(mCameraLensCoverState)); pw.print(" mHdmiPlugged="); pw.println(mHdmiPlugged); if (mLastSystemUiFlags != 0 || mResettingSystemUiFlags != 0 || mForceClearedSystemUiFlags != 0) { @@ -8339,16 +8349,24 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(prefix); pw.print("mWakeGestureEnabledSetting="); pw.println(mWakeGestureEnabledSetting); - pw.print(prefix); pw.print("mSupportAutoRotation="); pw.println(mSupportAutoRotation); - pw.print(prefix); pw.print("mUiMode="); pw.print(mUiMode); - pw.print(" mDockMode="); pw.print(mDockMode); - pw.print(" mEnableCarDockHomeCapture="); pw.print(mEnableCarDockHomeCapture); - pw.print(" mCarDockRotation="); pw.print(mCarDockRotation); - pw.print(" mDeskDockRotation="); pw.println(mDeskDockRotation); - pw.print(prefix); pw.print("mUserRotationMode="); pw.print(mUserRotationMode); - pw.print(" mUserRotation="); pw.print(mUserRotation); - pw.print(" mAllowAllRotations="); pw.println(mAllowAllRotations); - pw.print(prefix); pw.print("mCurrentAppOrientation="); pw.println(mCurrentAppOrientation); + pw.print(prefix); + pw.print("mSupportAutoRotation="); pw.print(mSupportAutoRotation); + pw.print(" mOrientationSensorEnabled="); pw.println(mOrientationSensorEnabled); + pw.print(prefix); pw.print("mUiMode="); pw.print(Configuration.uiModeToString(mUiMode)); + pw.print(" mDockMode="); pw.println(Intent.dockStateToString(mDockMode)); + pw.print(prefix); pw.print("mEnableCarDockHomeCapture="); + pw.print(mEnableCarDockHomeCapture); + pw.print(" mCarDockRotation="); + pw.print(Surface.rotationToString(mCarDockRotation)); + pw.print(" mDeskDockRotation="); + pw.println(Surface.rotationToString(mDeskDockRotation)); + pw.print(prefix); pw.print("mUserRotationMode="); + pw.print(WindowManagerPolicy.userRotationModeToString(mUserRotationMode)); + pw.print(" mUserRotation="); pw.print(Surface.rotationToString(mUserRotation)); + pw.print(" mAllowAllRotations="); + pw.println(allowAllRotationsToString(mAllowAllRotations)); + pw.print(prefix); pw.print("mCurrentAppOrientation="); + pw.println(ActivityInfo.screenOrientationToString(mCurrentAppOrientation)); pw.print(prefix); pw.print("mCarDockEnablesAccelerometer="); pw.print(mCarDockEnablesAccelerometer); pw.print(" mDeskDockEnablesAccelerometer="); @@ -8357,23 +8375,54 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(mLidKeyboardAccessibility); pw.print(" mLidNavigationAccessibility="); pw.print(mLidNavigationAccessibility); pw.print(" mLidControlsScreenLock="); pw.println(mLidControlsScreenLock); - pw.print(" mLidControlsSleep="); pw.println(mLidControlsSleep); + pw.print(prefix); pw.print("mLidControlsSleep="); pw.println(mLidControlsSleep); pw.print(prefix); - pw.print(" mLongPressOnBackBehavior="); pw.println(mLongPressOnBackBehavior); + pw.print("mLongPressOnBackBehavior="); + pw.println(longPressOnBackBehaviorToString(mLongPressOnBackBehavior)); pw.print(prefix); - pw.print("mShortPressOnPowerBehavior="); pw.print(mShortPressOnPowerBehavior); - pw.print(" mLongPressOnPowerBehavior="); pw.println(mLongPressOnPowerBehavior); + pw.print("mPanicPressOnBackBehavior="); + pw.println(panicPressOnBackBehaviorToString(mPanicPressOnBackBehavior)); pw.print(prefix); - pw.print("mDoublePressOnPowerBehavior="); pw.print(mDoublePressOnPowerBehavior); - pw.print(" mTriplePressOnPowerBehavior="); pw.println(mTriplePressOnPowerBehavior); - pw.print(prefix); pw.print("mHasSoftInput="); pw.println(mHasSoftInput); - pw.print(prefix); pw.print("mAwake="); pw.println(mAwake); - pw.print(prefix); pw.print("mScreenOnEarly="); pw.print(mScreenOnEarly); + pw.print("mLongPressOnHomeBehavior="); + pw.println(longPressOnHomeBehaviorToString(mLongPressOnHomeBehavior)); + pw.print(prefix); + pw.print("mDoubleTapOnHomeBehavior="); + pw.println(doubleTapOnHomeBehaviorToString(mDoubleTapOnHomeBehavior)); + pw.print(prefix); + pw.print("mShortPressOnPowerBehavior="); + pw.println(shortPressOnPowerBehaviorToString(mShortPressOnPowerBehavior)); + pw.print(prefix); + pw.print("mLongPressOnPowerBehavior="); + pw.println(longPressOnPowerBehaviorToString(mLongPressOnPowerBehavior)); + pw.print(prefix); + pw.print("mDoublePressOnPowerBehavior="); + pw.println(multiPressOnPowerBehaviorToString(mDoublePressOnPowerBehavior)); + pw.print(prefix); + pw.print("mTriplePressOnPowerBehavior="); + pw.println(multiPressOnPowerBehaviorToString(mTriplePressOnPowerBehavior)); + pw.print(prefix); + pw.print("mShortPressOnSleepBehavior="); + pw.println(shortPressOnSleepBehaviorToString(mShortPressOnSleepBehavior)); + pw.print(prefix); + pw.print("mShortPressOnWindowBehavior="); + pw.println(shortPressOnWindowBehaviorToString(mShortPressOnWindowBehavior)); + pw.print(prefix); + pw.print("mHasSoftInput="); pw.print(mHasSoftInput); + pw.print(" mDismissImeOnBackKeyPressed="); pw.println(mDismissImeOnBackKeyPressed); + pw.print(prefix); + pw.print("mIncallPowerBehavior="); + pw.print(incallPowerBehaviorToString(mIncallPowerBehavior)); + pw.print(" mIncallBackBehavior="); + pw.print(incallBackBehaviorToString(mIncallBackBehavior)); + pw.print(" mEndcallBehavior="); + pw.println(endcallBehaviorToString(mEndcallBehavior)); + pw.print(prefix); pw.print("mHomePressed="); pw.println(mHomePressed); + pw.print(prefix); + pw.print("mAwake="); pw.print(mAwake); + pw.print("mScreenOnEarly="); pw.print(mScreenOnEarly); pw.print(" mScreenOnFully="); pw.println(mScreenOnFully); pw.print(prefix); pw.print("mKeyguardDrawComplete="); pw.print(mKeyguardDrawComplete); pw.print(" mWindowManagerDrawComplete="); pw.println(mWindowManagerDrawComplete); - pw.print(prefix); pw.print("mOrientationSensorEnabled="); - pw.println(mOrientationSensorEnabled); pw.print(prefix); pw.print("mOverscanScreen=("); pw.print(mOverscanScreenLeft); pw.print(","); pw.print(mOverscanScreenTop); pw.print(") "); pw.print(mOverscanScreenWidth); @@ -8439,8 +8488,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(prefix); pw.print("mLastInputMethodTargetWindow="); pw.println(mLastInputMethodTargetWindow); } - pw.print(prefix); pw.print("mDismissImeOnBackKeyPressed="); - pw.println(mDismissImeOnBackKeyPressed); if (mStatusBar != null) { pw.print(prefix); pw.print("mStatusBar="); pw.print(mStatusBar); pw.print(" isStatusBarKeyguard="); @@ -8473,26 +8520,28 @@ public class PhoneWindowManager implements WindowManagerPolicy { } pw.print(prefix); pw.print("mTopIsFullscreen="); pw.print(mTopIsFullscreen); pw.print(" mKeyguardOccluded="); pw.println(mKeyguardOccluded); - pw.print(" mKeyguardOccludedChanged="); pw.println(mKeyguardOccludedChanged); + pw.print(prefix); + pw.print("mKeyguardOccludedChanged="); pw.print(mKeyguardOccludedChanged); pw.print(" mPendingKeyguardOccluded="); pw.println(mPendingKeyguardOccluded); pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar); pw.print(" mForceStatusBarFromKeyguard="); pw.println(mForceStatusBarFromKeyguard); - pw.print(prefix); pw.print("mHomePressed="); pw.println(mHomePressed); pw.print(prefix); pw.print("mAllowLockscreenWhenOn="); pw.print(mAllowLockscreenWhenOn); pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout); pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive); - pw.print(prefix); pw.print("mEndcallBehavior="); pw.print(mEndcallBehavior); - pw.print(" mIncallPowerBehavior="); pw.print(mIncallPowerBehavior); - pw.print(" mIncallBackBehavior="); pw.print(mIncallBackBehavior); - pw.print(" mLongPressOnHomeBehavior="); pw.println(mLongPressOnHomeBehavior); - pw.print(prefix); pw.print("mLandscapeRotation="); pw.print(mLandscapeRotation); - pw.print(" mSeascapeRotation="); pw.println(mSeascapeRotation); - pw.print(prefix); pw.print("mPortraitRotation="); pw.print(mPortraitRotation); - pw.print(" mUpsideDownRotation="); pw.println(mUpsideDownRotation); - pw.print(prefix); pw.print("mDemoHdmiRotation="); pw.print(mDemoHdmiRotation); + pw.print(prefix); pw.print("mLandscapeRotation="); + pw.print(Surface.rotationToString(mLandscapeRotation)); + pw.print(" mSeascapeRotation="); + pw.println(Surface.rotationToString(mSeascapeRotation)); + pw.print(prefix); pw.print("mPortraitRotation="); + pw.print(Surface.rotationToString(mPortraitRotation)); + pw.print(" mUpsideDownRotation="); + pw.println(Surface.rotationToString(mUpsideDownRotation)); + pw.print(prefix); pw.print("mDemoHdmiRotation="); + pw.print(Surface.rotationToString(mDemoHdmiRotation)); pw.print(" mDemoHdmiRotationLock="); pw.println(mDemoHdmiRotationLock); - pw.print(prefix); pw.print("mUndockedHdmiRotation="); pw.println(mUndockedHdmiRotation); + pw.print(prefix); pw.print("mUndockedHdmiRotation="); + pw.println(Surface.rotationToString(mUndockedHdmiRotation)); if (mHasFeatureLeanback) { pw.print(prefix); pw.print("mAccessibilityTvKey1Pressed="); pw.println(mAccessibilityTvKey1Pressed); @@ -8519,5 +8568,169 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mKeyguardDelegate != null) { mKeyguardDelegate.dump(prefix, pw); } + + pw.print(prefix); pw.println("Looper state:"); + mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + " "); + } + + private static String allowAllRotationsToString(int allowAll) { + switch (allowAll) { + case -1: + return "unknown"; + case 0: + return "false"; + case 1: + return "true"; + default: + return Integer.toString(allowAll); + } + } + + private static String endcallBehaviorToString(int behavior) { + StringBuilder sb = new StringBuilder(); + if ((behavior & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0 ) { + sb.append("home|"); + } + if ((behavior & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) { + sb.append("sleep|"); + } + + final int N = sb.length(); + if (N == 0) { + return "<nothing>"; + } else { + // Chop off the trailing '|' + return sb.substring(0, N - 1); + } + } + + private static String incallPowerBehaviorToString(int behavior) { + if ((behavior & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0) { + return "hangup"; + } else { + return "sleep"; + } + } + + private static String incallBackBehaviorToString(int behavior) { + if ((behavior & Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR_HANGUP) != 0) { + return "hangup"; + } else { + return "<nothing>"; + } + } + + private static String longPressOnBackBehaviorToString(int behavior) { + switch (behavior) { + case LONG_PRESS_BACK_NOTHING: + return "LONG_PRESS_BACK_NOTHING"; + case LONG_PRESS_BACK_GO_TO_VOICE_ASSIST: + return "LONG_PRESS_BACK_GO_TO_VOICE_ASSIST"; + default: + return Integer.toString(behavior); + } + } + + private static String panicPressOnBackBehaviorToString(int behavior) { + switch (behavior) { + case PANIC_PRESS_BACK_NOTHING: + return "PANIC_PRESS_BACK_NOTHING"; + case PANIC_PRESS_BACK_HOME: + return "PANIC_PRESS_BACK_HOME"; + default: + return Integer.toString(behavior); + } + } + + private static String longPressOnHomeBehaviorToString(int behavior) { + switch (behavior) { + case LONG_PRESS_HOME_NOTHING: + return "LONG_PRESS_HOME_NOTHING"; + case LONG_PRESS_HOME_ALL_APPS: + return "LONG_PRESS_HOME_ALL_APPS"; + case LONG_PRESS_HOME_ASSIST: + return "LONG_PRESS_HOME_ASSIST"; + default: + return Integer.toString(behavior); + } + } + + private static String doubleTapOnHomeBehaviorToString(int behavior) { + switch (behavior) { + case DOUBLE_TAP_HOME_NOTHING: + return "DOUBLE_TAP_HOME_NOTHING"; + case DOUBLE_TAP_HOME_RECENT_SYSTEM_UI: + return "DOUBLE_TAP_HOME_RECENT_SYSTEM_UI"; + default: + return Integer.toString(behavior); + } + } + + private static String shortPressOnPowerBehaviorToString(int behavior) { + switch (behavior) { + case SHORT_PRESS_POWER_NOTHING: + return "SHORT_PRESS_POWER_NOTHING"; + case SHORT_PRESS_POWER_GO_TO_SLEEP: + return "SHORT_PRESS_POWER_GO_TO_SLEEP"; + case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP: + return "SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP"; + case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME: + return "SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME"; + case SHORT_PRESS_POWER_GO_HOME: + return "SHORT_PRESS_POWER_GO_HOME"; + case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME: + return "SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME"; + default: + return Integer.toString(behavior); + } + } + + private static String longPressOnPowerBehaviorToString(int behavior) { + switch (behavior) { + case LONG_PRESS_POWER_NOTHING: + return "LONG_PRESS_POWER_NOTHING"; + case LONG_PRESS_POWER_GLOBAL_ACTIONS: + return "LONG_PRESS_POWER_GLOBAL_ACTIONS"; + case LONG_PRESS_POWER_SHUT_OFF: + return "LONG_PRESS_POWER_SHUT_OFF"; + case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM: + return "LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM"; + default: + return Integer.toString(behavior); + } + } + private static String multiPressOnPowerBehaviorToString(int behavior) { + switch (behavior) { + case MULTI_PRESS_POWER_NOTHING: + return "MULTI_PRESS_POWER_NOTHING"; + case MULTI_PRESS_POWER_THEATER_MODE: + return "MULTI_PRESS_POWER_THEATER_MODE"; + case MULTI_PRESS_POWER_BRIGHTNESS_BOOST: + return "MULTI_PRESS_POWER_BRIGHTNESS_BOOST"; + default: + return Integer.toString(behavior); + } + } + + private static String shortPressOnSleepBehaviorToString(int behavior) { + switch (behavior) { + case SHORT_PRESS_SLEEP_GO_TO_SLEEP: + return "SHORT_PRESS_SLEEP_GO_TO_SLEEP"; + case SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME: + return "SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME"; + default: + return Integer.toString(behavior); + } + } + + private static String shortPressOnWindowBehaviorToString(int behavior) { + switch (behavior) { + case SHORT_PRESS_WINDOW_NOTHING: + return "SHORT_PRESS_WINDOW_NOTHING"; + case SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE: + return "SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE"; + default: + return Integer.toString(behavior); + } } } diff --git a/com/android/server/policy/WindowOrientationListener.java b/com/android/server/policy/WindowOrientationListener.java index 64f64c0d..169fd278 100644 --- a/com/android/server/policy/WindowOrientationListener.java +++ b/com/android/server/policy/WindowOrientationListener.java @@ -26,6 +26,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.text.TextUtils; import android.util.Slog; +import android.view.Surface; import java.io.PrintWriter; import java.util.Arrays; @@ -236,7 +237,7 @@ public abstract class WindowOrientationListener { pw.println(prefix + TAG); prefix += " "; pw.println(prefix + "mEnabled=" + mEnabled); - pw.println(prefix + "mCurrentRotation=" + mCurrentRotation); + pw.println(prefix + "mCurrentRotation=" + Surface.rotationToString(mCurrentRotation)); pw.println(prefix + "mSensorType=" + mSensorType); pw.println(prefix + "mSensor=" + mSensor); pw.println(prefix + "mRate=" + mRate); @@ -1026,8 +1027,9 @@ public abstract class WindowOrientationListener { public void dumpLocked(PrintWriter pw, String prefix) { pw.println(prefix + "OrientationSensorJudge"); prefix += " "; - pw.println(prefix + "mDesiredRotation=" + mDesiredRotation); - pw.println(prefix + "mProposedRotation=" + mProposedRotation); + pw.println(prefix + "mDesiredRotation=" + Surface.rotationToString(mDesiredRotation)); + pw.println(prefix + "mProposedRotation=" + + Surface.rotationToString(mProposedRotation)); pw.println(prefix + "mTouching=" + mTouching); pw.println(prefix + "mTouchEndedTimestampNanos=" + mTouchEndedTimestampNanos); } diff --git a/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/com/android/server/policy/keyguard/KeyguardServiceDelegate.java index 50e5e7bd..70cd54ff 100644 --- a/com/android/server/policy/keyguard/KeyguardServiceDelegate.java +++ b/com/android/server/policy/keyguard/KeyguardServiceDelegate.java @@ -1,5 +1,7 @@ package com.android.server.policy.keyguard; +import static android.view.Display.INVALID_DISPLAY; + import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; @@ -13,6 +15,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; import android.util.Slog; +import android.view.WindowManagerPolicy; import android.view.WindowManagerPolicy.OnKeyguardExitResult; import com.android.internal.policy.IKeyguardDismissCallback; @@ -201,7 +204,10 @@ public class KeyguardServiceDelegate { mKeyguardState.reset(); mHandler.post(() -> { try { - ActivityManager.getService().setLockScreenShown(true); + // There are no longer any keyguard windows on secondary displays, so pass + // INVALID_DISPLAY. All that means is that showWhenLocked activities on + // secondary displays now get to show. + ActivityManager.getService().setLockScreenShown(true, INVALID_DISPLAY); } catch (RemoteException e) { // Local call. } @@ -412,13 +418,45 @@ public class KeyguardServiceDelegate { pw.println(prefix + "systemIsReady=" + mKeyguardState.systemIsReady); pw.println(prefix + "deviceHasKeyguard=" + mKeyguardState.deviceHasKeyguard); pw.println(prefix + "enabled=" + mKeyguardState.enabled); - pw.println(prefix + "offReason=" + mKeyguardState.offReason); + pw.println(prefix + "offReason=" + + WindowManagerPolicy.offReasonToString(mKeyguardState.offReason)); pw.println(prefix + "currentUser=" + mKeyguardState.currentUser); pw.println(prefix + "bootCompleted=" + mKeyguardState.bootCompleted); - pw.println(prefix + "screenState=" + mKeyguardState.screenState); - pw.println(prefix + "interactiveState=" + mKeyguardState.interactiveState); + pw.println(prefix + "screenState=" + screenStateToString(mKeyguardState.screenState)); + pw.println(prefix + "interactiveState=" + + interactiveStateToString(mKeyguardState.interactiveState)); if (mKeyguardService != null) { mKeyguardService.dump(prefix, pw); } } + + private static String screenStateToString(int screen) { + switch (screen) { + case SCREEN_STATE_OFF: + return "SCREEN_STATE_OFF"; + case SCREEN_STATE_TURNING_ON: + return "SCREEN_STATE_TURNING_ON"; + case SCREEN_STATE_ON: + return "SCREEN_STATE_ON"; + case SCREEN_STATE_TURNING_OFF: + return "SCREEN_STATE_TURNING_OFF"; + default: + return Integer.toString(screen); + } + } + + private static String interactiveStateToString(int interactive) { + switch (interactive) { + case INTERACTIVE_STATE_SLEEP: + return "INTERACTIVE_STATE_SLEEP"; + case INTERACTIVE_STATE_WAKING: + return "INTERACTIVE_STATE_WAKING"; + case INTERACTIVE_STATE_AWAKE: + return "INTERACTIVE_STATE_AWAKE"; + case INTERACTIVE_STATE_GOING_TO_SLEEP: + return "INTERACTIVE_STATE_GOING_TO_SLEEP"; + default: + return Integer.toString(interactive); + } + } } diff --git a/com/android/server/power/PowerManagerService.java b/com/android/server/power/PowerManagerService.java index 3b701302..b917dae2 100644 --- a/com/android/server/power/PowerManagerService.java +++ b/com/android/server/power/PowerManagerService.java @@ -206,6 +206,8 @@ public final class PowerManagerService extends SystemService private static final String REASON_REBOOT = "reboot"; private static final String REASON_USERREQUESTED = "shutdown,userrequested"; private static final String REASON_THERMAL_SHUTDOWN = "shutdown,thermal"; + private static final String REASON_LOW_BATTERY = "shutdown,battery"; + private static final String REASON_BATTERY_THERMAL_STATE = "shutdown,thermal,battery"; private static final String TRACE_SCREEN_ON = "Screen turning on"; @@ -1569,12 +1571,15 @@ public final class PowerManagerService extends SystemService return true; } - private void setWakefulnessLocked(int wakefulness, int reason) { + @VisibleForTesting + void setWakefulnessLocked(int wakefulness, int reason) { if (mWakefulness != wakefulness) { mWakefulness = wakefulness; mWakefulnessChanging = true; mDirty |= DIRTY_WAKEFULNESS; - mNotifier.onWakefulnessChangeStarted(wakefulness, reason); + if (mNotifier != null) { + mNotifier.onWakefulnessChangeStarted(wakefulness, reason); + } } } @@ -2432,11 +2437,8 @@ public final class PowerManagerService extends SystemService return value >= -1.0f && value <= 1.0f; } - private int getDesiredScreenPolicyLocked() { - if (mIsVrModeEnabled) { - return DisplayPowerRequest.POLICY_VR; - } - + @VisibleForTesting + int getDesiredScreenPolicyLocked() { if (mWakefulness == WAKEFULNESS_ASLEEP || sQuiescent) { return DisplayPowerRequest.POLICY_OFF; } @@ -2452,6 +2454,13 @@ public final class PowerManagerService extends SystemService // doze after screen off. This causes the screen off transition to be skipped. } + // It is important that POLICY_VR check happens after the wakefulness checks above so + // that VR-mode does not prevent displays from transitioning to the correct state when + // dozing or sleeping. + if (mIsVrModeEnabled) { + return DisplayPowerRequest.POLICY_VR; + } + if ((mWakeLockSummary & WAKE_LOCK_SCREEN_BRIGHT) != 0 || (mUserActivitySummary & USER_ACTIVITY_SCREEN_BRIGHT) != 0 || !mBootCompleted @@ -3113,6 +3122,11 @@ public final class PowerManagerService extends SystemService } } + @VisibleForTesting + void setVrModeEnabled(boolean enabled) { + mIsVrModeEnabled = enabled; + } + private void powerHintInternal(int hintId, int data) { nativeSendPowerHint(hintId, data); } @@ -3810,7 +3824,7 @@ public final class PowerManagerService extends SystemService synchronized (mLock) { if (mIsVrModeEnabled != enabled) { - mIsVrModeEnabled = enabled; + setVrModeEnabled(enabled); mDirty |= DIRTY_VR_MODE_CHANGED; updatePowerStateLocked(); } @@ -4639,6 +4653,10 @@ public final class PowerManagerService extends SystemService return PowerManager.SHUTDOWN_REASON_USER_REQUESTED; case REASON_THERMAL_SHUTDOWN: return PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN; + case REASON_LOW_BATTERY: + return PowerManager.SHUTDOWN_REASON_LOW_BATTERY; + case REASON_BATTERY_THERMAL_STATE: + return PowerManager.SHUTDOWN_REASON_BATTERY_THERMAL; default: return PowerManager.SHUTDOWN_REASON_UNKNOWN; } diff --git a/com/android/server/power/ShutdownThread.java b/com/android/server/power/ShutdownThread.java index 853e1b26..515fa399 100644 --- a/com/android/server/power/ShutdownThread.java +++ b/com/android/server/power/ShutdownThread.java @@ -457,8 +457,7 @@ public final class ShutdownThread extends Thread { // First send the high-level shut down broadcast. mActionDone = false; Intent intent = new Intent(Intent.ACTION_SHUTDOWN); - intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND - | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL, null, br, mHandler, 0, null, null); diff --git a/com/android/server/stats/StatsCompanionService.java b/com/android/server/stats/StatsCompanionService.java new file mode 100644 index 00000000..f1fb3e7b --- /dev/null +++ b/com/android/server/stats/StatsCompanionService.java @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2017 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.stats; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.os.IStatsCompanionService; +import android.os.IStatsManager; +import android.os.Process; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.KernelWakelockReader; +import com.android.internal.os.KernelWakelockStats; +import com.android.server.SystemService; + +import java.util.Map; + +/** + * Helper service for statsd (the native stats management service in cmds/statsd/). + * Used for registering and receiving alarms on behalf of statsd. + * @hide + */ +public class StatsCompanionService extends IStatsCompanionService.Stub { + static final String TAG = "StatsCompanionService"; + static final boolean DEBUG = true; + + private final Context mContext; + private final AlarmManager mAlarmManager; + @GuardedBy("sStatsdLock") + private static IStatsManager sStatsd; + private static final Object sStatsdLock = new Object(); + + private final PendingIntent mAnomalyAlarmIntent; + private final PendingIntent mPollingAlarmIntent; + + public StatsCompanionService(Context context) { + super(); + mContext = context; + mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); + + mAnomalyAlarmIntent = PendingIntent.getBroadcast(mContext, 0, + new Intent(mContext, AnomalyAlarmReceiver.class), 0); + mPollingAlarmIntent = PendingIntent.getBroadcast(mContext, 0, + new Intent(mContext, PollingAlarmReceiver.class), 0); + } + + public final static class AnomalyAlarmReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred."); + synchronized (sStatsdLock) { + if (sStatsd == null) { + Slog.w(TAG, "Could not access statsd to inform it of anomaly alarm firing"); + return; + } + try { + // Two-way call to statsd to retain AlarmManager wakelock + sStatsd.informAnomalyAlarmFired(); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to inform statsd of anomaly alarm firing", e); + } + } + // AlarmManager releases its own wakelock here. + } + }; + + public final static class PollingAlarmReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (DEBUG) Slog.d(TAG, "Time to poll something."); + synchronized (sStatsdLock) { + if (sStatsd == null) { + Slog.w(TAG, "Could not access statsd to inform it of polling alarm firing"); + return; + } + try { + // Two-way call to statsd to retain AlarmManager wakelock + sStatsd.informPollAlarmFired(); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to inform statsd of polling alarm firing", e); + } + } + // AlarmManager releases its own wakelock here. + } + }; + + @Override // Binder call + public void setAnomalyAlarm(long timestampMs) { + enforceCallingPermission(); + if (DEBUG) Slog.d(TAG, "Setting anomaly alarm for " + timestampMs); + final long callingToken = Binder.clearCallingIdentity(); + try { + // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens. + // This alarm is inexact, leaving its exactness completely up to the OS optimizations. + // AlarmManager will automatically cancel any previous mAnomalyAlarmIntent alarm. + mAlarmManager.set(AlarmManager.RTC, timestampMs, mAnomalyAlarmIntent); + } finally { + Binder.restoreCallingIdentity(callingToken); + } + } + + @Override // Binder call + public void cancelAnomalyAlarm() { + enforceCallingPermission(); + if (DEBUG) Slog.d(TAG, "Cancelling anomaly alarm"); + final long callingToken = Binder.clearCallingIdentity(); + try { + mAlarmManager.cancel(mAnomalyAlarmIntent); + } finally { + Binder.restoreCallingIdentity(callingToken); + } + } + + @Override // Binder call + public void setPollingAlarms(long timestampMs, long intervalMs) { + enforceCallingPermission(); + if (DEBUG) Slog.d(TAG, "Setting polling alarm for " + timestampMs + + " every " + intervalMs + "ms"); + final long callingToken = Binder.clearCallingIdentity(); + try { + // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens. + // This alarm is inexact, leaving its exactness completely up to the OS optimizations. + // TODO: totally inexact means that stats per bucket could be quite off. Is this okay? + mAlarmManager.setRepeating(AlarmManager.RTC, timestampMs, intervalMs, + mPollingAlarmIntent); + } finally { + Binder.restoreCallingIdentity(callingToken); + } + } + + @Override // Binder call + public void cancelPollingAlarms() { + enforceCallingPermission(); + if (DEBUG) Slog.d(TAG, "Cancelling polling alarm"); + final long callingToken = Binder.clearCallingIdentity(); + try { + mAlarmManager.cancel(mPollingAlarmIntent); + } finally { + Binder.restoreCallingIdentity(callingToken); + } + } + + // These values must be kept in sync with cmd/statsd/StatsPullerManager.h. + // TODO: pull the constant from stats_events.proto instead + private static final int PULL_CODE_KERNEL_WAKELOCKS = 20; + + private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader(); + private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats(); + + @Override // Binder call + public String pullData(int pullCode) { + enforceCallingPermission(); + if (DEBUG) Slog.d(TAG, "Fetching " + pullCode); + + StringBuilder s = new StringBuilder(); // TODO: use and return a Parcel instead of a string + switch (pullCode) { + case PULL_CODE_KERNEL_WAKELOCKS: + final KernelWakelockStats wakelockStats = + mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats); + + for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) { + String name = ent.getKey(); + KernelWakelockStats.Entry kws = ent.getValue(); + s.append("Wakelock ") + .append(name) + .append(", time=") + .append(kws.mTotalTime) + .append(", count=") + .append(kws.mCount) + .append('\n'); + } + break; + default: + Slog.w(TAG, "No such pollable data as " + pullCode); + return null; + } + return s.toString(); + } + + @Override // Binder call + public void statsdReady() { + enforceCallingPermission(); + if (DEBUG) Slog.d(TAG, "learned that statsdReady"); + sayHiToStatsd(); // tell statsd that we're ready too and link to it + } + + private void enforceCallingPermission() { + if (Binder.getCallingPid() == Process.myPid()) { + return; + } + mContext.enforceCallingPermission(android.Manifest.permission.STATSCOMPANION, null); + } + + // Lifecycle and related code + + /** Fetches the statsd IBinder service */ + private static IStatsManager fetchStatsdService() { + return IStatsManager.Stub.asInterface(ServiceManager.getService("stats")); + } + + public static final class Lifecycle extends SystemService { + private StatsCompanionService mStatsCompanionService; + + public Lifecycle(Context context) { + super(context); + } + + @Override + public void onStart() { + mStatsCompanionService = new StatsCompanionService(getContext()); + try { + publishBinderService(Context.STATS_COMPANION_SERVICE, mStatsCompanionService); + if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_COMPANION_SERVICE); + } catch (Exception e) { + Slog.e(TAG, "Failed to publishBinderService", e); + } + } + + @Override + public void onBootPhase(int phase) { + super.onBootPhase(phase); + if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { + mStatsCompanionService.systemReady(); + } + } + } + + /** Now that the android system is ready, StatsCompanion is ready too, so inform statsd. */ + private void systemReady() { + if (DEBUG) Slog.d(TAG, "Learned that systemReady"); + sayHiToStatsd(); + } + + /** Tells statsd that statscompanion is ready. If the binder call returns, link to statsd. */ + private void sayHiToStatsd() { + synchronized (sStatsdLock) { + if (sStatsd != null) { + Slog.e(TAG, "Trying to fetch statsd, but it was already fetched", + new IllegalStateException("sStatsd is not null when being fetched")); + return; + } + sStatsd = fetchStatsdService(); + if (sStatsd == null) { + Slog.w(TAG, "Could not access statsd"); + return; + } + if (DEBUG) Slog.d(TAG, "Saying hi to statsd"); + try { + sStatsd.statsCompanionReady(); + // If the statsCompanionReady two-way binder call returns, link to statsd. + try { + sStatsd.asBinder().linkToDeath(new StatsdDeathRecipient(), 0); + } catch (RemoteException e) { + Slog.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e); + forgetEverything(); + } + } catch (RemoteException e) { + Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e); + forgetEverything(); + } + } + } + + private class StatsdDeathRecipient implements IBinder.DeathRecipient { + @Override + public void binderDied() { + Slog.i(TAG, "Statsd is dead - erase all my knowledge."); + forgetEverything(); + } + } + + private void forgetEverything() { + synchronized (sStatsdLock) { + sStatsd = null; + cancelAnomalyAlarm(); + cancelPollingAlarms(); + } + } + +} diff --git a/com/android/server/statusbar/StatusBarManagerService.java b/com/android/server/statusbar/StatusBarManagerService.java index 38dc33fa..bdfbe481 100644 --- a/com/android/server/statusbar/StatusBarManagerService.java +++ b/com/android/server/statusbar/StatusBarManagerService.java @@ -31,6 +31,7 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.UserHandle; +import android.service.notification.NotificationStats; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Slog; @@ -97,12 +98,58 @@ public class StatusBarManagerService extends IStatusBarService.Stub { int what2; IBinder token; + public DisableRecord(int userId, IBinder token) { + this.userId = userId; + this.token = token; + try { + token.linkToDeath(this, 0); + } catch (RemoteException re) { + // Give up + } + } + + @Override public void binderDied() { Slog.i(TAG, "binder died for pkg=" + pkg); disableForUser(0, token, pkg, userId); disable2ForUser(0, token, pkg, userId); token.unlinkToDeath(this, 0); } + + public void setFlags(int what, int which, String pkg) { + switch (which) { + case 1: + what1 = what; + return; + case 2: + what2 = what; + return; + default: + Slog.w(TAG, "Can't set unsupported disable flag " + which + + ": 0x" + Integer.toHexString(what)); + } + this.pkg = pkg; + } + + public int getFlags(int which) { + switch (which) { + case 1: return what1; + case 2: return what2; + default: + Slog.w(TAG, "Can't get unsupported disable flag " + which); + return 0; + } + } + + public boolean isEmpty() { + return what1 == 0 && what2 == 0; + } + + @Override + public String toString() { + return String.format("userId=%d what1=0x%08X what2=0x%08X pkg=%s token=%s", + userId, what1, what2, pkg, token); + } } /** @@ -483,7 +530,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub { */ @Override public void disable2ForUser(int what, IBinder token, String pkg, int userId) { - enforceStatusBar(); + enforceStatusBarService(); synchronized (mLock) { disableLocked(userId, what, token, pkg, 2); @@ -897,13 +944,15 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } @Override - public void onNotificationClear(String pkg, String tag, int id, int userId) { + public void onNotificationClear(String pkg, String tag, int id, int userId, String key, + @NotificationStats.DismissalSurface int dismissalSurface) { enforceStatusBarService(); final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); long identity = Binder.clearCallingIdentity(); try { - mNotificationDelegate.onNotificationClear(callingUid, callingPid, pkg, tag, id, userId); + mNotificationDelegate.onNotificationClear( + callingUid, callingPid, pkg, tag, id, userId, key, dismissalSurface); } finally { Binder.restoreCallingIdentity(identity); } @@ -937,6 +986,28 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } @Override + public void onNotificationDirectReplied(String key) throws RemoteException { + enforceStatusBarService(); + long identity = Binder.clearCallingIdentity(); + try { + mNotificationDelegate.onNotificationDirectReplied(key); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void onNotificationSettingsViewed(String key) throws RemoteException { + enforceStatusBarService(); + long identity = Binder.clearCallingIdentity(); + try { + mNotificationDelegate.onNotificationSettingsViewed(key); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public void onClearAllNotifications(int userId) { enforceStatusBarService(); final int callingUid = Binder.getCallingUid(); @@ -970,42 +1041,42 @@ public class StatusBarManagerService extends IStatusBarService.Stub { Slog.d(TAG, "manageDisableList userId=" + userId + " what=0x" + Integer.toHexString(what) + " pkg=" + pkg); } - // update the list + + // Find matching record, if any final int N = mDisableRecords.size(); - DisableRecord tok = null; + DisableRecord record = null; int i; - for (i=0; i<N; i++) { - DisableRecord t = mDisableRecords.get(i); - if (t.token == token && t.userId == userId) { - tok = t; + for (i = 0; i < N; i++) { + DisableRecord r = mDisableRecords.get(i); + if (r.token == token && r.userId == userId) { + record = r; break; } } - if (what == 0 || !token.isBinderAlive()) { - if (tok != null) { + + // Remove record if binder is already dead + if (!token.isBinderAlive()) { + if (record != null) { mDisableRecords.remove(i); - tok.token.unlinkToDeath(tok, 0); - } - } else { - if (tok == null) { - tok = new DisableRecord(); - tok.userId = userId; - try { - token.linkToDeath(tok, 0); - } - catch (RemoteException ex) { - return; // give up - } - mDisableRecords.add(tok); + record.token.unlinkToDeath(record, 0); } - if (which == 1) { - tok.what1 = what; - } else { - tok.what2 = what; + return; + } + + // Update existing record + if (record != null) { + record.setFlags(what, which, pkg); + if (record.isEmpty()) { + mDisableRecords.remove(i); + record.token.unlinkToDeath(record, 0); } - tok.token = token; - tok.pkg = pkg; + return; } + + // Record doesn't exist, so we create a new one + record = new DisableRecord(userId, token); + record.setFlags(what, which, pkg); + mDisableRecords.add(record); } // lock on mDisableRecords @@ -1016,7 +1087,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub { for (int i=0; i<N; i++) { final DisableRecord rec = mDisableRecords.get(i); if (rec.userId == userId) { - net |= (which == 1) ? rec.what1 : rec.what2; + net |= rec.getFlags(which); } } return net; @@ -1036,11 +1107,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub { pw.println(" mDisableRecords.size=" + N); for (int i=0; i<N; i++) { DisableRecord tok = mDisableRecords.get(i); - pw.println(" [" + i + "] userId=" + tok.userId - + " what1=0x" + Integer.toHexString(tok.what1) - + " what2=0x" + Integer.toHexString(tok.what2) - + " pkg=" + tok.pkg - + " token=" + tok.token); + pw.println(" [" + i + "] " + tok); } pw.println(" mCurrentUserId=" + mCurrentUserId); pw.println(" mIcons="); diff --git a/com/android/server/storage/AppCollector.java b/com/android/server/storage/AppCollector.java index 03b754f7..0b51f9cc 100644 --- a/com/android/server/storage/AppCollector.java +++ b/com/android/server/storage/AppCollector.java @@ -135,7 +135,7 @@ public class AppCollector { PackageStats packageStats = new PackageStats(app.packageName, user.id); packageStats.cacheSize = storageStats.getCacheBytes(); - packageStats.codeSize = storageStats.getCodeBytes(); + packageStats.codeSize = storageStats.getAppBytes(); packageStats.dataSize = storageStats.getDataBytes(); stats.add(packageStats); } catch (NameNotFoundException | IOException e) { diff --git a/com/android/server/storage/DiskStatsFileLogger.java b/com/android/server/storage/DiskStatsFileLogger.java index 0094ab55..1db3ec4c 100644 --- a/com/android/server/storage/DiskStatsFileLogger.java +++ b/com/android/server/storage/DiskStatsFileLogger.java @@ -56,10 +56,12 @@ public class DiskStatsFileLogger { public static final String SYSTEM_KEY = "systemSize"; public static final String MISC_KEY = "otherSize"; public static final String APP_SIZE_AGG_KEY = "appSize"; + public static final String APP_DATA_SIZE_AGG_KEY = "appDataSize"; public static final String APP_CACHE_AGG_KEY = "cacheSize"; public static final String PACKAGE_NAMES_KEY = "packageNames"; public static final String APP_SIZES_KEY = "appSizes"; public static final String APP_CACHES_KEY = "cacheSizes"; + public static final String APP_DATA_KEY = "appDataSizes"; public static final String LAST_QUERY_TIMESTAMP_KEY = "queryTime"; private MeasurementResult mResult; @@ -114,31 +116,39 @@ public class DiskStatsFileLogger { private void addAppsToJson(JSONObject json) throws JSONException { JSONArray names = new JSONArray(); JSONArray appSizeList = new JSONArray(); + JSONArray appDataSizeList = new JSONArray(); JSONArray cacheSizeList = new JSONArray(); long appSizeSum = 0L; + long appDataSizeSum = 0L; long cacheSizeSum = 0L; boolean isExternal = Environment.isExternalStorageEmulated(); for (Map.Entry<String, PackageStats> entry : filterOnlyPrimaryUser().entrySet()) { PackageStats stat = entry.getValue(); - long appSize = stat.codeSize + stat.dataSize; + long appSize = stat.codeSize; + long appDataSize = stat.dataSize; long cacheSize = stat.cacheSize; if (isExternal) { - appSize += stat.externalCodeSize + stat.externalDataSize; + appSize += stat.externalCodeSize; + appDataSize += stat.externalDataSize; cacheSize += stat.externalCacheSize; } appSizeSum += appSize; + appDataSizeSum += appDataSize; cacheSizeSum += cacheSize; names.put(stat.packageName); appSizeList.put(appSize); + appDataSizeList.put(appDataSize); cacheSizeList.put(cacheSize); } json.put(PACKAGE_NAMES_KEY, names); json.put(APP_SIZES_KEY, appSizeList); json.put(APP_CACHES_KEY, cacheSizeList); + json.put(APP_DATA_KEY, appDataSizeList); json.put(APP_SIZE_AGG_KEY, appSizeSum); json.put(APP_CACHE_AGG_KEY, cacheSizeSum); + json.put(APP_DATA_SIZE_AGG_KEY, appDataSizeSum); } /** diff --git a/com/android/server/timezone/IntentHelper.java b/com/android/server/timezone/IntentHelper.java index 0cb90657..5de54321 100644 --- a/com/android/server/timezone/IntentHelper.java +++ b/com/android/server/timezone/IntentHelper.java @@ -23,15 +23,22 @@ package com.android.server.timezone; */ interface IntentHelper { - void initialize(String updateAppPackageName, String dataAppPackageName, Listener listener); + void initialize(String updateAppPackageName, String dataAppPackageName, + PackageTracker packageTracker); void sendTriggerUpdateCheck(CheckToken checkToken); - void enableReliabilityTriggering(); + /** + * Schedule a "reliability trigger" after at least minimumDelayMillis, replacing any existing + * scheduled one. A reliability trigger ensures that the {@link PackageTracker} can pick up + * reliably if a previous update check did not complete for some reason. It can happen when + * the device is idle. The trigger is expected to call + * {@link PackageTracker#triggerUpdateIfNeeded(boolean)} with a {@code false} value. + */ + void scheduleReliabilityTrigger(long minimumDelayMillis); - void disableReliabilityTriggering(); - - interface Listener { - void triggerUpdateIfNeeded(boolean packageUpdated); - } + /** + * Make sure there is no reliability trigger scheduled. No-op if there wasn't one. + */ + void unscheduleReliabilityTrigger(); } diff --git a/com/android/server/timezone/IntentHelperImpl.java b/com/android/server/timezone/IntentHelperImpl.java index 6db70cd8..6e6259d9 100644 --- a/com/android/server/timezone/IntentHelperImpl.java +++ b/com/android/server/timezone/IntentHelperImpl.java @@ -24,6 +24,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.PatternMatcher; +import android.os.UserHandle; import android.util.Slog; /** @@ -36,16 +37,13 @@ final class IntentHelperImpl implements IntentHelper { private final Context mContext; private String mUpdaterAppPackageName; - private boolean mReliabilityReceiverEnabled; - private Receiver mReliabilityReceiver; - IntentHelperImpl(Context context) { mContext = context; } @Override - public void initialize( - String updaterAppPackageName, String dataAppPackageName, Listener listener) { + public void initialize(String updaterAppPackageName, String dataAppPackageName, + PackageTracker packageTracker) { mUpdaterAppPackageName = updaterAppPackageName; // Register for events of interest. @@ -78,10 +76,10 @@ final class IntentHelperImpl implements IntentHelper { // We do not register for ACTION_PACKAGE_DATA_CLEARED because the updater / data apps are // not expected to need local data. - Receiver packageUpdateReceiver = new Receiver(listener, true /* packageUpdated */); - mContext.registerReceiver(packageUpdateReceiver, packageIntentFilter); - - mReliabilityReceiver = new Receiver(listener, false /* packageUpdated */); + Receiver packageUpdateReceiver = new Receiver(packageTracker); + mContext.registerReceiverAsUser( + packageUpdateReceiver, UserHandle.SYSTEM, packageIntentFilter, + null /* broadcastPermission */, null /* default handler */); } /** Sends an intent to trigger an update check. */ @@ -93,39 +91,26 @@ final class IntentHelperImpl implements IntentHelper { } @Override - public synchronized void enableReliabilityTriggering() { - if (!mReliabilityReceiverEnabled) { - // The intent filter that exists to make updates reliable in the event of failures / - // reboots. - IntentFilter reliabilityIntentFilter = new IntentFilter(); - reliabilityIntentFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_START); - mContext.registerReceiver(mReliabilityReceiver, reliabilityIntentFilter); - mReliabilityReceiverEnabled = true; - } + public synchronized void scheduleReliabilityTrigger(long minimumDelayMillis) { + TimeZoneUpdateIdler.schedule(mContext, minimumDelayMillis); } @Override - public synchronized void disableReliabilityTriggering() { - if (mReliabilityReceiverEnabled) { - mContext.unregisterReceiver(mReliabilityReceiver); - mReliabilityReceiverEnabled = false; - } + public synchronized void unscheduleReliabilityTrigger() { + TimeZoneUpdateIdler.unschedule(mContext); } private static class Receiver extends BroadcastReceiver { - private final Listener mListener; - private final boolean mPackageUpdated; + private final PackageTracker mPackageTracker; - private Receiver(Listener listener, boolean packageUpdated) { - mListener = listener; - mPackageUpdated = packageUpdated; + private Receiver(PackageTracker packageTracker) { + mPackageTracker = packageTracker; } @Override public void onReceive(Context context, Intent intent) { Slog.d(TAG, "Received intent: " + intent.toString()); - mListener.triggerUpdateIfNeeded(mPackageUpdated); + mPackageTracker.triggerUpdateIfNeeded(true /* packageChanged */); } } - } diff --git a/com/android/server/timezone/PackageTracker.java b/com/android/server/timezone/PackageTracker.java index 24e0fe48..f0306b9b 100644 --- a/com/android/server/timezone/PackageTracker.java +++ b/com/android/server/timezone/PackageTracker.java @@ -51,7 +51,7 @@ import java.io.PrintWriter; */ // Also made non-final so it can be mocked. @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) -public class PackageTracker implements IntentHelper.Listener { +public class PackageTracker { private static final String TAG = "timezone.PackageTracker"; private final PackageManagerHelper mPackageManagerHelper; @@ -72,6 +72,13 @@ public class PackageTracker implements IntentHelper.Listener { // The number of failed checks in a row before reliability checks should stop happening. private long mFailedCheckRetryCount; + /* + * The minimum delay between a successive reliability triggers / other operations. Should to be + * larger than mCheckTimeAllowedMillis to avoid reliability triggers happening during package + * update checks. + */ + private int mDelayBeforeReliabilityCheckMillis; + // Reliability check state: If a check was triggered but not acknowledged within // mCheckTimeAllowedMillis then another one can be triggered. private Long mLastTriggerTimestamp = null; @@ -122,6 +129,7 @@ public class PackageTracker implements IntentHelper.Listener { mDataAppPackageName = mConfigHelper.getDataAppPackageName(); mCheckTimeAllowedMillis = mConfigHelper.getCheckTimeAllowedMillis(); mFailedCheckRetryCount = mConfigHelper.getFailedCheckRetryCount(); + mDelayBeforeReliabilityCheckMillis = mCheckTimeAllowedMillis + (60 * 1000); // Validate the device configuration including the application packages. // The manifest entries in the apps themselves are not validated until use as they can @@ -135,9 +143,10 @@ public class PackageTracker implements IntentHelper.Listener { // Initialize the intent helper. mIntentHelper.initialize(mUpdateAppPackageName, mDataAppPackageName, this); - // Enable the reliability triggering so we will have at least one reliability trigger if - // a package isn't updated. - mIntentHelper.enableReliabilityTriggering(); + // Schedule a reliability trigger so we will have at least one after boot. This will allow + // us to catch if a package updated wasn't handled to completion. There's no hurry: it's ok + // to delay for a while before doing this even if idle. + mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis); Slog.i(TAG, "Time zone updater / data package tracking enabled"); } @@ -195,7 +204,6 @@ public class PackageTracker implements IntentHelper.Listener { * @param packageChanged true if this method was called because a known packaged definitely * changed, false if the cause is a reliability trigger */ - @Override public synchronized void triggerUpdateIfNeeded(boolean packageChanged) { if (!mTrackingEnabled) { throw new IllegalStateException("Unexpected call. Tracking is disabled."); @@ -212,8 +220,8 @@ public class PackageTracker implements IntentHelper.Listener { + " updaterApp=" + updaterAppManifestValid + ", dataApp=" + dataAppManifestValid); - // There's no point in doing reliability checks if the current packages are bad. - mIntentHelper.disableReliabilityTriggering(); + // There's no point in doing any reliability triggers if the current packages are bad. + mIntentHelper.unscheduleReliabilityTrigger(); return; } @@ -238,7 +246,8 @@ public class PackageTracker implements IntentHelper.Listener { Slog.d(TAG, "triggerUpdateIfNeeded: checkComplete call is not yet overdue." + " Not triggering."); - // Not doing any work, but also not disabling future reliability triggers. + // Don't do any work now but we do schedule a future reliability trigger. + mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis); return; } } else if (mCheckFailureCount > mFailedCheckRetryCount) { @@ -247,13 +256,13 @@ public class PackageTracker implements IntentHelper.Listener { Slog.i(TAG, "triggerUpdateIfNeeded: number of allowed consecutive check failures" + " exceeded. Stopping reliability triggers until next reboot or package" + " update."); - mIntentHelper.disableReliabilityTriggering(); + mIntentHelper.unscheduleReliabilityTrigger(); return; } else if (mCheckFailureCount == 0) { // Case 4. Slog.i(TAG, "triggerUpdateIfNeeded: No reliability check required. Last check was" + " successful."); - mIntentHelper.disableReliabilityTriggering(); + mIntentHelper.unscheduleReliabilityTrigger(); return; } } @@ -263,7 +272,7 @@ public class PackageTracker implements IntentHelper.Listener { if (currentInstalledVersions == null) { // This should not happen if the device is configured in a valid way. Slog.e(TAG, "triggerUpdateIfNeeded: currentInstalledVersions was null"); - mIntentHelper.disableReliabilityTriggering(); + mIntentHelper.unscheduleReliabilityTrigger(); return; } @@ -288,7 +297,7 @@ public class PackageTracker implements IntentHelper.Listener { // The last check succeeded and nothing has changed. Do nothing and disable // reliability checks. Slog.i(TAG, "triggerUpdateIfNeeded: Prior check succeeded. No need to trigger."); - mIntentHelper.disableReliabilityTriggering(); + mIntentHelper.unscheduleReliabilityTrigger(); return; } } @@ -299,6 +308,8 @@ public class PackageTracker implements IntentHelper.Listener { if (checkToken == null) { Slog.w(TAG, "triggerUpdateIfNeeded: Unable to generate check token." + " Not sending check request."); + // Trigger again later: perhaps we'll have better luck. + mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis); return; } @@ -309,9 +320,9 @@ public class PackageTracker implements IntentHelper.Listener { // Update the reliability check state in case the update fails. setCheckInProgress(); - // Enable reliability triggering in case the check doesn't succeed and there is no - // response at all. Enabling reliability triggering is idempotent. - mIntentHelper.enableReliabilityTriggering(); + // Schedule a reliability trigger in case the update check doesn't succeed and there is no + // response at all. It will be cancelled if the check is successful in recordCheckResult. + mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis); } /** @@ -370,9 +381,9 @@ public class PackageTracker implements IntentHelper.Listener { + " storage state."); mPackageStatusStorage.resetCheckState(); - // Enable reliability triggering and reset the failure count so we know that the + // Schedule a reliability trigger and reset the failure count so we know that the // next reliability trigger will do something. - mIntentHelper.enableReliabilityTriggering(); + mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis); mCheckFailureCount = 0; } else { // This is the expected case when tracking is enabled: a check was triggered and it has @@ -385,13 +396,13 @@ public class PackageTracker implements IntentHelper.Listener { setCheckComplete(); if (success) { - // Since the check was successful, no more reliability checks are required until + // Since the check was successful, no reliability trigger is required until // there is a package change. - mIntentHelper.disableReliabilityTriggering(); + mIntentHelper.unscheduleReliabilityTrigger(); mCheckFailureCount = 0; } else { - // Enable reliability triggering to potentially check again in future. - mIntentHelper.enableReliabilityTriggering(); + // Enable schedule a reliability trigger to check again in future. + mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis); mCheckFailureCount++; } } else { @@ -400,8 +411,8 @@ public class PackageTracker implements IntentHelper.Listener { Slog.i(TAG, "recordCheckResult: could not update token=" + checkToken + " with success=" + success + ". Optimistic lock failure"); - // Enable reliability triggering to potentially try again in future. - mIntentHelper.enableReliabilityTriggering(); + // Schedule a reliability trigger to potentially try again in future. + mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis); mCheckFailureCount++; } } @@ -515,6 +526,7 @@ public class PackageTracker implements IntentHelper.Listener { ", mUpdateAppPackageName='" + mUpdateAppPackageName + '\'' + ", mDataAppPackageName='" + mDataAppPackageName + '\'' + ", mCheckTimeAllowedMillis=" + mCheckTimeAllowedMillis + + ", mDelayBeforeReliabilityCheckMillis=" + mDelayBeforeReliabilityCheckMillis + ", mFailedCheckRetryCount=" + mFailedCheckRetryCount + ", mLastTriggerTimestamp=" + mLastTriggerTimestamp + ", mCheckTriggered=" + mCheckTriggered + diff --git a/com/android/server/timezone/PackageTrackerHelperImpl.java b/com/android/server/timezone/PackageTrackerHelperImpl.java index 2e0c21bf..b89dd383 100644 --- a/com/android/server/timezone/PackageTrackerHelperImpl.java +++ b/com/android/server/timezone/PackageTrackerHelperImpl.java @@ -26,6 +26,7 @@ import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.os.SystemClock; +import android.os.UserHandle; import android.util.Slog; import java.util.List; @@ -114,8 +115,8 @@ final class PackageTrackerHelperImpl implements ClockHelper, ConfigHelper, Packa @Override public boolean contentProviderRegistered(String authority, String requiredPackageName) { int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; - ProviderInfo providerInfo = - mPackageManager.resolveContentProvider(authority, flags); + ProviderInfo providerInfo = mPackageManager.resolveContentProviderAsUser( + authority, flags, UserHandle.SYSTEM.getIdentifier()); if (providerInfo == null) { Slog.i(TAG, "contentProviderRegistered: No content provider registered with authority=" + authority); @@ -136,7 +137,8 @@ final class PackageTrackerHelperImpl implements ClockHelper, ConfigHelper, Packa throws PackageManager.NameNotFoundException { int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; - List<ResolveInfo> resolveInfo = mPackageManager.queryBroadcastReceivers(intent, flags); + List<ResolveInfo> resolveInfo = mPackageManager.queryBroadcastReceiversAsUser( + intent, flags, UserHandle.SYSTEM); if (resolveInfo.size() != 1) { Slog.i(TAG, "receiverRegistered: Zero or multiple broadcast receiver registered for" + " intent=" + intent + ", found=" + resolveInfo); diff --git a/com/android/server/timezone/RulesManagerService.java b/com/android/server/timezone/RulesManagerService.java index 3ad4419c..52b49baf 100644 --- a/com/android/server/timezone/RulesManagerService.java +++ b/com/android/server/timezone/RulesManagerService.java @@ -47,6 +47,7 @@ import java.util.Arrays; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import libcore.icu.ICU; +import libcore.util.TimeZoneFinder; import libcore.util.ZoneInfoDB; import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED; @@ -69,18 +70,22 @@ public final class RulesManagerService extends IRulesManager.Stub { DistroVersion.CURRENT_FORMAT_MINOR_VERSION); public static class Lifecycle extends SystemService { - private RulesManagerService mService; - public Lifecycle(Context context) { super(context); } @Override public void onStart() { - mService = RulesManagerService.create(getContext()); - mService.start(); + RulesManagerService service = RulesManagerService.create(getContext()); + service.start(); + + // Publish the binder service so it can be accessed from other (appropriately + // permissioned) processes. + publishBinderService(Context.TIME_ZONE_RULES_MANAGER_SERVICE, service); - publishBinderService(Context.TIME_ZONE_RULES_MANAGER_SERVICE, mService); + // Publish the service instance locally so we can use it directly from within the system + // server from TimeZoneUpdateIdler. + publishLocalService(RulesManagerService.class, service); } } @@ -475,9 +480,10 @@ public final class RulesManagerService extends IRulesManager.Stub { case 'a': { // Report the active rules version (i.e. the rules in use by the current // process). - pw.println("Active rules version (ICU, libcore): " + pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): " + ICU.getTZDataVersion() + "," - + ZoneInfoDB.getInstance().getVersion()); + + ZoneInfoDB.getInstance().getVersion() + "," + + TimeZoneFinder.getInstance().getIanaVersion()); break; } default: { @@ -490,12 +496,24 @@ public final class RulesManagerService extends IRulesManager.Stub { } pw.println("RulesManagerService state: " + toString()); - pw.println("Active rules version (ICU, libcore): " + ICU.getTZDataVersion() + "," - + ZoneInfoDB.getInstance().getVersion()); + pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): " + + ICU.getTZDataVersion() + "," + + ZoneInfoDB.getInstance().getVersion() + "," + + TimeZoneFinder.getInstance().getIanaVersion()); pw.println("Distro state: " + rulesState.toString()); mPackageTracker.dump(pw); } + /** + * Called when the device is considered idle. + */ + void notifyIdle() { + // No package has changed: we are just triggering because the device is idle and there + // *might* be work to do. + final boolean packageChanged = false; + mPackageTracker.triggerUpdateIfNeeded(packageChanged); + } + @Override public String toString() { return "RulesManagerService{" + diff --git a/com/android/server/timezone/TimeZoneUpdateIdler.java b/com/android/server/timezone/TimeZoneUpdateIdler.java new file mode 100644 index 00000000..a7767a4f --- /dev/null +++ b/com/android/server/timezone/TimeZoneUpdateIdler.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2017 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.timezone; + +import com.android.server.LocalServices; + +import android.app.job.JobInfo; +import android.app.job.JobParameters; +import android.app.job.JobScheduler; +import android.app.job.JobService; +import android.content.ComponentName; +import android.content.Context; +import android.util.Slog; + +/** + * A JobService used to trigger time zone rules update work when a device falls idle. + */ +public final class TimeZoneUpdateIdler extends JobService { + + private static final String TAG = "timezone.TimeZoneUpdateIdler"; + + /** The static job ID used to handle on-idle work. */ + // Must be unique within UID (system service) + private static final int TIME_ZONE_UPDATE_IDLE_JOB_ID = 27042305; + + @Override + public boolean onStartJob(JobParameters params) { + RulesManagerService rulesManagerService = + LocalServices.getService(RulesManagerService.class); + + Slog.d(TAG, "onStartJob() called"); + + // Note: notifyIdle() explicitly handles canceling / re-scheduling so no need to reschedule + // here. + rulesManagerService.notifyIdle(); + + // Everything is handled synchronously. We are done. + return false; + } + + @Override + public boolean onStopJob(JobParameters params) { + // Reschedule if stopped unless it was cancelled due to unschedule(). + boolean reschedule = params.getStopReason() != JobParameters.REASON_CANCELED; + Slog.d(TAG, "onStopJob() called: Reschedule=" + reschedule); + return reschedule; + } + + /** + * Schedules the TimeZoneUpdateIdler job service to run once. + * + * @param context Context to use to get a job scheduler. + */ + public static void schedule(Context context, long minimumDelayMillis) { + // Request that the JobScheduler tell us when the device falls idle. + JobScheduler jobScheduler = + (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); + + // The TimeZoneUpdateIdler will send an intent that will trigger the Receiver. + ComponentName idlerJobServiceName = + new ComponentName(context, TimeZoneUpdateIdler.class); + + // We require the device is idle, but also that it is charging to be as non-invasive as + // we can. + JobInfo.Builder jobInfoBuilder = + new JobInfo.Builder(TIME_ZONE_UPDATE_IDLE_JOB_ID, idlerJobServiceName) + .setRequiresDeviceIdle(true) + .setRequiresCharging(true) + .setMinimumLatency(minimumDelayMillis); + + Slog.d(TAG, "schedule() called: minimumDelayMillis=" + minimumDelayMillis); + jobScheduler.schedule(jobInfoBuilder.build()); + } + + /** + * Unschedules the TimeZoneUpdateIdler job service. + * + * @param context Context to use to get a job scheduler. + */ + public static void unschedule(Context context) { + JobScheduler jobScheduler = + (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); + Slog.d(TAG, "unschedule() called"); + jobScheduler.cancel(TIME_ZONE_UPDATE_IDLE_JOB_ID); + } +} diff --git a/com/android/server/twilight/TwilightState.java b/com/android/server/twilight/TwilightState.java index 30a8cccb..71304a7a 100644 --- a/com/android/server/twilight/TwilightState.java +++ b/com/android/server/twilight/TwilightState.java @@ -18,7 +18,10 @@ package com.android.server.twilight; import android.text.format.DateFormat; -import java.util.Calendar; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.TimeZone; /** * The twilight state, consisting of the sunrise and sunset times (in millis) for the current @@ -45,12 +48,11 @@ public final class TwilightState { } /** - * Returns a new {@link Calendar} instance initialized to {@link #sunriseTimeMillis()}. + * Returns a new {@link LocalDateTime} instance initialized to {@link #sunriseTimeMillis()}. */ - public Calendar sunrise() { - final Calendar sunrise = Calendar.getInstance(); - sunrise.setTimeInMillis(mSunriseTimeMillis); - return sunrise; + public LocalDateTime sunrise() { + final ZoneId zoneId = TimeZone.getDefault().toZoneId(); + return LocalDateTime.ofInstant(Instant.ofEpochMilli(mSunriseTimeMillis), zoneId); } /** @@ -62,12 +64,11 @@ public final class TwilightState { } /** - * Returns a new {@link Calendar} instance initialized to {@link #sunsetTimeMillis()}. + * Returns a new {@link LocalDateTime} instance initialized to {@link #sunsetTimeMillis()}. */ - public Calendar sunset() { - final Calendar sunset = Calendar.getInstance(); - sunset.setTimeInMillis(mSunsetTimeMillis); - return sunset; + public LocalDateTime sunset() { + final ZoneId zoneId = TimeZone.getDefault().toZoneId(); + return LocalDateTime.ofInstant(Instant.ofEpochMilli(mSunsetTimeMillis), zoneId); } /** diff --git a/com/android/server/usage/AppStandbyController.java b/com/android/server/usage/AppStandbyController.java new file mode 100644 index 00000000..b2446ba7 --- /dev/null +++ b/com/android/server/usage/AppStandbyController.java @@ -0,0 +1,987 @@ +/** + * Copyright (C) 2017 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.usage; + +import static com.android.server.SystemService.PHASE_BOOT_COMPLETED; +import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY; +import static com.android.server.usage.UsageStatsService.MSG_REPORT_EVENT; + +import android.app.ActivityManager; +import android.app.AppGlobals; +import android.app.admin.DevicePolicyManager; +import android.app.usage.UsageEvents; +import android.app.usage.UsageStatsManagerInternal; +import android.appwidget.AppWidgetManager; +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; +import android.content.pm.ParceledListSlice; +import android.database.ContentObserver; +import android.hardware.display.DisplayManager; +import android.net.NetworkScoreManager; +import android.os.BatteryManager; +import android.os.BatteryStats; +import android.os.Handler; +import android.os.IDeviceIdleController; +import android.os.Looper; +import android.os.Message; +import android.os.PowerManager; +import android.os.Process; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemClock; +import android.os.UserHandle; +import android.provider.Settings; +import android.telephony.TelephonyManager; +import android.util.KeyValueListParser; +import android.util.Slog; +import android.util.SparseIntArray; +import android.util.TimeUtils; +import android.view.Display; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.app.IBatteryStats; +import com.android.internal.os.SomeArgs; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.IndentingPrintWriter; +import com.android.server.LocalServices; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + +/** + * Manages the standby state of an app, listening to various events. + */ +public class AppStandbyController { + + private static final String TAG = "AppStandbyController"; + private static final boolean DEBUG = false; + + static final boolean COMPRESS_TIME = false; + private static final long ONE_MINUTE = 60 * 1000; + + // To name the lock for stack traces + static class Lock {} + + /** Lock to protect the app's standby state. Required for calls into AppIdleHistory */ + private final Object mAppIdleLock = new Lock(); + + /** Keeps the history and state for each app. */ + @GuardedBy("mAppIdleLock") + private AppIdleHistory mAppIdleHistory; + + @GuardedBy("mAppIdleLock") + private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener> + mPackageAccessListeners = new ArrayList<>(); + + /** Whether we've queried the list of carrier privileged apps. */ + @GuardedBy("mAppIdleLock") + private boolean mHaveCarrierPrivilegedApps; + + /** List of carrier-privileged apps that should be excluded from standby */ + @GuardedBy("mAppIdleLock") + private List<String> mCarrierPrivilegedApps; + + // Messages for the handler + static final int MSG_INFORM_LISTENERS = 3; + static final int MSG_FORCE_IDLE_STATE = 4; + static final int MSG_CHECK_IDLE_STATES = 5; + static final int MSG_CHECK_PAROLE_TIMEOUT = 6; + static final int MSG_PAROLE_END_TIMEOUT = 7; + static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8; + static final int MSG_PAROLE_STATE_CHANGED = 9; + static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10; + + long mAppIdleScreenThresholdMillis; + long mCheckIdleIntervalMillis; + long mAppIdleWallclockThresholdMillis; + long mAppIdleParoleIntervalMillis; + long mAppIdleParoleDurationMillis; + boolean mAppIdleEnabled; + boolean mAppIdleTempParoled; + boolean mCharging; + private long mLastAppIdleParoledTime; + private boolean mSystemServicesReady = false; + + private volatile boolean mPendingOneTimeCheckIdleStates; + + private final Handler mHandler; + private final Context mContext; + + private DisplayManager mDisplayManager; + private IDeviceIdleController mDeviceIdleController; + private AppWidgetManager mAppWidgetManager; + private IBatteryStats mBatteryStats; + private PowerManager mPowerManager; + private PackageManager mPackageManager; + private PackageManagerInternal mPackageManagerInternal; + + AppStandbyController(Context context, Looper looper) { + mContext = context; + mHandler = new AppStandbyHandler(looper); + mPackageManager = mContext.getPackageManager(); + mAppIdleEnabled = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_enableAutoPowerModes); + if (mAppIdleEnabled) { + IntentFilter deviceStates = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); + deviceStates.addAction(BatteryManager.ACTION_DISCHARGING); + deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); + mContext.registerReceiver(new DeviceStateReceiver(), deviceStates); + } + synchronized (mAppIdleLock) { + mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime()); + } + + IntentFilter packageFilter = new IntentFilter(); + packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); + packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); + packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); + packageFilter.addDataScheme("package"); + + mContext.registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, packageFilter, + null, mHandler); + } + + public void onBootPhase(int phase) { + if (phase == PHASE_SYSTEM_SERVICES_READY) { + // Observe changes to the threshold + SettingsObserver settingsObserver = new SettingsObserver(mHandler); + settingsObserver.registerObserver(); + settingsObserver.updateSettings(); + + mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class); + mDeviceIdleController = IDeviceIdleController.Stub.asInterface( + ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)); + mBatteryStats = IBatteryStats.Stub.asInterface( + ServiceManager.getService(BatteryStats.SERVICE_NAME)); + mDisplayManager = (DisplayManager) mContext.getSystemService( + Context.DISPLAY_SERVICE); + mPowerManager = mContext.getSystemService(PowerManager.class); + mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); + + mDisplayManager.registerDisplayListener(mDisplayListener, mHandler); + synchronized (mAppIdleLock) { + mAppIdleHistory.updateDisplay(isDisplayOn(), SystemClock.elapsedRealtime()); + } + + if (mPendingOneTimeCheckIdleStates) { + postOneTimeCheckIdleStates(); + } + + mSystemServicesReady = true; + } else if (phase == PHASE_BOOT_COMPLETED) { + setChargingState(mContext.getSystemService(BatteryManager.class).isCharging()); + } + } + + void reportContentProviderUsage(String authority, String providerPkgName, int userId) { + // Get sync adapters for the authority + String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser( + authority, userId); + for (String packageName: packages) { + // Only force the sync adapters to active if the provider is not in the same package and + // the sync adapter is a system package. + try { + PackageInfo pi = mPackageManager.getPackageInfoAsUser( + packageName, PackageManager.MATCH_SYSTEM_ONLY, userId); + if (pi == null || pi.applicationInfo == null) { + continue; + } + if (!packageName.equals(providerPkgName)) { + setAppIdleAsync(packageName, false, userId); + } + } catch (PackageManager.NameNotFoundException e) { + // Shouldn't happen + } + } + } + + void setChargingState(boolean charging) { + synchronized (mAppIdleLock) { + if (mCharging != charging) { + mCharging = charging; + postParoleStateChanged(); + } + } + } + + /** Paroled here means temporary pardon from being inactive */ + void setAppIdleParoled(boolean paroled) { + synchronized (mAppIdleLock) { + final long now = System.currentTimeMillis(); + if (mAppIdleTempParoled != paroled) { + mAppIdleTempParoled = paroled; + if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleTempParoled); + if (paroled) { + postParoleEndTimeout(); + } else { + mLastAppIdleParoledTime = now; + postNextParoleTimeout(now); + } + postParoleStateChanged(); + } + } + } + + boolean isParoledOrCharging() { + synchronized (mAppIdleLock) { + return mAppIdleTempParoled || mCharging; + } + } + + private void postNextParoleTimeout(long now) { + if (DEBUG) Slog.d(TAG, "Posting MSG_CHECK_PAROLE_TIMEOUT"); + mHandler.removeMessages(MSG_CHECK_PAROLE_TIMEOUT); + // Compute when the next parole needs to happen. We check more frequently than necessary + // since the message handler delays are based on elapsedRealTime and not wallclock time. + // The comparison is done in wallclock time. + long timeLeft = (mLastAppIdleParoledTime + mAppIdleParoleIntervalMillis) - now; + if (timeLeft < 0) { + timeLeft = 0; + } + mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft); + } + + private void postParoleEndTimeout() { + if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_END_TIMEOUT"); + mHandler.removeMessages(MSG_PAROLE_END_TIMEOUT); + mHandler.sendEmptyMessageDelayed(MSG_PAROLE_END_TIMEOUT, mAppIdleParoleDurationMillis); + } + + private void postParoleStateChanged() { + if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_STATE_CHANGED"); + mHandler.removeMessages(MSG_PAROLE_STATE_CHANGED); + mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED); + } + + void postCheckIdleStates(int userId) { + mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0)); + } + + /** + * We send a different message to check idle states once, otherwise we would end up + * scheduling a series of repeating checkIdleStates each time we fired off one. + */ + void postOneTimeCheckIdleStates() { + if (mDeviceIdleController == null) { + // Not booted yet; wait for it! + mPendingOneTimeCheckIdleStates = true; + } else { + mHandler.sendEmptyMessage(MSG_ONE_TIME_CHECK_IDLE_STATES); + mPendingOneTimeCheckIdleStates = false; + } + } + + /** + * Check all running users' or specified user's apps to see if they enter an idle state. + * @return Returns whether checking should continue periodically. + */ + boolean checkIdleStates(int checkUserId) { + if (!mAppIdleEnabled) { + return false; + } + + final int[] runningUserIds; + try { + runningUserIds = ActivityManager.getService().getRunningUserIds(); + if (checkUserId != UserHandle.USER_ALL + && !ArrayUtils.contains(runningUserIds, checkUserId)) { + return false; + } + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + + final long elapsedRealtime = SystemClock.elapsedRealtime(); + for (int i = 0; i < runningUserIds.length; i++) { + final int userId = runningUserIds[i]; + if (checkUserId != UserHandle.USER_ALL && checkUserId != userId) { + continue; + } + if (DEBUG) { + Slog.d(TAG, "Checking idle state for user " + userId); + } + List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser( + PackageManager.MATCH_DISABLED_COMPONENTS, + userId); + final int packageCount = packages.size(); + for (int p = 0; p < packageCount; p++) { + final PackageInfo pi = packages.get(p); + final String packageName = pi.packageName; + final boolean isIdle = isAppIdleFiltered(packageName, + UserHandle.getAppId(pi.applicationInfo.uid), + userId, elapsedRealtime); + mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, + userId, isIdle ? 1 : 0, packageName)); + if (isIdle) { + synchronized (mAppIdleLock) { + mAppIdleHistory.setIdle(packageName, userId, elapsedRealtime); + } + } + } + } + if (DEBUG) { + Slog.d(TAG, "checkIdleStates took " + + (SystemClock.elapsedRealtime() - elapsedRealtime)); + } + return true; + } + + /** Check if it's been a while since last parole and let idle apps do some work */ + void checkParoleTimeout() { + boolean setParoled = false; + synchronized (mAppIdleLock) { + final long now = System.currentTimeMillis(); + if (!mAppIdleTempParoled) { + final long timeSinceLastParole = now - mLastAppIdleParoledTime; + if (timeSinceLastParole > mAppIdleParoleIntervalMillis) { + if (DEBUG) Slog.d(TAG, "Crossed default parole interval"); + setParoled = true; + } else { + if (DEBUG) Slog.d(TAG, "Not long enough to go to parole"); + postNextParoleTimeout(now); + } + } + } + if (setParoled) { + setAppIdleParoled(true); + } + } + + private void notifyBatteryStats(String packageName, int userId, boolean idle) { + try { + final int uid = mPackageManager.getPackageUidAsUser(packageName, + PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); + if (idle) { + mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE, + packageName, uid); + } else { + mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE, + packageName, uid); + } + } catch (PackageManager.NameNotFoundException | RemoteException e) { + } + } + + void onDeviceIdleModeChanged() { + final boolean deviceIdle = mPowerManager.isDeviceIdleMode(); + if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle); + boolean paroled = false; + synchronized (mAppIdleLock) { + final long timeSinceLastParole = System.currentTimeMillis() - mLastAppIdleParoledTime; + if (!deviceIdle + && timeSinceLastParole >= mAppIdleParoleIntervalMillis) { + if (DEBUG) { + Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false"); + } + paroled = true; + } else if (deviceIdle) { + if (DEBUG) Slog.i(TAG, "Device idle, back to prison"); + paroled = false; + } else { + return; + } + } + setAppIdleParoled(paroled); + } + + void reportEvent(UsageEvents.Event event, long elapsedRealtime, int userId) { + synchronized (mAppIdleLock) { + // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back + // about apps that are on some kind of whitelist anyway. + final boolean previouslyIdle = mAppIdleHistory.isIdle( + event.mPackage, userId, elapsedRealtime); + // Inform listeners if necessary + if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND + || event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND + || event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION + || event.mEventType == UsageEvents.Event.USER_INTERACTION)) { + mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime); + if (previouslyIdle) { + mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId, + /* idle = */ 0, event.mPackage)); + notifyBatteryStats(event.mPackage, userId, false); + } + } + } + + } + + /** + * Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle, + * then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind + * the threshold for idle. + * + * This method is always called from the handler thread, so not much synchronization is + * required. + */ + void forceIdleState(String packageName, int userId, boolean idle) { + final int appId = getAppId(packageName); + if (appId < 0) return; + final long elapsedRealtime = SystemClock.elapsedRealtime(); + + final boolean previouslyIdle = isAppIdleFiltered(packageName, appId, + userId, elapsedRealtime); + synchronized (mAppIdleLock) { + mAppIdleHistory.setIdle(packageName, userId, idle, elapsedRealtime); + } + final boolean stillIdle = isAppIdleFiltered(packageName, appId, + userId, elapsedRealtime); + // Inform listeners if necessary + if (previouslyIdle != stillIdle) { + mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId, + /* idle = */ stillIdle ? 1 : 0, packageName)); + if (!stillIdle) { + notifyBatteryStats(packageName, userId, idle); + } + } + } + + public void onUserRemoved(int userId) { + synchronized (mAppIdleLock) { + mAppIdleHistory.onUserRemoved(userId); + } + } + + private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) { + synchronized (mAppIdleLock) { + return mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime); + } + } + + void addListener(UsageStatsManagerInternal.AppIdleStateChangeListener listener) { + synchronized (mAppIdleLock) { + if (!mPackageAccessListeners.contains(listener)) { + mPackageAccessListeners.add(listener); + } + } + } + + void removeListener(UsageStatsManagerInternal.AppIdleStateChangeListener listener) { + synchronized (mAppIdleLock) { + mPackageAccessListeners.remove(listener); + } + } + + int getAppId(String packageName) { + try { + ApplicationInfo ai = mPackageManager.getApplicationInfo(packageName, + PackageManager.MATCH_ANY_USER + | PackageManager.MATCH_DISABLED_COMPONENTS); + return ai.uid; + } catch (PackageManager.NameNotFoundException re) { + return -1; + } + } + + boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime, + boolean shouldObfuscateInstantApps) { + if (isParoledOrCharging()) { + return false; + } + if (shouldObfuscateInstantApps && + mPackageManagerInternal.isPackageEphemeral(userId, packageName)) { + return false; + } + return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime); + } + + /** + * Checks if an app has been idle for a while and filters out apps that are excluded. + * It returns false if the current system state allows all apps to be considered active. + * This happens if the device is plugged in or temporarily allowed to make exceptions. + * Called by interface impls. + */ + boolean isAppIdleFiltered(String packageName, int appId, int userId, + long elapsedRealtime) { + if (packageName == null) return false; + // If not enabled at all, of course nobody is ever idle. + if (!mAppIdleEnabled) { + return false; + } + if (appId < Process.FIRST_APPLICATION_UID) { + // System uids never go idle. + return false; + } + if (packageName.equals("android")) { + // Nor does the framework (which should be redundant with the above, but for MR1 we will + // retain this for safety). + return false; + } + if (mSystemServicesReady) { + try { + // We allow all whitelisted apps, including those that don't want to be whitelisted + // for idle mode, because app idle (aka app standby) is really not as big an issue + // for controlling who participates vs. doze mode. + if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) { + return false; + } + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + + if (isActiveDeviceAdmin(packageName, userId)) { + return false; + } + + if (isActiveNetworkScorer(packageName)) { + return false; + } + + if (mAppWidgetManager != null + && mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) { + return false; + } + + if (isDeviceProvisioningPackage(packageName)) { + return false; + } + } + + if (!isAppIdleUnfiltered(packageName, userId, elapsedRealtime)) { + return false; + } + + // Check this last, as it is the most expensive check + // TODO: Optimize this by fetching the carrier privileged apps ahead of time + if (isCarrierApp(packageName)) { + return false; + } + + return true; + } + + int[] getIdleUidsForUser(int userId) { + if (!mAppIdleEnabled) { + return new int[0]; + } + + final long elapsedRealtime = SystemClock.elapsedRealtime(); + + List<ApplicationInfo> apps; + try { + ParceledListSlice<ApplicationInfo> slice = AppGlobals.getPackageManager() + .getInstalledApplications(/* flags= */ 0, userId); + if (slice == null) { + return new int[0]; + } + apps = slice.getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + // State of each uid. Key is the uid. Value lower 16 bits is the number of apps + // associated with that uid, upper 16 bits is the number of those apps that is idle. + SparseIntArray uidStates = new SparseIntArray(); + + // Now resolve all app state. Iterating over all apps, keeping track of how many + // we find for each uid and how many of those are idle. + for (int i = apps.size() - 1; i >= 0; i--) { + ApplicationInfo ai = apps.get(i); + + // Check whether this app is idle. + boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid), + userId, elapsedRealtime); + + int index = uidStates.indexOfKey(ai.uid); + if (index < 0) { + uidStates.put(ai.uid, 1 + (idle ? 1<<16 : 0)); + } else { + int value = uidStates.valueAt(index); + uidStates.setValueAt(index, value + 1 + (idle ? 1<<16 : 0)); + } + } + if (DEBUG) { + Slog.d(TAG, "getIdleUids took " + (SystemClock.elapsedRealtime() - elapsedRealtime)); + } + int numIdle = 0; + for (int i = uidStates.size() - 1; i >= 0; i--) { + int value = uidStates.valueAt(i); + if ((value&0x7fff) == (value>>16)) { + numIdle++; + } + } + + int[] res = new int[numIdle]; + numIdle = 0; + for (int i = uidStates.size() - 1; i >= 0; i--) { + int value = uidStates.valueAt(i); + if ((value&0x7fff) == (value>>16)) { + res[numIdle] = uidStates.keyAt(i); + numIdle++; + } + } + + return res; + } + + void setAppIdleAsync(String packageName, boolean idle, int userId) { + if (packageName == null) return; + + mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName) + .sendToTarget(); + } + + private boolean isActiveDeviceAdmin(String packageName, int userId) { + DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); + if (dpm == null) return false; + return dpm.packageHasActiveAdmins(packageName, userId); + } + + /** + * Returns {@code true} if the supplied package is the device provisioning app. Otherwise, + * returns {@code false}. + */ + private boolean isDeviceProvisioningPackage(String packageName) { + String deviceProvisioningPackage = mContext.getResources().getString( + com.android.internal.R.string.config_deviceProvisioningPackage); + return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName); + } + + private boolean isCarrierApp(String packageName) { + synchronized (mAppIdleLock) { + if (!mHaveCarrierPrivilegedApps) { + fetchCarrierPrivilegedAppsLA(); + } + if (mCarrierPrivilegedApps != null) { + return mCarrierPrivilegedApps.contains(packageName); + } + return false; + } + } + + void clearCarrierPrivilegedApps() { + if (DEBUG) { + Slog.i(TAG, "Clearing carrier privileged apps list"); + } + synchronized (mAppIdleLock) { + mHaveCarrierPrivilegedApps = false; + mCarrierPrivilegedApps = null; // Need to be refetched. + } + } + + @GuardedBy("mAppIdleLock") + private void fetchCarrierPrivilegedAppsLA() { + TelephonyManager telephonyManager = + mContext.getSystemService(TelephonyManager.class); + mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivileges(); + mHaveCarrierPrivilegedApps = true; + if (DEBUG) { + Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps); + } + } + + private boolean isActiveNetworkScorer(String packageName) { + NetworkScoreManager nsm = (NetworkScoreManager) mContext.getSystemService( + Context.NETWORK_SCORE_SERVICE); + return packageName != null && packageName.equals(nsm.getActiveScorerPackage()); + } + + void informListeners(String packageName, int userId, boolean isIdle) { + for (UsageStatsManagerInternal.AppIdleStateChangeListener listener : mPackageAccessListeners) { + listener.onAppIdleStateChanged(packageName, userId, isIdle); + } + } + + void informParoleStateChanged() { + final boolean paroled = isParoledOrCharging(); + for (UsageStatsManagerInternal.AppIdleStateChangeListener listener : mPackageAccessListeners) { + listener.onParoleStateChanged(paroled); + } + } + + void flushToDisk(int userId) { + synchronized (mAppIdleLock) { + mAppIdleHistory.writeAppIdleTimes(userId); + } + } + + void flushDurationsToDisk() { + // Persist elapsed and screen on time. If this fails for whatever reason, the apps will be + // considered not-idle, which is the safest outcome in such an event. + synchronized (mAppIdleLock) { + mAppIdleHistory.writeAppIdleDurations(); + } + } + + boolean isDisplayOn() { + return mDisplayManager + .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON; + } + + void clearAppIdleForPackage(String packageName, int userId) { + synchronized (mAppIdleLock) { + mAppIdleHistory.clearUsage(packageName, userId); + } + } + + private class PackageReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (Intent.ACTION_PACKAGE_ADDED.equals(action) + || Intent.ACTION_PACKAGE_CHANGED.equals(action)) { + clearCarrierPrivilegedApps(); + } + if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) || + Intent.ACTION_PACKAGE_ADDED.equals(action)) + && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + clearAppIdleForPackage(intent.getData().getSchemeSpecificPart(), + getSendingUserId()); + } + } + } + + void initializeDefaultsForSystemApps(int userId) { + Slog.d(TAG, "Initializing defaults for system apps on user " + userId); + final long elapsedRealtime = SystemClock.elapsedRealtime(); + List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser( + PackageManager.MATCH_DISABLED_COMPONENTS, + userId); + final int packageCount = packages.size(); + synchronized (mAppIdleLock) { + for (int i = 0; i < packageCount; i++) { + final PackageInfo pi = packages.get(i); + String packageName = pi.packageName; + if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) { + mAppIdleHistory.reportUsage(packageName, userId, elapsedRealtime); + } + } + } + } + + void postReportContentProviderUsage(String name, String packageName, int userId) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = name; + args.arg2 = packageName; + args.arg3 = userId; + mHandler.obtainMessage(MSG_REPORT_CONTENT_PROVIDER_USAGE, args) + .sendToTarget(); + } + + void dumpHistory(IndentingPrintWriter idpw, int userId) { + synchronized (mAppIdleLock) { + mAppIdleHistory.dumpHistory(idpw, userId); + } + } + + void dumpUser(IndentingPrintWriter idpw, int userId) { + synchronized (mAppIdleLock) { + mAppIdleHistory.dump(idpw, userId); + } + } + + void dumpState(String[] args, PrintWriter pw) { + synchronized (mAppIdleLock) { + pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps + + "): " + mCarrierPrivilegedApps); + } + + pw.println(); + pw.println("Settings:"); + + pw.print(" mAppIdleDurationMillis="); + TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw); + pw.println(); + + pw.print(" mAppIdleWallclockThresholdMillis="); + TimeUtils.formatDuration(mAppIdleWallclockThresholdMillis, pw); + pw.println(); + + pw.print(" mCheckIdleIntervalMillis="); + TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw); + pw.println(); + + pw.print(" mAppIdleParoleIntervalMillis="); + TimeUtils.formatDuration(mAppIdleParoleIntervalMillis, pw); + pw.println(); + + pw.print(" mAppIdleParoleDurationMillis="); + TimeUtils.formatDuration(mAppIdleParoleDurationMillis, pw); + pw.println(); + + pw.println(); + pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled); + pw.print(" mAppIdleTempParoled="); pw.print(mAppIdleTempParoled); + pw.print(" mCharging="); pw.print(mCharging); + pw.print(" mLastAppIdleParoledTime="); + TimeUtils.formatDuration(mLastAppIdleParoledTime, pw); + pw.println(); + } + + class AppStandbyHandler extends Handler { + + AppStandbyHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_FORCE_IDLE_STATE: + forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1); + break; + + case MSG_CHECK_IDLE_STATES: + if (checkIdleStates(msg.arg1)) { + mHandler.sendMessageDelayed(mHandler.obtainMessage( + MSG_CHECK_IDLE_STATES, msg.arg1, 0), + mCheckIdleIntervalMillis); + } + break; + + case MSG_ONE_TIME_CHECK_IDLE_STATES: + mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES); + checkIdleStates(UserHandle.USER_ALL); + break; + + case MSG_CHECK_PAROLE_TIMEOUT: + checkParoleTimeout(); + break; + + case MSG_PAROLE_END_TIMEOUT: + if (DEBUG) Slog.d(TAG, "Ending parole"); + setAppIdleParoled(false); + break; + + case MSG_REPORT_CONTENT_PROVIDER_USAGE: + SomeArgs args = (SomeArgs) msg.obj; + reportContentProviderUsage((String) args.arg1, // authority name + (String) args.arg2, // package name + (int) args.arg3); // userId + args.recycle(); + break; + + case MSG_PAROLE_STATE_CHANGED: + if (DEBUG) Slog.d(TAG, "Parole state: " + mAppIdleTempParoled + + ", Charging state:" + mCharging); + informParoleStateChanged(); + break; + default: + super.handleMessage(msg); + break; + + } + } + }; + + private class DeviceStateReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { + setChargingState(intent.getIntExtra("plugged", 0) != 0); + } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) { + onDeviceIdleModeChanged(); + } + } + } + + private final DisplayManager.DisplayListener mDisplayListener + = new DisplayManager.DisplayListener() { + + @Override public void onDisplayAdded(int displayId) { + } + + @Override public void onDisplayRemoved(int displayId) { + } + + @Override public void onDisplayChanged(int displayId) { + if (displayId == Display.DEFAULT_DISPLAY) { + final boolean displayOn = isDisplayOn(); + synchronized (mAppIdleLock) { + mAppIdleHistory.updateDisplay(displayOn, SystemClock.elapsedRealtime()); + } + } + } + }; + + /** + * Observe settings changes for {@link Settings.Global#APP_IDLE_CONSTANTS}. + */ + private class SettingsObserver extends ContentObserver { + /** + * This flag has been used to disable app idle on older builds with bug b/26355386. + */ + @Deprecated + private static final String KEY_IDLE_DURATION_OLD = "idle_duration"; + + private static final String KEY_IDLE_DURATION = "idle_duration2"; + private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold"; + private static final String KEY_PAROLE_INTERVAL = "parole_interval"; + private static final String KEY_PAROLE_DURATION = "parole_duration"; + + private final KeyValueListParser mParser = new KeyValueListParser(','); + + SettingsObserver(Handler handler) { + super(handler); + } + + void registerObserver() { + mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor( + Settings.Global.APP_IDLE_CONSTANTS), false, this); + } + + @Override + public void onChange(boolean selfChange) { + updateSettings(); + postOneTimeCheckIdleStates(); + } + + void updateSettings() { + synchronized (mAppIdleLock) { + // Look at global settings for this. + // TODO: Maybe apply different thresholds for different users. + try { + mParser.setString(Settings.Global.getString(mContext.getContentResolver(), + Settings.Global.APP_IDLE_CONSTANTS)); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage()); + // fallthrough, mParser is empty and all defaults will be returned. + } + + // Default: 12 hours of screen-on time sans dream-time + mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION, + COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE); + + mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD, + COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days + + mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4, + COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE); // 8 hours + + // Default: 24 hours between paroles + mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL, + COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE); + + mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION, + COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes + mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis, + mAppIdleScreenThresholdMillis); + } + } + } + +} + diff --git a/com/android/server/usage/UsageStatsService.java b/com/android/server/usage/UsageStatsService.java index 25e471cb..afafea19 100644 --- a/com/android/server/usage/UsageStatsService.java +++ b/com/android/server/usage/UsageStatsService.java @@ -18,37 +18,24 @@ package com.android.server.usage; import android.Manifest; import android.app.ActivityManager; -import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.IUidObserver; -import android.app.admin.DevicePolicyManager; import android.app.usage.ConfigurationStats; import android.app.usage.IUsageStatsManager; import android.app.usage.UsageEvents; import android.app.usage.UsageEvents.Event; import android.app.usage.UsageStats; import android.app.usage.UsageStatsManagerInternal; -import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener; -import android.appwidget.AppWidgetManager; import android.content.BroadcastReceiver; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.content.res.Configuration; -import android.database.ContentObserver; -import android.hardware.display.DisplayManager; -import android.net.NetworkScoreManager; -import android.os.BatteryManager; -import android.os.BatteryStats; import android.os.Binder; import android.os.Environment; import android.os.FileUtils; @@ -56,7 +43,6 @@ import android.os.Handler; import android.os.IDeviceIdleController; import android.os.Looper; import android.os.Message; -import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -64,21 +50,12 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; -import android.provider.Settings; -import android.telephony.TelephonyManager; import android.util.ArraySet; -import android.util.KeyValueListParser; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; -import android.util.TimeUtils; -import android.view.Display; -import com.android.internal.annotations.GuardedBy; -import com.android.internal.app.IBatteryStats; import com.android.internal.os.BackgroundThread; -import com.android.internal.os.SomeArgs; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; @@ -88,7 +65,6 @@ import java.io.File; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -107,7 +83,6 @@ public class UsageStatsService extends SystemService implements static final boolean COMPRESS_TIME = false; private static final long TEN_SECONDS = 10 * 1000; - private static final long ONE_MINUTE = 60 * 1000; private static final long TWENTY_MINUTES = 20 * 60 * 1000; private static final long FLUSH_INTERVAL = COMPRESS_TIME ? TEN_SECONDS : TWENTY_MINUTES; private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds. @@ -115,24 +90,10 @@ public class UsageStatsService extends SystemService implements private static final boolean ENABLE_KERNEL_UPDATES = true; private static final File KERNEL_COUNTER_FILE = new File("/proc/uid_procstat/set"); - long mAppIdleScreenThresholdMillis; - long mCheckIdleIntervalMillis; - long mAppIdleWallclockThresholdMillis; - long mAppIdleParoleIntervalMillis; - long mAppIdleParoleDurationMillis; - // Handler message types. static final int MSG_REPORT_EVENT = 0; static final int MSG_FLUSH_TO_DISK = 1; static final int MSG_REMOVE_USER = 2; - static final int MSG_INFORM_LISTENERS = 3; - static final int MSG_FORCE_IDLE_STATE = 4; - static final int MSG_CHECK_IDLE_STATES = 5; - static final int MSG_CHECK_PAROLE_TIMEOUT = 6; - static final int MSG_PAROLE_END_TIMEOUT = 7; - static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8; - static final int MSG_PAROLE_STATE_CHANGED = 9; - static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10; private final Object mLock = new Object(); Handler mHandler; @@ -140,11 +101,7 @@ public class UsageStatsService extends SystemService implements UserManager mUserManager; PackageManager mPackageManager; PackageManagerInternal mPackageManagerInternal; - AppWidgetManager mAppWidgetManager; IDeviceIdleController mDeviceIdleController; - private DisplayManager mDisplayManager; - private PowerManager mPowerManager; - private IBatteryStats mBatteryStats; private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>(); private final SparseIntArray mUidToKernelCounter = new SparseIntArray(); @@ -152,26 +109,7 @@ public class UsageStatsService extends SystemService implements long mRealTimeSnapshot; long mSystemTimeSnapshot; - boolean mAppIdleEnabled; - boolean mAppIdleTempParoled; - boolean mCharging; - private long mLastAppIdleParoledTime; - - private volatile boolean mPendingOneTimeCheckIdleStates; - private boolean mSystemServicesReady = false; - - private final Object mAppIdleLock = new Object(); - @GuardedBy("mAppIdleLock") - private AppIdleHistory mAppIdleHistory; - - @GuardedBy("mAppIdleLock") - private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener> - mPackageAccessListeners = new ArrayList<>(); - - @GuardedBy("mAppIdleLock") - private boolean mHaveCarrierPrivilegedApps; - @GuardedBy("mAppIdleLock") - private List<String> mCarrierPrivilegedApps; + AppStandbyController mAppStandby; public UsageStatsService(Context context) { super(context); @@ -185,6 +123,8 @@ public class UsageStatsService extends SystemService implements mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mHandler = new H(BackgroundThread.get().getLooper()); + mAppStandby = new AppStandbyController(getContext(), BackgroundThread.get().getLooper()); + File systemDataDir = new File(Environment.getDataDirectory(), "system"); mUsageStatsDir = new File(systemDataDir, "usagestats"); mUsageStatsDir.mkdirs(); @@ -198,30 +138,9 @@ public class UsageStatsService extends SystemService implements getContext().registerReceiverAsUser(new UserActionsReceiver(), UserHandle.ALL, filter, null, mHandler); - IntentFilter packageFilter = new IntentFilter(); - packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); - packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); - packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); - packageFilter.addDataScheme("package"); - - getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, packageFilter, - null, mHandler); - - mAppIdleEnabled = getContext().getResources().getBoolean( - com.android.internal.R.bool.config_enableAutoPowerModes); - if (mAppIdleEnabled) { - IntentFilter deviceStates = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); - deviceStates.addAction(BatteryManager.ACTION_DISCHARGING); - deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED); - getContext().registerReceiver(new DeviceStateReceiver(), deviceStates); - } - synchronized (mLock) { cleanUpRemovedUsersLocked(); } - synchronized (mAppIdleLock) { - mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime()); - } mRealTimeSnapshot = SystemClock.elapsedRealtime(); mSystemTimeSnapshot = System.currentTimeMillis(); @@ -233,28 +152,10 @@ public class UsageStatsService extends SystemService implements @Override public void onBootPhase(int phase) { if (phase == PHASE_SYSTEM_SERVICES_READY) { - // Observe changes to the threshold - SettingsObserver settingsObserver = new SettingsObserver(mHandler); - settingsObserver.registerObserver(); - settingsObserver.updateSettings(); + mAppStandby.onBootPhase(phase); - mAppWidgetManager = getContext().getSystemService(AppWidgetManager.class); mDeviceIdleController = IDeviceIdleController.Stub.asInterface( ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)); - mBatteryStats = IBatteryStats.Stub.asInterface( - ServiceManager.getService(BatteryStats.SERVICE_NAME)); - mDisplayManager = (DisplayManager) getContext().getSystemService( - Context.DISPLAY_SERVICE); - mPowerManager = getContext().getSystemService(PowerManager.class); - - mDisplayManager.registerDisplayListener(mDisplayListener, mHandler); - synchronized (mAppIdleLock) { - mAppIdleHistory.updateDisplay(isDisplayOn(), SystemClock.elapsedRealtime()); - } - - if (mPendingOneTimeCheckIdleStates) { - postOneTimeCheckIdleStates(); - } if (ENABLE_KERNEL_UPDATES && KERNEL_COUNTER_FILE.exists()) { try { @@ -268,18 +169,9 @@ public class UsageStatsService extends SystemService implements } else { Slog.w(TAG, "Missing procfs interface: " + KERNEL_COUNTER_FILE); } - - mSystemServicesReady = true; - } else if (phase == PHASE_BOOT_COMPLETED) { - setChargingState(getContext().getSystemService(BatteryManager.class).isCharging()); } } - private boolean isDisplayOn() { - return mDisplayManager - .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON; - } - private class UserActionsReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { @@ -291,60 +183,12 @@ public class UsageStatsService extends SystemService implements } } else if (Intent.ACTION_USER_STARTED.equals(action)) { if (userId >=0) { - postCheckIdleStates(userId); + mAppStandby.postCheckIdleStates(userId); } } } } - private class PackageReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (Intent.ACTION_PACKAGE_ADDED.equals(action) - || Intent.ACTION_PACKAGE_CHANGED.equals(action)) { - clearCarrierPrivilegedApps(); - } - if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) || - Intent.ACTION_PACKAGE_ADDED.equals(action)) - && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { - clearAppIdleForPackage(intent.getData().getSchemeSpecificPart(), - getSendingUserId()); - } - } - } - - private class DeviceStateReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { - setChargingState(intent.getIntExtra("plugged", 0) != 0); - } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) { - onDeviceIdleModeChanged(); - } - } - } - - private final DisplayManager.DisplayListener mDisplayListener - = new DisplayManager.DisplayListener() { - - @Override public void onDisplayAdded(int displayId) { - } - - @Override public void onDisplayRemoved(int displayId) { - } - - @Override public void onDisplayChanged(int displayId) { - if (displayId == Display.DEFAULT_DISPLAY) { - final boolean displayOn = isDisplayOn(); - synchronized (UsageStatsService.this.mAppIdleLock) { - mAppIdleHistory.updateDisplay(displayOn, SystemClock.elapsedRealtime()); - } - } - } - }; - private final IUidObserver mUidObserver = new IUidObserver.Stub() { @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) { @@ -388,42 +232,18 @@ public class UsageStatsService extends SystemService implements @Override public void onStatsReloaded() { - postOneTimeCheckIdleStates(); + mAppStandby.postOneTimeCheckIdleStates(); } @Override public void onNewUpdate(int userId) { - initializeDefaultsForSystemApps(userId); - } - - private void initializeDefaultsForSystemApps(int userId) { - Slog.d(TAG, "Initializing defaults for system apps on user " + userId); - final long elapsedRealtime = SystemClock.elapsedRealtime(); - List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser( - PackageManager.MATCH_DISABLED_COMPONENTS, - userId); - final int packageCount = packages.size(); - synchronized (mAppIdleLock) { - for (int i = 0; i < packageCount; i++) { - final PackageInfo pi = packages.get(i); - String packageName = pi.packageName; - if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) { - mAppIdleHistory.reportUsage(packageName, userId, elapsedRealtime); - } - } - } + mAppStandby.initializeDefaultsForSystemApps(userId); } private boolean shouldObfuscateInstantAppsForCaller(int callingUid, int userId) { return !mPackageManagerInternal.canAccessInstantApps(callingUid, userId); } - void clearAppIdleForPackage(String packageName, int userId) { - synchronized (mAppIdleLock) { - mAppIdleHistory.clearUsage(packageName, userId); - } - } - private void cleanUpRemovedUsersLocked() { final List<UserInfo> users = mUserManager.getUsers(true); if (users == null || users.size() == 0) { @@ -451,195 +271,6 @@ public class UsageStatsService extends SystemService implements } } - void setChargingState(boolean charging) { - synchronized (mAppIdleLock) { - if (mCharging != charging) { - mCharging = charging; - postParoleStateChanged(); - } - } - } - - /** Paroled here means temporary pardon from being inactive */ - void setAppIdleParoled(boolean paroled) { - synchronized (mAppIdleLock) { - final long now = System.currentTimeMillis(); - if (mAppIdleTempParoled != paroled) { - mAppIdleTempParoled = paroled; - if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleTempParoled); - if (paroled) { - postParoleEndTimeout(); - } else { - mLastAppIdleParoledTime = now; - postNextParoleTimeout(now); - } - postParoleStateChanged(); - } - } - } - - boolean isParoledOrCharging() { - synchronized (mAppIdleLock) { - return mAppIdleTempParoled || mCharging; - } - } - - private void postNextParoleTimeout(long now) { - if (DEBUG) Slog.d(TAG, "Posting MSG_CHECK_PAROLE_TIMEOUT"); - mHandler.removeMessages(MSG_CHECK_PAROLE_TIMEOUT); - // Compute when the next parole needs to happen. We check more frequently than necessary - // since the message handler delays are based on elapsedRealTime and not wallclock time. - // The comparison is done in wallclock time. - long timeLeft = (mLastAppIdleParoledTime + mAppIdleParoleIntervalMillis) - now; - if (timeLeft < 0) { - timeLeft = 0; - } - mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft); - } - - private void postParoleEndTimeout() { - if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_END_TIMEOUT"); - mHandler.removeMessages(MSG_PAROLE_END_TIMEOUT); - mHandler.sendEmptyMessageDelayed(MSG_PAROLE_END_TIMEOUT, mAppIdleParoleDurationMillis); - } - - private void postParoleStateChanged() { - if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_STATE_CHANGED"); - mHandler.removeMessages(MSG_PAROLE_STATE_CHANGED); - mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED); - } - - void postCheckIdleStates(int userId) { - mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0)); - } - - /** - * We send a different message to check idle states once, otherwise we would end up - * scheduling a series of repeating checkIdleStates each time we fired off one. - */ - void postOneTimeCheckIdleStates() { - if (mDeviceIdleController == null) { - // Not booted yet; wait for it! - mPendingOneTimeCheckIdleStates = true; - } else { - mHandler.sendEmptyMessage(MSG_ONE_TIME_CHECK_IDLE_STATES); - mPendingOneTimeCheckIdleStates = false; - } - } - - /** - * Check all running users' or specified user's apps to see if they enter an idle state. - * @return Returns whether checking should continue periodically. - */ - boolean checkIdleStates(int checkUserId) { - if (!mAppIdleEnabled) { - return false; - } - - final int[] runningUserIds; - try { - runningUserIds = ActivityManager.getService().getRunningUserIds(); - if (checkUserId != UserHandle.USER_ALL - && !ArrayUtils.contains(runningUserIds, checkUserId)) { - return false; - } - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - - final long elapsedRealtime = SystemClock.elapsedRealtime(); - for (int i = 0; i < runningUserIds.length; i++) { - final int userId = runningUserIds[i]; - if (checkUserId != UserHandle.USER_ALL && checkUserId != userId) { - continue; - } - if (DEBUG) { - Slog.d(TAG, "Checking idle state for user " + userId); - } - List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser( - PackageManager.MATCH_DISABLED_COMPONENTS, - userId); - final int packageCount = packages.size(); - for (int p = 0; p < packageCount; p++) { - final PackageInfo pi = packages.get(p); - final String packageName = pi.packageName; - final boolean isIdle = isAppIdleFiltered(packageName, - UserHandle.getAppId(pi.applicationInfo.uid), - userId, elapsedRealtime); - mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, - userId, isIdle ? 1 : 0, packageName)); - if (isIdle) { - synchronized (mAppIdleLock) { - mAppIdleHistory.setIdle(packageName, userId, elapsedRealtime); - } - } - } - } - if (DEBUG) { - Slog.d(TAG, "checkIdleStates took " - + (SystemClock.elapsedRealtime() - elapsedRealtime)); - } - return true; - } - - /** Check if it's been a while since last parole and let idle apps do some work */ - void checkParoleTimeout() { - boolean setParoled = false; - synchronized (mAppIdleLock) { - final long now = System.currentTimeMillis(); - if (!mAppIdleTempParoled) { - final long timeSinceLastParole = now - mLastAppIdleParoledTime; - if (timeSinceLastParole > mAppIdleParoleIntervalMillis) { - if (DEBUG) Slog.d(TAG, "Crossed default parole interval"); - setParoled = true; - } else { - if (DEBUG) Slog.d(TAG, "Not long enough to go to parole"); - postNextParoleTimeout(now); - } - } - } - if (setParoled) { - setAppIdleParoled(true); - } - } - - private void notifyBatteryStats(String packageName, int userId, boolean idle) { - try { - final int uid = mPackageManager.getPackageUidAsUser(packageName, - PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); - if (idle) { - mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE, - packageName, uid); - } else { - mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE, - packageName, uid); - } - } catch (NameNotFoundException | RemoteException e) { - } - } - - void onDeviceIdleModeChanged() { - final boolean deviceIdle = mPowerManager.isDeviceIdleMode(); - if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle); - boolean paroled = false; - synchronized (mAppIdleLock) { - final long timeSinceLastParole = System.currentTimeMillis() - mLastAppIdleParoledTime; - if (!deviceIdle - && timeSinceLastParole >= mAppIdleParoleIntervalMillis) { - if (DEBUG) { - Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false"); - } - paroled = true; - } else if (deviceIdle) { - if (DEBUG) Slog.i(TAG, "Device idle, back to prison"); - paroled = false; - } else { - return; - } - } - setAppIdleParoled(paroled); - } - private static void deleteRecursively(File f) { File[] files = f.listFiles(); if (files != null) { @@ -724,76 +355,7 @@ public class UsageStatsService extends SystemService implements getUserDataAndInitializeIfNeededLocked(userId, timeNow); service.reportEvent(event); - synchronized (mAppIdleLock) { - // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back - // about apps that are on some kind of whitelist anyway. - final boolean previouslyIdle = mAppIdleHistory.isIdle( - event.mPackage, userId, elapsedRealtime); - // Inform listeners if necessary - if ((event.mEventType == Event.MOVE_TO_FOREGROUND - || event.mEventType == Event.MOVE_TO_BACKGROUND - || event.mEventType == Event.SYSTEM_INTERACTION - || event.mEventType == Event.USER_INTERACTION)) { - mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime); - if (previouslyIdle) { - mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId, - /* idle = */ 0, event.mPackage)); - notifyBatteryStats(event.mPackage, userId, false); - } - } - } - } - } - - void reportContentProviderUsage(String authority, String providerPkgName, int userId) { - // Get sync adapters for the authority - String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser( - authority, userId); - for (String packageName: packages) { - // Only force the sync adapters to active if the provider is not in the same package and - // the sync adapter is a system package. - try { - PackageInfo pi = mPackageManager.getPackageInfoAsUser( - packageName, PackageManager.MATCH_SYSTEM_ONLY, userId); - if (pi == null || pi.applicationInfo == null) { - continue; - } - if (!packageName.equals(providerPkgName)) { - setAppIdleAsync(packageName, false, userId); - } - } catch (NameNotFoundException e) { - // Shouldn't happen - } - } - } - - /** - * Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle, - * then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind - * the threshold for idle. - * - * This method is always called from the handler thread, so not much synchronization is - * required. - */ - void forceIdleState(String packageName, int userId, boolean idle) { - final int appId = getAppId(packageName); - if (appId < 0) return; - final long elapsedRealtime = SystemClock.elapsedRealtime(); - - final boolean previouslyIdle = isAppIdleFiltered(packageName, appId, - userId, elapsedRealtime); - synchronized (mAppIdleLock) { - mAppIdleHistory.setIdle(packageName, userId, idle, elapsedRealtime); - } - final boolean stillIdle = isAppIdleFiltered(packageName, appId, - userId, elapsedRealtime); - // Inform listeners if necessary - if (previouslyIdle != stillIdle) { - mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId, - /* idle = */ stillIdle ? 1 : 0, packageName)); - if (!stillIdle) { - notifyBatteryStats(packageName, userId, idle); - } + mAppStandby.reportEvent(event, elapsedRealtime, userId); } } @@ -813,9 +375,7 @@ public class UsageStatsService extends SystemService implements synchronized (mLock) { Slog.i(TAG, "Removing user " + userId + " and all data."); mUserState.remove(userId); - synchronized (mAppIdleLock) { - mAppIdleHistory.onUserRemoved(userId); - } + mAppStandby.onUserRemoved(userId); cleanUpRemovedUsersLocked(); } } @@ -887,253 +447,6 @@ public class UsageStatsService extends SystemService implements } } - private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) { - synchronized (mAppIdleLock) { - return mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime); - } - } - - void addListener(AppIdleStateChangeListener listener) { - synchronized (mAppIdleLock) { - if (!mPackageAccessListeners.contains(listener)) { - mPackageAccessListeners.add(listener); - } - } - } - - void removeListener(AppIdleStateChangeListener listener) { - synchronized (mAppIdleLock) { - mPackageAccessListeners.remove(listener); - } - } - - int getAppId(String packageName) { - try { - ApplicationInfo ai = mPackageManager.getApplicationInfo(packageName, - PackageManager.MATCH_ANY_USER - | PackageManager.MATCH_DISABLED_COMPONENTS); - return ai.uid; - } catch (NameNotFoundException re) { - return -1; - } - } - - boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime, - boolean shouldObfuscateInstantApps) { - if (isParoledOrCharging()) { - return false; - } - if (shouldObfuscateInstantApps && - mPackageManagerInternal.isPackageEphemeral(userId, packageName)) { - return false; - } - return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime); - } - - /** - * Checks if an app has been idle for a while and filters out apps that are excluded. - * It returns false if the current system state allows all apps to be considered active. - * This happens if the device is plugged in or temporarily allowed to make exceptions. - * Called by interface impls. - */ - private boolean isAppIdleFiltered(String packageName, int appId, int userId, - long elapsedRealtime) { - if (packageName == null) return false; - // If not enabled at all, of course nobody is ever idle. - if (!mAppIdleEnabled) { - return false; - } - if (appId < Process.FIRST_APPLICATION_UID) { - // System uids never go idle. - return false; - } - if (packageName.equals("android")) { - // Nor does the framework (which should be redundant with the above, but for MR1 we will - // retain this for safety). - return false; - } - if (mSystemServicesReady) { - try { - // We allow all whitelisted apps, including those that don't want to be whitelisted - // for idle mode, because app idle (aka app standby) is really not as big an issue - // for controlling who participates vs. doze mode. - if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) { - return false; - } - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - - if (isActiveDeviceAdmin(packageName, userId)) { - return false; - } - - if (isActiveNetworkScorer(packageName)) { - return false; - } - - if (mAppWidgetManager != null - && mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) { - return false; - } - - if (isDeviceProvisioningPackage(packageName)) { - return false; - } - } - - if (!isAppIdleUnfiltered(packageName, userId, elapsedRealtime)) { - return false; - } - - // Check this last, as it is the most expensive check - // TODO: Optimize this by fetching the carrier privileged apps ahead of time - if (isCarrierApp(packageName)) { - return false; - } - - return true; - } - - int[] getIdleUidsForUser(int userId) { - if (!mAppIdleEnabled) { - return new int[0]; - } - - final long elapsedRealtime = SystemClock.elapsedRealtime(); - - List<ApplicationInfo> apps; - try { - ParceledListSlice<ApplicationInfo> slice = AppGlobals.getPackageManager() - .getInstalledApplications(/* flags= */ 0, userId); - if (slice == null) { - return new int[0]; - } - apps = slice.getList(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - - // State of each uid. Key is the uid. Value lower 16 bits is the number of apps - // associated with that uid, upper 16 bits is the number of those apps that is idle. - SparseIntArray uidStates = new SparseIntArray(); - - // Now resolve all app state. Iterating over all apps, keeping track of how many - // we find for each uid and how many of those are idle. - for (int i = apps.size() - 1; i >= 0; i--) { - ApplicationInfo ai = apps.get(i); - - // Check whether this app is idle. - boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid), - userId, elapsedRealtime); - - int index = uidStates.indexOfKey(ai.uid); - if (index < 0) { - uidStates.put(ai.uid, 1 + (idle ? 1<<16 : 0)); - } else { - int value = uidStates.valueAt(index); - uidStates.setValueAt(index, value + 1 + (idle ? 1<<16 : 0)); - } - } - if (DEBUG) { - Slog.d(TAG, "getIdleUids took " + (SystemClock.elapsedRealtime() - elapsedRealtime)); - } - int numIdle = 0; - for (int i = uidStates.size() - 1; i >= 0; i--) { - int value = uidStates.valueAt(i); - if ((value&0x7fff) == (value>>16)) { - numIdle++; - } - } - - int[] res = new int[numIdle]; - numIdle = 0; - for (int i = uidStates.size() - 1; i >= 0; i--) { - int value = uidStates.valueAt(i); - if ((value&0x7fff) == (value>>16)) { - res[numIdle] = uidStates.keyAt(i); - numIdle++; - } - } - - return res; - } - - void setAppIdleAsync(String packageName, boolean idle, int userId) { - if (packageName == null) return; - - mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName) - .sendToTarget(); - } - - private boolean isActiveDeviceAdmin(String packageName, int userId) { - DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class); - if (dpm == null) return false; - return dpm.packageHasActiveAdmins(packageName, userId); - } - - /** - * Returns {@code true} if the supplied package is the device provisioning app. Otherwise, - * returns {@code false}. - */ - private boolean isDeviceProvisioningPackage(String packageName) { - String deviceProvisioningPackage = getContext().getResources().getString( - com.android.internal.R.string.config_deviceProvisioningPackage); - return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName); - } - - private boolean isCarrierApp(String packageName) { - synchronized (mAppIdleLock) { - if (!mHaveCarrierPrivilegedApps) { - fetchCarrierPrivilegedAppsLA(); - } - if (mCarrierPrivilegedApps != null) { - return mCarrierPrivilegedApps.contains(packageName); - } - return false; - } - } - - void clearCarrierPrivilegedApps() { - if (DEBUG) { - Slog.i(TAG, "Clearing carrier privileged apps list"); - } - synchronized (mAppIdleLock) { - mHaveCarrierPrivilegedApps = false; - mCarrierPrivilegedApps = null; // Need to be refetched. - } - } - - @GuardedBy("mAppIdleLock") - private void fetchCarrierPrivilegedAppsLA() { - TelephonyManager telephonyManager = - getContext().getSystemService(TelephonyManager.class); - mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivileges(); - mHaveCarrierPrivilegedApps = true; - if (DEBUG) { - Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps); - } - } - - private boolean isActiveNetworkScorer(String packageName) { - NetworkScoreManager nsm = (NetworkScoreManager) getContext().getSystemService( - Context.NETWORK_SCORE_SERVICE); - return packageName != null && packageName.equals(nsm.getActiveScorerPackage()); - } - - void informListeners(String packageName, int userId, boolean isIdle) { - for (AppIdleStateChangeListener listener : mPackageAccessListeners) { - listener.onAppIdleStateChanged(packageName, userId, isIdle); - } - } - - void informParoleStateChanged() { - final boolean paroled = isParoledOrCharging(); - for (AppIdleStateChangeListener listener : mPackageAccessListeners) { - listener.onParoleStateChanged(paroled); - } - } - private static boolean validRange(long currentTime, long beginTime, long endTime) { return beginTime <= currentTime && beginTime < endTime; } @@ -1143,15 +456,10 @@ public class UsageStatsService extends SystemService implements for (int i = 0; i < userCount; i++) { UserUsageStatsService service = mUserState.valueAt(i); service.persistActiveStats(); - synchronized (mAppIdleLock) { - mAppIdleHistory.writeAppIdleTimes(mUserState.keyAt(i)); - } - } - // Persist elapsed and screen on time. If this fails for whatever reason, the apps will be - // considered not-idle, which is the safest outcome in such an event. - synchronized (mAppIdleLock) { - mAppIdleHistory.writeAppIdleDurations(); + mAppStandby.flushToDisk(mUserState.keyAt(i)); } + mAppStandby.flushDurationsToDisk(); + mHandler.removeMessages(MSG_FLUSH_TO_DISK); } @@ -1166,7 +474,8 @@ public class UsageStatsService extends SystemService implements final int userCount = mUserState.size(); for (int i = 0; i < userCount; i++) { - idpw.printPair("user", mUserState.keyAt(i)); + int userId = mUserState.keyAt(i); + idpw.printPair("user", userId); idpw.println(); idpw.increaseIndent(); if (argSet.contains("--checkin")) { @@ -1176,57 +485,19 @@ public class UsageStatsService extends SystemService implements idpw.println(); if (args.length > 0) { if ("history".equals(args[0])) { - synchronized (mAppIdleLock) { - mAppIdleHistory.dumpHistory(idpw, mUserState.keyAt(i)); - } + mAppStandby.dumpHistory(idpw, userId); } else if ("flush".equals(args[0])) { - UsageStatsService.this.flushToDiskLocked(); + flushToDiskLocked(); pw.println("Flushed stats to disk"); } } } - synchronized (mAppIdleLock) { - mAppIdleHistory.dump(idpw, mUserState.keyAt(i)); - } + mAppStandby.dumpUser(idpw, userId); idpw.decreaseIndent(); } pw.println(); - synchronized (mAppIdleLock) { - pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps - + "): " + mCarrierPrivilegedApps); - } - - pw.println(); - pw.println("Settings:"); - - pw.print(" mAppIdleDurationMillis="); - TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw); - pw.println(); - - pw.print(" mAppIdleWallclockThresholdMillis="); - TimeUtils.formatDuration(mAppIdleWallclockThresholdMillis, pw); - pw.println(); - - pw.print(" mCheckIdleIntervalMillis="); - TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw); - pw.println(); - - pw.print(" mAppIdleParoleIntervalMillis="); - TimeUtils.formatDuration(mAppIdleParoleIntervalMillis, pw); - pw.println(); - - pw.print(" mAppIdleParoleDurationMillis="); - TimeUtils.formatDuration(mAppIdleParoleDurationMillis, pw); - pw.println(); - - pw.println(); - pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled); - pw.print(" mAppIdleTempParoled="); pw.print(mAppIdleTempParoled); - pw.print(" mCharging="); pw.print(mCharging); - pw.print(" mLastAppIdleParoledTime="); - TimeUtils.formatDuration(mLastAppIdleParoledTime, pw); - pw.println(); + mAppStandby.dumpState(args, pw); } } @@ -1250,50 +521,6 @@ public class UsageStatsService extends SystemService implements onUserRemoved(msg.arg1); break; - case MSG_INFORM_LISTENERS: - informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1); - break; - - case MSG_FORCE_IDLE_STATE: - forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1); - break; - - case MSG_CHECK_IDLE_STATES: - if (checkIdleStates(msg.arg1)) { - mHandler.sendMessageDelayed(mHandler.obtainMessage( - MSG_CHECK_IDLE_STATES, msg.arg1, 0), - mCheckIdleIntervalMillis); - } - break; - - case MSG_ONE_TIME_CHECK_IDLE_STATES: - mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES); - checkIdleStates(UserHandle.USER_ALL); - break; - - case MSG_CHECK_PAROLE_TIMEOUT: - checkParoleTimeout(); - break; - - case MSG_PAROLE_END_TIMEOUT: - if (DEBUG) Slog.d(TAG, "Ending parole"); - setAppIdleParoled(false); - break; - - case MSG_REPORT_CONTENT_PROVIDER_USAGE: - SomeArgs args = (SomeArgs) msg.obj; - reportContentProviderUsage((String) args.arg1, // authority name - (String) args.arg2, // package name - (int) args.arg3); // userId - args.recycle(); - break; - - case MSG_PAROLE_STATE_CHANGED: - if (DEBUG) Slog.d(TAG, "Parole state: " + mAppIdleTempParoled - + ", Charging state:" + mCharging); - informParoleStateChanged(); - break; - default: super.handleMessage(msg); break; @@ -1301,72 +528,6 @@ public class UsageStatsService extends SystemService implements } } - /** - * Observe settings changes for {@link Settings.Global#APP_IDLE_CONSTANTS}. - */ - private class SettingsObserver extends ContentObserver { - /** - * This flag has been used to disable app idle on older builds with bug b/26355386. - */ - @Deprecated - private static final String KEY_IDLE_DURATION_OLD = "idle_duration"; - - private static final String KEY_IDLE_DURATION = "idle_duration2"; - private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold"; - private static final String KEY_PAROLE_INTERVAL = "parole_interval"; - private static final String KEY_PAROLE_DURATION = "parole_duration"; - - private final KeyValueListParser mParser = new KeyValueListParser(','); - - SettingsObserver(Handler handler) { - super(handler); - } - - void registerObserver() { - getContext().getContentResolver().registerContentObserver(Settings.Global.getUriFor( - Settings.Global.APP_IDLE_CONSTANTS), false, this); - } - - @Override - public void onChange(boolean selfChange) { - updateSettings(); - postOneTimeCheckIdleStates(); - } - - void updateSettings() { - synchronized (mAppIdleLock) { - // Look at global settings for this. - // TODO: Maybe apply different thresholds for different users. - try { - mParser.setString(Settings.Global.getString(getContext().getContentResolver(), - Settings.Global.APP_IDLE_CONSTANTS)); - } catch (IllegalArgumentException e) { - Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage()); - // fallthrough, mParser is empty and all defaults will be returned. - } - - // Default: 12 hours of screen-on time sans dream-time - mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION, - COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE); - - mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD, - COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days - - mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4, - COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE); // 8 hours - - // Default: 24 hours between paroles - mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL, - COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE); - - mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION, - COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes - mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis, - mAppIdleScreenThresholdMillis); - } - } - } - private final class BinderService extends IUsageStatsManager.Stub { private boolean hasPermission(String callingPackage) { @@ -1462,7 +623,8 @@ public class UsageStatsService extends SystemService implements Binder.getCallingUid(), userId); final long token = Binder.clearCallingIdentity(); try { - return UsageStatsService.this.isAppIdleFilteredOrParoled(packageName, userId, + return mAppStandby.isAppIdleFilteredOrParoled( + packageName, userId, SystemClock.elapsedRealtime(), obfuscateInstantApps); } finally { Binder.restoreCallingIdentity(token); @@ -1483,9 +645,9 @@ public class UsageStatsService extends SystemService implements "No permission to change app idle state"); final long token = Binder.clearCallingIdentity(); try { - final int appId = getAppId(packageName); + final int appId = mAppStandby.getAppId(packageName); if (appId < 0) return; - UsageStatsService.this.setAppIdleAsync(packageName, idle, userId); + mAppStandby.setAppIdleAsync(packageName, idle, userId); } finally { Binder.restoreCallingIdentity(token); } @@ -1509,7 +671,7 @@ public class UsageStatsService extends SystemService implements getContext().enforceCallingOrSelfPermission( android.Manifest.permission.BIND_CARRIER_SERVICES, "onCarrierPrivilegedAppsChanged can only be called by privileged apps."); - UsageStatsService.this.clearCarrierPrivilegedApps(); + mAppStandby.clearCarrierPrivilegedApps(); } @Override @@ -1624,28 +786,23 @@ public class UsageStatsService extends SystemService implements @Override public void reportContentProviderUsage(String name, String packageName, int userId) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = name; - args.arg2 = packageName; - args.arg3 = userId; - mHandler.obtainMessage(MSG_REPORT_CONTENT_PROVIDER_USAGE, args) - .sendToTarget(); + mAppStandby.postReportContentProviderUsage(name, packageName, userId); } @Override public boolean isAppIdle(String packageName, int uidForAppId, int userId) { - return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId, - SystemClock.elapsedRealtime()); + return mAppStandby.isAppIdleFiltered(packageName, uidForAppId, + userId, SystemClock.elapsedRealtime()); } @Override public int[] getIdleUidsForUser(int userId) { - return UsageStatsService.this.getIdleUidsForUser(userId); + return mAppStandby.getIdleUidsForUser(userId); } @Override public boolean isAppIdleParoleOn() { - return isParoledOrCharging(); + return mAppStandby.isParoledOrCharging(); } @Override @@ -1658,20 +815,20 @@ public class UsageStatsService extends SystemService implements @Override public void addAppIdleStateChangeListener(AppIdleStateChangeListener listener) { - UsageStatsService.this.addListener(listener); + mAppStandby.addListener(listener); listener.onParoleStateChanged(isAppIdleParoleOn()); } @Override public void removeAppIdleStateChangeListener( AppIdleStateChangeListener listener) { - UsageStatsService.this.removeListener(listener); + mAppStandby.removeListener(listener); } @Override public byte[] getBackupPayload(int user, String key) { // Check to ensure that only user 0's data is b/r for now - synchronized (UsageStatsService.this.mLock) { + synchronized (mLock) { if (user == UserHandle.USER_SYSTEM) { final UserUsageStatsService userStats = getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked()); @@ -1684,7 +841,7 @@ public class UsageStatsService extends SystemService implements @Override public void applyRestoredPayload(int user, String key, byte[] payload) { - synchronized (UsageStatsService.this.mLock) { + synchronized (mLock) { if (user == UserHandle.USER_SYSTEM) { final UserUsageStatsService userStats = getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked()); diff --git a/com/android/server/usb/UsbAlsaManager.java b/com/android/server/usb/UsbAlsaManager.java index 68c1d5f6..acc27bee 100644 --- a/com/android/server/usb/UsbAlsaManager.java +++ b/com/android/server/usb/UsbAlsaManager.java @@ -314,7 +314,11 @@ public final class UsbAlsaManager { return null; } - mDevicesParser.scan(); + if (!mDevicesParser.scan()) { + Slog.e(TAG, "Error parsing ALSA devices file."); + return null; + } + int device = mDevicesParser.getDefaultDeviceNum(card); boolean hasPlayback = mDevicesParser.hasPlaybackDevices(card); diff --git a/com/android/server/utils/ManagedApplicationService.java b/com/android/server/utils/ManagedApplicationService.java index 0f251fd8..c5553881 100644 --- a/com/android/server/utils/ManagedApplicationService.java +++ b/com/android/server/utils/ManagedApplicationService.java @@ -16,19 +16,24 @@ package com.android.server.utils; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Handler; import android.os.IBinder; import android.os.IBinder.DeathRecipient; import android.os.IInterface; import android.os.RemoteException; +import android.os.SystemClock; import android.os.UserHandle; import android.util.Slog; +import java.text.SimpleDateFormat; import java.util.Objects; +import java.util.Date; /** * Manages the lifecycle of an application-provided service bound from system server. @@ -38,39 +43,126 @@ import java.util.Objects; public class ManagedApplicationService { private final String TAG = getClass().getSimpleName(); + /** + * Attempt to reconnect service forever if an onBindingDied or onServiceDisconnected event + * is received. + */ + public static final int RETRY_FOREVER = 1; + + /** + * Never attempt to reconnect the service - a single onBindingDied or onServiceDisconnected + * event will cause this to fully unbind the service and never attempt to reconnect. + */ + public static final int RETRY_NEVER = 2; + + /** + * Attempt to reconnect the service until the maximum number of retries is reached, then stop. + * + * The first retry will occur MIN_RETRY_DURATION_MS after the disconnection, and each + * subsequent retry will occur after 2x the duration used for the previous retry up to the + * MAX_RETRY_DURATION_MS duration. + * + * In this case, retries mean a full unbindService/bindService pair to handle cases when the + * usual service re-connection logic in ActiveServices has very high backoff times or when the + * serviceconnection has fully died due to a package update or similar. + */ + public static final int RETRY_BEST_EFFORT = 3; + + // Maximum number of retries before giving up (for RETRY_BEST_EFFORT). + private static final int MAX_RETRY_COUNT = 4; + // Max time between retry attempts. + private static final long MAX_RETRY_DURATION_MS = 16000; + // Min time between retry attempts. + private static final long MIN_RETRY_DURATION_MS = 2000; + // Time since the last retry attempt after which to clear the retry attempt counter. + private static final long RETRY_RESET_TIME_MS = MAX_RETRY_DURATION_MS * 4; + private final Context mContext; private final int mUserId; private final ComponentName mComponent; private final int mClientLabel; private final String mSettingsAction; private final BinderChecker mChecker; - - private final DeathRecipient mDeathRecipient = new DeathRecipient() { - @Override - public void binderDied() { - synchronized (mLock) { - mBoundInterface = null; - } - } - }; + private final boolean mIsImportant; + private final int mRetryType; + private final Handler mHandler; + private final Runnable mRetryRunnable = this::doRetry; + private final EventCallback mEventCb; private final Object mLock = new Object(); // State protected by mLock - private ServiceConnection mPendingConnection; private ServiceConnection mConnection; private IInterface mBoundInterface; private PendingEvent mPendingEvent; + private int mRetryCount; + private long mLastRetryTimeMs; + private long mNextRetryDurationMs = MIN_RETRY_DURATION_MS; + private boolean mRetrying; + + public static interface LogFormattable { + String toLogString(SimpleDateFormat dateFormat); + } + + /** + * Lifecycle event of this managed service. + */ + public static class LogEvent implements LogFormattable { + public static final int EVENT_CONNECTED = 1; + public static final int EVENT_DISCONNECTED = 2; + public static final int EVENT_BINDING_DIED = 3; + public static final int EVENT_STOPPED_PERMANENTLY = 4; + + // Time of the events in "current time ms" timebase. + public final long timestamp; + // Name of the component for this system service. + public final ComponentName component; + // ID of the event that occurred. + public final int event; + + public LogEvent(long timestamp, ComponentName component, int event) { + this.timestamp = timestamp; + this.component = component; + this.event = event; + } + + @Override + public String toLogString(SimpleDateFormat dateFormat) { + return dateFormat.format(new Date(timestamp)) + " " + eventToString(event) + + " Managed Service: " + + ((component == null) ? "None" : component.flattenToString()); + } + + public static String eventToString(int event) { + switch (event) { + case EVENT_CONNECTED: + return "Connected"; + case EVENT_DISCONNECTED: + return "Disconnected"; + case EVENT_BINDING_DIED: + return "Binding Died For"; + case EVENT_STOPPED_PERMANENTLY: + return "Permanently Stopped"; + default: + return "Unknown Event Occurred"; + } + } + } private ManagedApplicationService(final Context context, final ComponentName component, final int userId, int clientLabel, String settingsAction, - BinderChecker binderChecker) { + BinderChecker binderChecker, boolean isImportant, int retryType, Handler handler, + EventCallback eventCallback) { mContext = context; mComponent = component; mUserId = userId; mClientLabel = clientLabel; mSettingsAction = settingsAction; mChecker = binderChecker; + mIsImportant = isImportant; + mRetryType = retryType; + mHandler = handler; + mEventCb = eventCallback; } /** @@ -85,7 +177,17 @@ public class ManagedApplicationService { * Implement to call IInterface methods after service is connected. */ public interface PendingEvent { - void runEvent(IInterface service) throws RemoteException; + void runEvent(IInterface service) throws RemoteException; + } + + /** + * Implement to be notified about any problems with remote service. + */ + public interface EventCallback { + /** + * Called when an sevice lifecycle event occurs. + */ + void onServiceEvent(LogEvent event); } /** @@ -95,19 +197,28 @@ public class ManagedApplicationService { * @param component the {@link ComponentName} of the application service to bind. * @param userId the user ID of user to bind the application service as. * @param clientLabel the resource ID of a label displayed to the user indicating the - * binding service. + * binding service, or 0 if none is desired. * @param settingsAction an action that can be used to open the Settings UI to enable/disable - * binding to these services. - * @param binderChecker an interface used to validate the returned binder object. + * binding to these services, or null if none is desired. + * @param binderChecker an interface used to validate the returned binder object, or null if + * this interface is unchecked. + * @param isImportant bind the user service with BIND_IMPORTANT. + * @param retryType reconnect behavior to have when bound service is disconnected. + * @param handler the Handler to use for retries and delivering EventCallbacks. + * @param eventCallback a callback used to deliver disconnection events, or null if you + * don't care. * @return a ManagedApplicationService instance. */ public static ManagedApplicationService build(@NonNull final Context context, - @NonNull final ComponentName component, final int userId, @NonNull int clientLabel, - @NonNull String settingsAction, @NonNull BinderChecker binderChecker) { + @NonNull final ComponentName component, final int userId, int clientLabel, + @Nullable String settingsAction, @Nullable BinderChecker binderChecker, + boolean isImportant, int retryType, @NonNull Handler handler, + @Nullable EventCallback eventCallback) { return new ManagedApplicationService(context, component, userId, clientLabel, - settingsAction, binderChecker); + settingsAction, binderChecker, isImportant, retryType, handler, eventCallback); } + /** * @return the user ID of the user that owns the bound service. */ @@ -138,13 +249,12 @@ public class ManagedApplicationService { return true; } - - /** - * Send an event to run as soon as the binder interface is available. - * - * @param event a {@link PendingEvent} to send. - */ - public void sendEvent(@NonNull PendingEvent event) { + /** + * Send an event to run as soon as the binder interface is available. + * + * @param event a {@link PendingEvent} to send. + */ + public void sendEvent(@NonNull PendingEvent event) { IInterface iface; synchronized (mLock) { iface = mBoundInterface; @@ -167,15 +277,13 @@ public class ManagedApplicationService { */ public void disconnect() { synchronized (mLock) { - // Wipe out pending connections - mPendingConnection = null; - // Unbind existing connection, if it exists - if (mConnection != null) { - mContext.unbindService(mConnection); - mConnection = null; + if (mConnection == null) { + return; } + mContext.unbindService(mConnection); + mConnection = null; mBoundInterface = null; } } @@ -185,48 +293,70 @@ public class ManagedApplicationService { */ public void connect() { synchronized (mLock) { - if (mConnection != null || mPendingConnection != null) { + if (mConnection != null) { // We're already connected or are trying to connect return; } - final PendingIntent pendingIntent = PendingIntent.getActivity( - mContext, 0, new Intent(mSettingsAction), 0); - final Intent intent = new Intent().setComponent(mComponent). - putExtra(Intent.EXTRA_CLIENT_LABEL, mClientLabel). - putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent); + Intent intent = new Intent().setComponent(mComponent); + if (mClientLabel != 0) { + intent.putExtra(Intent.EXTRA_CLIENT_LABEL, mClientLabel); + } + if (mSettingsAction != null) { + intent.putExtra(Intent.EXTRA_CLIENT_INTENT, + PendingIntent.getActivity(mContext, 0, new Intent(mSettingsAction), 0)); + } + + mConnection = new ServiceConnection() { + @Override + public void onBindingDied(ComponentName componentName) { + final long timestamp = System.currentTimeMillis(); + Slog.w(TAG, "Service binding died: " + componentName); + synchronized (mLock) { + if (mConnection != this) { + return; + } + mHandler.post(() -> { + mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent, + LogEvent.EVENT_BINDING_DIED)); + }); + + mBoundInterface = null; + startRetriesLocked(); + } + } - final ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { + final long timestamp = System.currentTimeMillis(); + Slog.i(TAG, "Service connected: " + componentName); IInterface iface = null; PendingEvent pendingEvent = null; synchronized (mLock) { - if (mPendingConnection == this) { - // No longer pending, remove from pending connection - mPendingConnection = null; - mConnection = this; - } else { - // Service connection wasn't pending, must have been disconnected - mContext.unbindService(this); + if (mConnection != this) { + // Must've been unbound. return; } + mHandler.post(() -> { + mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent, + LogEvent.EVENT_CONNECTED)); + }); - try { - iBinder.linkToDeath(mDeathRecipient, 0); + stopRetriesLocked(); + + mBoundInterface = null; + if (mChecker != null) { mBoundInterface = mChecker.asInterface(iBinder); if (!mChecker.checkType(mBoundInterface)) { - // Received an invalid binder, disconnect - mContext.unbindService(this); + // Received an invalid binder, disconnect. mBoundInterface = null; + Slog.w(TAG, "Invalid binder from " + componentName); + startRetriesLocked(); + return; } iface = mBoundInterface; pendingEvent = mPendingEvent; mPendingEvent = null; - } catch (RemoteException e) { - // DOA - Slog.w(TAG, "Unable to bind service: " + intent, e); - mBoundInterface = null; } } if (iface != null && pendingEvent != null) { @@ -234,28 +364,44 @@ public class ManagedApplicationService { pendingEvent.runEvent(iface); } catch (RuntimeException | RemoteException ex) { Slog.e(TAG, "Received exception from user service: ", ex); + startRetriesLocked(); } } } @Override public void onServiceDisconnected(ComponentName componentName) { - Slog.w(TAG, "Service disconnected: " + intent); - mConnection = null; - mBoundInterface = null; + final long timestamp = System.currentTimeMillis(); + Slog.w(TAG, "Service disconnected: " + componentName); + synchronized (mLock) { + if (mConnection != this) { + return; + } + + mHandler.post(() -> { + mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent, + LogEvent.EVENT_DISCONNECTED)); + }); + + mBoundInterface = null; + startRetriesLocked(); + } } }; - mPendingConnection = serviceConnection; - + int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE; + if (mIsImportant) { + flags |= Context.BIND_IMPORTANT; + } try { - if (!mContext.bindServiceAsUser(intent, serviceConnection, - Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, + if (!mContext.bindServiceAsUser(intent, mConnection, flags, new UserHandle(mUserId))) { Slog.w(TAG, "Unable to bind service: " + intent); + startRetriesLocked(); } } catch (SecurityException e) { Slog.w(TAG, "Unable to bind service: " + intent, e); + startRetriesLocked(); } } } @@ -263,4 +409,81 @@ public class ManagedApplicationService { private boolean matches(final ComponentName component, final int userId) { return Objects.equals(mComponent, component) && mUserId == userId; } + + private void startRetriesLocked() { + if (checkAndDeliverServiceDiedCbLocked()) { + // If we delivered the service callback, disconnect and stop retrying. + disconnect(); + return; + } + + if (mRetrying) { + // Retry already queued, don't queue a new one. + return; + } + mRetrying = true; + queueRetryLocked(); + } + + private void stopRetriesLocked() { + mRetrying = false; + mHandler.removeCallbacks(mRetryRunnable); + } + + private void queueRetryLocked() { + long now = SystemClock.uptimeMillis(); + if ((now - mLastRetryTimeMs) > RETRY_RESET_TIME_MS) { + // It's been longer than the reset time since we last had to retry. Re-initialize. + mNextRetryDurationMs = MIN_RETRY_DURATION_MS; + mRetryCount = 0; + } + mLastRetryTimeMs = now; + mHandler.postDelayed(mRetryRunnable, mNextRetryDurationMs); + mNextRetryDurationMs = Math.min(2 * mNextRetryDurationMs, MAX_RETRY_DURATION_MS); + mRetryCount++; + } + + private boolean checkAndDeliverServiceDiedCbLocked() { + + if (mRetryType == RETRY_NEVER || (mRetryType == RETRY_BEST_EFFORT + && mRetryCount >= MAX_RETRY_COUNT)) { + // If we never retry, or we've exhausted our retries, post the onServiceDied callback. + Slog.e(TAG, "Service " + mComponent + " has died too much, not retrying."); + if (mEventCb != null) { + final long timestamp = System.currentTimeMillis(); + mHandler.post(() -> { + mEventCb.onServiceEvent(new LogEvent(timestamp, mComponent, + LogEvent.EVENT_STOPPED_PERMANENTLY)); + }); + } + return true; + } + return false; + } + + private void doRetry() { + synchronized (mLock) { + if (mConnection == null) { + // We disconnected for good. Don't attempt to retry. + return; + } + if (!mRetrying) { + // We successfully connected. Don't attempt to retry. + return; + } + Slog.i(TAG, "Attempting to reconnect " + mComponent + "..."); + // While frameworks may restart the remote Service if we stay bound, we have little + // control of the backoff timing for reconnecting the service. In the event of a + // process crash, the backoff time can be very large (1-30 min), which is not + // acceptable for the types of services this is used for. Instead force an unbind/bind + // sequence to cause a more immediate retry. + disconnect(); + if (checkAndDeliverServiceDiedCbLocked()) { + // No more retries. + return; + } + queueRetryLocked(); + connect(); + } + } } diff --git a/com/android/server/utils/PriorityDump.java b/com/android/server/utils/PriorityDump.java index c05cc3ff..054f1564 100644 --- a/com/android/server/utils/PriorityDump.java +++ b/com/android/server/utils/PriorityDump.java @@ -59,10 +59,10 @@ public class SpringfieldNuclearPowerPlant extends Binder { Donuts in the box: 1 Nuclear reactor status: DANGER - MELTDOWN IMMINENT - $ adb shell dumpsys snpp --dump_priority CRITICAL + $ adb shell dumpsys snpp --dump-priority CRITICAL Donuts in the box: 1 - $ adb shell dumpsys snpp --dump_priority NORMAL + $ adb shell dumpsys snpp --dump-priority NORMAL Nuclear reactor status: DANGER - MELTDOWN IMMINENT * </code></pre> @@ -84,7 +84,7 @@ public class SpringfieldNuclearPowerPlant extends Binder { */ public final class PriorityDump { - public static final String PRIORITY_ARG = "--dump_priority"; + public static final String PRIORITY_ARG = "--dump-priority"; private PriorityDump() { throw new UnsupportedOperationException(); @@ -92,12 +92,12 @@ public final class PriorityDump { /** * Parses {@code} and call the proper {@link PriorityDumper} method when the first argument is - * {@code --dump_priority}, stripping the priority and its type. + * {@code --dump-priority}, stripping the priority and its type. * <p> - * For example, if called as {@code --dump_priority HIGH arg1 arg2 arg3}, it will call + * For example, if called as {@code --dump-priority HIGH arg1 arg2 arg3}, it will call * <code>dumper.dumpHigh(fd, pw, {"arg1", "arg2", "arg3"}) </code> * <p> - * If the {@code --dump_priority} is not set, it calls + * If the {@code --dump-priority} is not set, it calls * {@link PriorityDumper#dump(FileDescriptor, PrintWriter, String[])} passing the whole * {@code args} instead. */ @@ -124,7 +124,7 @@ public final class PriorityDump { } /** - * Gets an array without the {@code --dump_priority PRIORITY} prefix. + * Gets an array without the {@code --dump-priority PRIORITY} prefix. */ private static String[] getStrippedArgs(String[] args) { final String[] stripped = new String[args.length - 2]; diff --git a/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java index 3788cf33..b040a632 100644 --- a/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java +++ b/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java @@ -20,6 +20,7 @@ import static android.app.ActivityManager.START_ASSISTANT_HIDDEN_SESSION; import static android.app.ActivityManager.START_ASSISTANT_NOT_ACTIVE_SESSION; import static android.app.ActivityManager.START_VOICE_HIDDEN_SESSION; import static android.app.ActivityManager.START_VOICE_NOT_ACTIVE_SESSION; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import android.app.ActivityManager; import android.app.ActivityManager.StackId; @@ -222,8 +223,8 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne } intent = new Intent(intent); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - ActivityOptions options = ActivityOptions.makeBasic(); - options.setLaunchStackId(StackId.ASSISTANT_STACK_ID); + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchActivityType(ACTIVITY_TYPE_ASSISTANT); return mAm.startAssistantActivity(mComponent.getPackageName(), callingPid, callingUid, intent, resolvedType, options.toBundle(), mUser); } catch (RemoteException e) { diff --git a/com/android/server/vr/Vr2dDisplay.java b/com/android/server/vr/Vr2dDisplay.java index 8f50a39a..95d03d4b 100644 --- a/com/android/server/vr/Vr2dDisplay.java +++ b/com/android/server/vr/Vr2dDisplay.java @@ -294,6 +294,9 @@ class Vr2dDisplay { int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH; flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT; + flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; + flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; + flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL; mVirtualDisplay = mDisplayManager.createVirtualDisplay(null /* projection */, DISPLAY_NAME, mVirtualDisplayWidth, mVirtualDisplayHeight, mVirtualDisplayDpi, null /* surface */, flags, null /* callback */, null /* handler */, diff --git a/com/android/server/vr/VrManagerInternal.java b/com/android/server/vr/VrManagerInternal.java index bdd9de01..7b1e12e2 100644 --- a/com/android/server/vr/VrManagerInternal.java +++ b/com/android/server/vr/VrManagerInternal.java @@ -74,6 +74,13 @@ public abstract class VrManagerInternal { public abstract void onScreenStateChanged(boolean isScreenOn); /** + * Set whether the keyguard is currently active/showing. + * + * @param isShowing is {@code true} if the keyguard is active/showing. + */ + public abstract void onKeyguardStateChanged(boolean isShowing); + + /** * Return NO_ERROR if the given package is installed on the device and enabled as a * VrListenerService for the given current user, or a negative error code indicating a failure. * diff --git a/com/android/server/vr/VrManagerService.java b/com/android/server/vr/VrManagerService.java index 1f0b2f00..e7e4efcc 100644 --- a/com/android/server/vr/VrManagerService.java +++ b/com/android/server/vr/VrManagerService.java @@ -66,6 +66,8 @@ import com.android.server.LocalServices; import com.android.server.SystemConfig; import com.android.server.SystemService; import com.android.server.utils.ManagedApplicationService.PendingEvent; +import com.android.server.utils.ManagedApplicationService.LogEvent; +import com.android.server.utils.ManagedApplicationService.LogFormattable; import com.android.server.vr.EnabledComponentsObserver.EnabledComponentChangeListener; import com.android.server.utils.ManagedApplicationService; import com.android.server.utils.ManagedApplicationService.BinderChecker; @@ -108,16 +110,18 @@ public class VrManagerService extends SystemService implements EnabledComponentC static final boolean DBG = false; private static final int PENDING_STATE_DELAY_MS = 300; - private static final int EVENT_LOG_SIZE = 32; + private static final int EVENT_LOG_SIZE = 64; private static final int INVALID_APPOPS_MODE = -1; /** Null set of sleep sleep flags. */ private static final int FLAG_NONE = 0; /** Flag set when the device is not sleeping. */ - private static final int FLAG_AWAKE = 1; + private static final int FLAG_AWAKE = 1 << 0; /** Flag set when the screen has been turned on. */ - private static final int FLAG_SCREEN_ON = 2; + private static final int FLAG_SCREEN_ON = 1 << 1; + /** Flag set when the keyguard is not active. */ + private static final int FLAG_KEYGUARD_UNLOCKED = 1 << 2; /** Flag indicating that all system sleep flags have been set.*/ - private static final int FLAG_ALL = FLAG_AWAKE | FLAG_SCREEN_ON; + private static final int FLAG_ALL = FLAG_AWAKE | FLAG_SCREEN_ON | FLAG_KEYGUARD_UNLOCKED; private static native void initializeNative(); private static native void setVrModeNative(boolean enabled); @@ -134,6 +138,7 @@ public class VrManagerService extends SystemService implements EnabledComponentC private int mVrAppProcessId; private EnabledComponentsObserver mComponentObserver; private ManagedApplicationService mCurrentVrService; + private ManagedApplicationService mCurrentVrCompositorService; private ComponentName mDefaultVrService; private Context mContext; private ComponentName mCurrentVrModeComponent; @@ -147,19 +152,45 @@ public class VrManagerService extends SystemService implements EnabledComponentC private int mPreviousCoarseLocationMode = INVALID_APPOPS_MODE; private int mPreviousManageOverlayMode = INVALID_APPOPS_MODE; private VrState mPendingState; - private final ArrayDeque<VrState> mLoggingDeque = new ArrayDeque<>(EVENT_LOG_SIZE); + private boolean mLogLimitHit; + private final ArrayDeque<LogFormattable> mLoggingDeque = new ArrayDeque<>(EVENT_LOG_SIZE); private final NotificationAccessManager mNotifAccessManager = new NotificationAccessManager(); private INotificationManager mNotificationManager; /** Tracks the state of the screen and keyguard UI.*/ - private int mSystemSleepFlags = FLAG_AWAKE; + private int mSystemSleepFlags = FLAG_AWAKE | FLAG_KEYGUARD_UNLOCKED; /** * Set when ACTION_USER_UNLOCKED is fired. We shouldn't try to bind to the - * vr service before then. + * vr service before then. This gets set only once the first time the user unlocks the device + * and stays true thereafter. */ private boolean mUserUnlocked; private Vr2dDisplay mVr2dDisplay; private boolean mBootsToVr; + // Handles events from the managed services (e.g. VrListenerService and any bound VR compositor + // service). + private final ManagedApplicationService.EventCallback mEventCallback + = new ManagedApplicationService.EventCallback() { + @Override + public void onServiceEvent(LogEvent event) { + logEvent(event); + + ComponentName component = null; + synchronized (mLock) { + component = ((mCurrentVrService == null) ? null : mCurrentVrService.getComponent()); + } + + // If not on an AIO device and we permanently stopped trying to connect to the + // VrListenerService (or don't have one bound), leave persistent VR mode and VR mode. + if (!mBootsToVr && event.event == LogEvent.EVENT_STOPPED_PERMANENTLY && + (component == null || component.equals(event.component))) { + Slog.e(TAG, "VrListenerSevice has died permanently, leaving system VR mode."); + // We're not a native VR device. Leave VR + persistent mode. + setPersistentVrModeEnabled(false); + } + } + }; + private static final int MSG_VR_STATE_CHANGE = 0; private static final int MSG_PENDING_VR_STATE_CHANGE = 1; private static final int MSG_PERSISTENT_VR_MODE_STATE_CHANGE = 2; @@ -180,7 +211,6 @@ public class VrManagerService extends SystemService implements EnabledComponentC if (mBootsToVr) { setPersistentVrModeEnabled(true); } - consumeAndApplyPendingStateLocked(); if (mBootsToVr && !mVrModeEnabled) { setVrMode(true, mDefaultVrService, 0, -1, null); } @@ -202,29 +232,40 @@ public class VrManagerService extends SystemService implements EnabledComponentC } private void setSleepState(boolean isAsleep) { - synchronized(mLock) { + setSystemState(FLAG_AWAKE, !isAsleep); + } - if (!isAsleep) { - mSystemSleepFlags |= FLAG_AWAKE; - } else { - mSystemSleepFlags &= ~FLAG_AWAKE; - } + private void setScreenOn(boolean isScreenOn) { + setSystemState(FLAG_SCREEN_ON, isScreenOn); + } - updateVrModeAllowedLocked(); - } + private void setKeyguardShowing(boolean isShowing) { + setSystemState(FLAG_KEYGUARD_UNLOCKED, !isShowing); } - private void setScreenOn(boolean isScreenOn) { + private void setSystemState(int flags, boolean isOn) { synchronized(mLock) { - if (isScreenOn) { - mSystemSleepFlags |= FLAG_SCREEN_ON; + int oldState = mSystemSleepFlags; + if (isOn) { + mSystemSleepFlags |= flags; } else { - mSystemSleepFlags &= ~FLAG_SCREEN_ON; + mSystemSleepFlags &= ~flags; + } + if (oldState != mSystemSleepFlags) { + if (DBG) Slog.d(TAG, "System state: " + getStateAsString()); + updateVrModeAllowedLocked(); } - updateVrModeAllowedLocked(); } } + private String getStateAsString() { + return new StringBuilder() + .append((mSystemSleepFlags & FLAG_AWAKE) != 0 ? "awake, " : "") + .append((mSystemSleepFlags & FLAG_SCREEN_ON) != 0 ? "screen_on, " : "") + .append((mSystemSleepFlags & FLAG_KEYGUARD_UNLOCKED) != 0 ? "keyguard_off" : "") + .toString(); + } + private void setUserUnlocked() { synchronized(mLock) { mUserUnlocked = true; @@ -276,7 +317,24 @@ public class VrManagerService extends SystemService implements EnabledComponentC } }; - private static class VrState { + // Event used to log when settings are changed for dumpsys logs. + private static class SettingEvent implements LogFormattable { + public final long timestamp; + public final String what; + + SettingEvent(String what) { + this.timestamp = System.currentTimeMillis(); + this.what = what; + } + + @Override + public String toLogString(SimpleDateFormat dateFormat) { + return dateFormat.format(new Date(timestamp)) + " " + what; + } + } + + // Event used to track changes of the primary on-screen VR activity. + private static class VrState implements LogFormattable { final boolean enabled; final boolean running2dInVr; final int userId; @@ -286,7 +344,6 @@ public class VrManagerService extends SystemService implements EnabledComponentC final long timestamp; final boolean defaultPermissionsGranted; - VrState(boolean enabled, boolean running2dInVr, ComponentName targetPackageName, int userId, int processId, ComponentName callingPackage) { this.enabled = enabled; @@ -310,6 +367,39 @@ public class VrManagerService extends SystemService implements EnabledComponentC this.defaultPermissionsGranted = defaultPermissionsGranted; this.timestamp = System.currentTimeMillis(); } + + @Override + public String toLogString(SimpleDateFormat dateFormat) { + String tab = " "; + String newLine = "\n"; + StringBuilder sb = new StringBuilder(dateFormat.format(new Date(timestamp))); + sb.append(tab); + sb.append("State changed to:"); + sb.append(tab); + sb.append((enabled) ? "ENABLED" : "DISABLED"); + sb.append(newLine); + if (enabled) { + sb.append(tab); + sb.append("User="); + sb.append(userId); + sb.append(newLine); + sb.append(tab); + sb.append("Current VR Activity="); + sb.append((callingPackage == null) ? "None" : callingPackage.flattenToString()); + sb.append(newLine); + sb.append(tab); + sb.append("Bound VrListenerService="); + sb.append((targetPackageName == null) ? "None" + : targetPackageName.flattenToString()); + sb.append(newLine); + if (defaultPermissionsGranted) { + sb.append(tab); + sb.append("Default permissions granted to the bound VrListenerService."); + sb.append(newLine); + } + } + return sb.toString(); + } } private static final BinderChecker sBinderChecker = new BinderChecker() { @@ -490,6 +580,13 @@ public class VrManagerService extends SystemService implements EnabledComponentC } @Override + public void setAndBindCompositor(String componentName) { + enforceCallerPermissionAnyOf(Manifest.permission.RESTRICTED_VR_ACCESS); + VrManagerService.this.setAndBindCompositor( + (componentName == null) ? null : ComponentName.unflattenFromString(componentName)); + } + + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; @@ -497,6 +594,12 @@ public class VrManagerService extends SystemService implements EnabledComponentC pw.println("VR mode is currently: " + ((mVrModeAllowed) ? "allowed" : "disallowed")); pw.println("Persistent VR mode is currently: " + ((mPersistentVrModeEnabled) ? "enabled" : "disabled")); + pw.println("Currently bound VR listener service: " + + ((mCurrentVrService == null) + ? "None" : mCurrentVrService.getComponent().flattenToString())); + pw.println("Currently bound VR compositor service: " + + ((mCurrentVrCompositorService == null) + ? "None" : mCurrentVrCompositorService.getComponent().flattenToString())); pw.println("Previous state transitions:\n"); String tab = " "; dumpStateTransitions(pw); @@ -582,6 +685,11 @@ public class VrManagerService extends SystemService implements EnabledComponentC } @Override + public void onKeyguardStateChanged(boolean isShowing) { + VrManagerService.this.setKeyguardShowing(isShowing); + } + + @Override public boolean isCurrentVrListener(String packageName, int userId) { return VrManagerService.this.isCurrentVrListener(packageName, userId); } @@ -785,6 +893,7 @@ public class VrManagerService extends SystemService implements EnabledComponentC + mCurrentVrService.getComponent() + " for user " + mCurrentVrService.getUserId()); mCurrentVrService.disconnect(); + updateCompositorServiceLocked(UserHandle.USER_NULL, null); mCurrentVrService = null; } else { nothingChanged = true; @@ -798,6 +907,7 @@ public class VrManagerService extends SystemService implements EnabledComponentC Slog.i(TAG, "VR mode component changed to " + component + ", disconnecting " + mCurrentVrService.getComponent() + " for user " + mCurrentVrService.getUserId()); + updateCompositorServiceLocked(UserHandle.USER_NULL, null); createAndConnectService(component, userId); sendUpdatedCaller = true; } else { @@ -985,7 +1095,7 @@ public class VrManagerService extends SystemService implements EnabledComponentC private void createAndConnectService(@NonNull ComponentName component, int userId) { - mCurrentVrService = VrManagerService.create(mContext, component, userId); + mCurrentVrService = createVrListenerService(component, userId); mCurrentVrService.connect(); Slog.i(TAG, "Connecting " + component + " for user " + userId); } @@ -1020,13 +1130,27 @@ public class VrManagerService extends SystemService implements EnabledComponentC } /** - * Helper function for making ManagedApplicationService instances. + * Helper function for making ManagedApplicationService for VrListenerService instances. */ - private static ManagedApplicationService create(@NonNull Context context, - @NonNull ComponentName component, int userId) { - return ManagedApplicationService.build(context, component, userId, + private ManagedApplicationService createVrListenerService(@NonNull ComponentName component, + int userId) { + int retryType = (mBootsToVr) ? ManagedApplicationService.RETRY_FOREVER + : ManagedApplicationService.RETRY_NEVER; + return ManagedApplicationService.build(mContext, component, userId, R.string.vr_listener_binding_label, Settings.ACTION_VR_LISTENER_SETTINGS, - sBinderChecker); + sBinderChecker, /*isImportant*/true, retryType, mHandler, mEventCallback); + } + + /** + * Helper function for making ManagedApplicationService for VR Compositor instances. + */ + private ManagedApplicationService createVrCompositorService(@NonNull ComponentName component, + int userId) { + int retryType = (mBootsToVr) ? ManagedApplicationService.RETRY_FOREVER + : ManagedApplicationService.RETRY_BEST_EFFORT; + return ManagedApplicationService.build(mContext, component, userId, /*clientLabel*/0, + /*settingsAction*/null, /*binderChecker*/null, /*isImportant*/true, retryType, + mHandler, /*disconnectCallback*/mEventCallback); } /** @@ -1057,44 +1181,35 @@ public class VrManagerService extends SystemService implements EnabledComponentC private void logStateLocked() { ComponentName currentBoundService = (mCurrentVrService == null) ? null : - mCurrentVrService.getComponent(); - VrState current = new VrState(mVrModeEnabled, mRunning2dInVr, currentBoundService, - mCurrentVrModeUser, mVrAppProcessId, mCurrentVrModeComponent, mWasDefaultGranted); - if (mLoggingDeque.size() == EVENT_LOG_SIZE) { - mLoggingDeque.removeFirst(); + mCurrentVrService.getComponent(); + logEvent(new VrState(mVrModeEnabled, mRunning2dInVr, currentBoundService, + mCurrentVrModeUser, mVrAppProcessId, mCurrentVrModeComponent, mWasDefaultGranted)); + } + + private void logEvent(LogFormattable event) { + synchronized (mLoggingDeque) { + if (mLoggingDeque.size() == EVENT_LOG_SIZE) { + mLoggingDeque.removeFirst(); + mLogLimitHit = true; + } + mLoggingDeque.add(event); } - mLoggingDeque.add(current); } private void dumpStateTransitions(PrintWriter pw) { SimpleDateFormat d = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); - String tab = " "; - if (mLoggingDeque.size() == 0) { - pw.print(tab); - pw.println("None"); - } - for (VrState state : mLoggingDeque) { - pw.print(d.format(new Date(state.timestamp))); - pw.print(tab); - pw.print("State changed to:"); - pw.print(tab); - pw.println((state.enabled) ? "ENABLED" : "DISABLED"); - if (state.enabled) { - pw.print(tab); - pw.print("User="); - pw.println(state.userId); - pw.print(tab); - pw.print("Current VR Activity="); - pw.println((state.callingPackage == null) ? - "None" : state.callingPackage.flattenToString()); - pw.print(tab); - pw.print("Bound VrListenerService="); - pw.println((state.targetPackageName == null) ? - "None" : state.targetPackageName.flattenToString()); - if (state.defaultPermissionsGranted) { - pw.print(tab); - pw.println("Default permissions granted to the bound VrListenerService."); - } + synchronized (mLoggingDeque) { + if (mLoggingDeque.size() == 0) { + pw.print(" "); + pw.println("None"); + } + + if (mLogLimitHit) { + pw.println("..."); // Indicates log overflow + } + + for (LogFormattable event : mLoggingDeque) { + pw.println(event.toLogString(d)); } } } @@ -1177,10 +1292,41 @@ public class VrManagerService extends SystemService implements EnabledComponentC return INVALID_DISPLAY; } + private void setAndBindCompositor(ComponentName componentName) { + final int userId = UserHandle.getCallingUserId(); + final long token = Binder.clearCallingIdentity(); + synchronized (mLock) { + updateCompositorServiceLocked(userId, componentName); + } + Binder.restoreCallingIdentity(token); + } + + private void updateCompositorServiceLocked(int userId, ComponentName componentName) { + if (mCurrentVrCompositorService != null + && mCurrentVrCompositorService.disconnectIfNotMatching(componentName, userId)) { + Slog.i(TAG, "Disconnecting compositor service: " + + mCurrentVrCompositorService.getComponent()); + // Check if existing service matches the requested one, if not (or if the requested + // component is null) disconnect it. + mCurrentVrCompositorService = null; + } + + if (componentName != null && mCurrentVrCompositorService == null) { + // We don't have an existing service matching the requested component, so attempt to + // connect one. + Slog.i(TAG, "Connecting compositor service: " + componentName); + mCurrentVrCompositorService = createVrCompositorService(componentName, userId); + mCurrentVrCompositorService.connect(); + } + } + private void setPersistentModeAndNotifyListenersLocked(boolean enabled) { if (mPersistentVrModeEnabled == enabled) { return; } + String eventName = "Persistent VR mode " + ((enabled) ? "enabled" : "disabled"); + Slog.i(TAG, eventName); + logEvent(new SettingEvent(eventName)); mPersistentVrModeEnabled = enabled; mHandler.sendMessage(mHandler.obtainMessage(MSG_PERSISTENT_VR_MODE_STATE_CHANGE, diff --git a/com/android/server/webkit/SystemImpl.java b/com/android/server/webkit/SystemImpl.java index bf769ed4..1e334b83 100644 --- a/com/android/server/webkit/SystemImpl.java +++ b/com/android/server/webkit/SystemImpl.java @@ -304,6 +304,6 @@ public class SystemImpl implements SystemInterface { // flags declaring we want extra info from the package manager for webview providers private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA - | PackageManager.GET_SIGNATURES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING - | PackageManager.MATCH_ANY_USER; + | PackageManager.GET_SIGNATURES | PackageManager.GET_SHARED_LIBRARY_FILES + | PackageManager.MATCH_DEBUG_TRIAGED_MISSING | PackageManager.MATCH_ANY_USER; } diff --git a/com/android/server/wifi/NetworkListStoreData.java b/com/android/server/wifi/NetworkListStoreData.java index 5ddfd4df..f287d4b9 100644 --- a/com/android/server/wifi/NetworkListStoreData.java +++ b/com/android/server/wifi/NetworkListStoreData.java @@ -16,10 +16,12 @@ package com.android.server.wifi; +import android.content.Context; import android.net.IpConfiguration; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiConfiguration.NetworkSelectionStatus; import android.net.wifi.WifiEnterpriseConfig; +import android.os.Process; import android.util.Log; import android.util.Pair; @@ -52,6 +54,8 @@ public class NetworkListStoreData implements WifiConfigStore.StoreData { private static final String XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION = "WifiEnterpriseConfiguration"; + private final Context mContext; + /** * List of saved shared networks visible to all the users to be stored in the shared store file. */ @@ -62,7 +66,9 @@ public class NetworkListStoreData implements WifiConfigStore.StoreData { */ private List<WifiConfiguration> mUserConfigurations; - NetworkListStoreData() {} + NetworkListStoreData(Context context) { + mContext = context; + } @Override public void serializeData(XmlSerializer out, boolean shared) @@ -282,6 +288,19 @@ public class NetworkListStoreData implements WifiConfigStore.StoreData { "Configuration key does not match. Retrieved: " + configKeyParsed + ", Calculated: " + configKeyCalculated); } + // Set creatorUid/creatorName for networks which don't have it set to valid value. + String creatorName = mContext.getPackageManager().getNameForUid(configuration.creatorUid); + if (creatorName == null) { + Log.e(TAG, "Invalid creatorUid for saved network " + configuration.configKey() + + ", creatorUid=" + configuration.creatorUid); + configuration.creatorUid = Process.SYSTEM_UID; + configuration.creatorName = creatorName; + } else if (!creatorName.equals(configuration.creatorName)) { + Log.w(TAG, "Invalid creatorName for saved network " + configuration.configKey() + + ", creatorUid=" + configuration.creatorUid + + ", creatorName=" + configuration.creatorName); + configuration.creatorName = creatorName; + } configuration.setNetworkSelectionStatus(status); configuration.setIpConfiguration(ipConfiguration); diff --git a/com/android/server/wifi/OpenNetworkNotifier.java b/com/android/server/wifi/OpenNetworkNotifier.java index 31ff44b7..eee4ac53 100644 --- a/com/android/server/wifi/OpenNetworkNotifier.java +++ b/com/android/server/wifi/OpenNetworkNotifier.java @@ -40,11 +40,13 @@ import android.os.Messenger; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; +import com.android.server.wifi.nano.WifiMetricsProto.ConnectToNetworkNotificationAndActionCount; import com.android.server.wifi.util.ScanResultUtil; import java.io.FileDescriptor; @@ -124,6 +126,7 @@ public class OpenNetworkNotifier { private final Context mContext; private final Handler mHandler; private final FrameworkFacade mFrameworkFacade; + private final WifiMetrics mWifiMetrics; private final Clock mClock; private final WifiConfigManager mConfigManager; private final WifiStateMachine mWifiStateMachine; @@ -138,6 +141,7 @@ public class OpenNetworkNotifier { Looper looper, FrameworkFacade framework, Clock clock, + WifiMetrics wifiMetrics, WifiConfigManager wifiConfigManager, WifiConfigStore wifiConfigStore, WifiStateMachine wifiStateMachine, @@ -146,6 +150,7 @@ public class OpenNetworkNotifier { mContext = context; mHandler = new Handler(looper); mFrameworkFacade = framework; + mWifiMetrics = wifiMetrics; mClock = clock; mConfigManager = wifiConfigManager; mWifiStateMachine = wifiStateMachine; @@ -206,7 +211,7 @@ public class OpenNetworkNotifier { case WifiManager.CONNECT_NETWORK_SUCCEEDED: break; case WifiManager.CONNECT_NETWORK_FAILED: - handleConnectionFailure(); + handleConnectionAttemptFailedToSend(); break; default: Log.e(TAG, "Unknown message " + msg.what); @@ -226,6 +231,13 @@ public class OpenNetworkNotifier { if (mState != STATE_NO_NOTIFICATION) { getNotificationManager().cancel(SystemMessage.NOTE_NETWORK_AVAILABLE); + + if (mRecommendedNetwork != null) { + Log.d(TAG, "Notification with state=" + + mState + + " was cleared for recommended network: " + + mRecommendedNetwork.SSID); + } mState = STATE_NO_NOTIFICATION; mRecommendedNetwork = null; } @@ -295,6 +307,10 @@ public class OpenNetworkNotifier { postNotification(mNotificationBuilder.createNetworkConnectedNotification( mRecommendedNetwork)); + + Log.d(TAG, "User connected to recommended network: " + mRecommendedNetwork.SSID); + mWifiMetrics.incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTED_TO_NETWORK); mState = STATE_CONNECTED_NOTIFICATION; mHandler.postDelayed( () -> { @@ -313,6 +329,10 @@ public class OpenNetworkNotifier { return; } postNotification(mNotificationBuilder.createNetworkFailedNotification()); + + Log.d(TAG, "User failed to connect to recommended network: " + mRecommendedNetwork.SSID); + mWifiMetrics.incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_FAILED_TO_CONNECT); mState = STATE_CONNECT_FAILED_NOTIFICATION; mHandler.postDelayed( () -> { @@ -328,8 +348,18 @@ public class OpenNetworkNotifier { } private void postInitialNotification(ScanResult recommendedNetwork) { + if (mRecommendedNetwork != null + && TextUtils.equals(mRecommendedNetwork.SSID, recommendedNetwork.SSID)) { + return; + } postNotification(mNotificationBuilder.createConnectToNetworkNotification( recommendedNetwork)); + if (mState == STATE_NO_NOTIFICATION) { + mWifiMetrics.incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_RECOMMEND_NETWORK); + } else { + mWifiMetrics.incrementNumOpenNetworkRecommendationUpdates(); + } mState = STATE_SHOWING_RECOMMENDATION_NOTIFICATION; mRecommendedNetwork = recommendedNetwork; mNotificationRepeatTime = mClock.getWallClockMillis() + mNotificationRepeatDelay; @@ -340,11 +370,15 @@ public class OpenNetworkNotifier { } private void handleConnectToNetworkAction() { + mWifiMetrics.incrementConnectToNetworkNotificationAction(mState, + ConnectToNetworkNotificationAndActionCount.ACTION_CONNECT_TO_NETWORK); if (mState != STATE_SHOWING_RECOMMENDATION_NOTIFICATION) { return; } postNotification(mNotificationBuilder.createNetworkConnectingNotification( mRecommendedNetwork)); + mWifiMetrics.incrementConnectToNetworkNotification( + ConnectToNetworkNotificationAndActionCount.NOTIFICATION_CONNECTING_TO_NETWORK); Log.d(TAG, "User initiated connection to recommended network: " + mRecommendedNetwork.SSID); WifiConfiguration network = ScanResultUtil.createNetworkFromScanResult(mRecommendedNetwork); @@ -366,6 +400,8 @@ public class OpenNetworkNotifier { } private void handleSeeAllNetworksAction() { + mWifiMetrics.incrementConnectToNetworkNotificationAction(mState, + ConnectToNetworkNotificationAndActionCount.ACTION_PICK_WIFI_NETWORK); startWifiSettings(); } @@ -378,14 +414,26 @@ public class OpenNetworkNotifier { clearPendingNotification(false /* resetRepeatTime */); } + private void handleConnectionAttemptFailedToSend() { + handleConnectionFailure(); + mWifiMetrics.incrementNumOpenNetworkConnectMessageFailedToSend(); + } + private void handlePickWifiNetworkAfterConnectFailure() { + mWifiMetrics.incrementConnectToNetworkNotificationAction(mState, + ConnectToNetworkNotificationAndActionCount + .ACTION_PICK_WIFI_NETWORK_AFTER_CONNECT_FAILURE); startWifiSettings(); } private void handleUserDismissedAction() { + Log.d(TAG, "User dismissed notification with state=" + mState); + mWifiMetrics.incrementConnectToNetworkNotificationAction(mState, + ConnectToNetworkNotificationAndActionCount.ACTION_USER_DISMISSED_NOTIFICATION); if (mState == STATE_SHOWING_RECOMMENDATION_NOTIFICATION) { // blacklist dismissed network mBlacklistedSsids.add(mRecommendedNetwork.SSID); + mWifiMetrics.setOpenNetworkRecommenderBlacklistSize(mBlacklistedSsids.size()); mConfigManager.saveToStore(false /* forceWrite */); Log.d(TAG, "Network is added to the open network notification blacklist: " + mRecommendedNetwork.SSID); @@ -418,6 +466,7 @@ public class OpenNetworkNotifier { @Override public void setSsids(Set<String> ssidList) { mBlacklistedSsids.addAll(ssidList); + mWifiMetrics.setOpenNetworkRecommenderBlacklistSize(mBlacklistedSsids.size()); } } @@ -440,8 +489,10 @@ public class OpenNetworkNotifier { } private boolean getValue() { - return mFrameworkFacade.getIntegerSetting(mContext, + boolean enabled = mFrameworkFacade.getIntegerSetting(mContext, Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1; + mWifiMetrics.setIsWifiNetworksAvailableNotificationEnabled(enabled); + return enabled; } } } diff --git a/com/android/server/wifi/VelocityBasedConnectedScore.java b/com/android/server/wifi/VelocityBasedConnectedScore.java new file mode 100644 index 00000000..9d90332e --- /dev/null +++ b/com/android/server/wifi/VelocityBasedConnectedScore.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2017 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.wifi; + +import android.content.Context; +import android.net.wifi.WifiInfo; + +import com.android.internal.R; +import com.android.server.wifi.util.KalmanFilter; +import com.android.server.wifi.util.Matrix; + +/** + * Class used to calculate scores for connected wifi networks and report it to the associated + * network agent. + */ +public class VelocityBasedConnectedScore extends ConnectedScore { + + // Device configs. The values are examples. + private final int mThresholdMinimumRssi5; // -82 + private final int mThresholdMinimumRssi24; // -85 + + private int mFrequency = 5000; + private int mRssi = 0; + private final KalmanFilter mFilter; + private long mLastMillis; + + public VelocityBasedConnectedScore(Context context, Clock clock) { + super(clock); + mThresholdMinimumRssi5 = context.getResources().getInteger( + R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz); + mThresholdMinimumRssi24 = context.getResources().getInteger( + R.integer.config_wifi_framework_wifi_score_bad_rssi_threshold_24GHz); + mFilter = new KalmanFilter(); + mFilter.mH = new Matrix(2, new double[]{1.0, 0.0}); + mFilter.mR = new Matrix(1, new double[]{1.0}); + } + + /** + * Set the Kalman filter's state transition matrix F and process noise covariance Q given + * a time step. + * + * @param dt delta time, in seconds + */ + private void setDeltaTimeSeconds(double dt) { + mFilter.mF = new Matrix(2, new double[]{1.0, dt, 0.0, 1.0}); + Matrix tG = new Matrix(1, new double[]{0.5 * dt * dt, dt}); + double stda = 0.02; // standard deviation of modelled acceleration + mFilter.mQ = tG.dotTranspose(tG).dot(new Matrix(2, new double[]{ + stda * stda, 0.0, + 0.0, stda * stda})); + } + /** + * Reset the filter state. + */ + @Override + public void reset() { + mLastMillis = 0; + } + + /** + * Updates scoring state using RSSI and measurement noise estimate + * <p> + * This is useful if an RSSI comes from another source (e.g. scan results) and the + * expected noise varies by source. + * + * @param rssi signal strength (dB). + * @param millis millisecond-resolution time. + * @param standardDeviation of the RSSI. + */ + @Override + public void updateUsingRssi(int rssi, long millis, double standardDeviation) { + if (millis <= 0) return; + if (mLastMillis <= 0 || millis < mLastMillis) { + double initialVariance = 9.0 * standardDeviation * standardDeviation; + mFilter.mx = new Matrix(1, new double[]{rssi, 0.0}); + mFilter.mP = new Matrix(2, new double[]{initialVariance, 0.0, 0.0, 0.0}); + mLastMillis = millis; + return; + } + double dt = (millis - mLastMillis) * 0.001; + mFilter.mR.put(0, 0, standardDeviation * standardDeviation); + setDeltaTimeSeconds(dt); + mFilter.predict(); + mLastMillis = millis; + mFilter.update(new Matrix(1, new double[]{rssi})); + } + + /** + * Updates the state. + */ + @Override + public void updateUsingWifiInfo(WifiInfo wifiInfo, long millis) { + int frequency = wifiInfo.getFrequency(); + if (frequency != mFrequency) { + reset(); // Probably roamed + mFrequency = frequency; + } + updateUsingRssi(wifiInfo.getRssi(), millis, mDefaultRssiStandardDeviation); + } + + /** + * Velocity scorer - predict the rssi a few seconds from now + */ + @Override + public int generateScore() { + int badRssi = mFrequency >= 5000 ? mThresholdMinimumRssi5 : mThresholdMinimumRssi24; + double horizonSeconds = 15.0; + Matrix x = new Matrix(mFilter.mx); + double filteredRssi = x.get(0, 0); + setDeltaTimeSeconds(horizonSeconds); + x = mFilter.mF.dot(x); + double forecastRssi = x.get(0, 0); + if (forecastRssi > filteredRssi) { + forecastRssi = filteredRssi; // Be pessimistic about predicting an actual increase + } + int score = (int) (Math.round(forecastRssi) - badRssi) + WIFI_TRANSITION_SCORE; + return score; + } +} diff --git a/com/android/server/wifi/WifiBackupRestore.java b/com/android/server/wifi/WifiBackupRestore.java index 60c3b488..ae5e411f 100644 --- a/com/android/server/wifi/WifiBackupRestore.java +++ b/com/android/server/wifi/WifiBackupRestore.java @@ -228,9 +228,8 @@ public class WifiBackupRestore { } return parseNetworkConfigurationsFromXml(in, rootTagDepth, version); - } catch (XmlPullParserException e) { - Log.e(TAG, "Error parsing the backup data: " + e); - } catch (IOException e) { + } catch (XmlPullParserException | IOException | ClassCastException + | IllegalArgumentException e) { Log.e(TAG, "Error parsing the backup data: " + e); } return null; diff --git a/com/android/server/wifi/WifiConfigManager.java b/com/android/server/wifi/WifiConfigManager.java index f8b33cbb..ba1695a5 100644 --- a/com/android/server/wifi/WifiConfigManager.java +++ b/com/android/server/wifi/WifiConfigManager.java @@ -2333,12 +2333,13 @@ public class WifiConfigManager { public List<WifiScanner.PnoSettings.PnoNetwork> retrievePnoNetworkList() { List<WifiScanner.PnoSettings.PnoNetwork> pnoList = new ArrayList<>(); List<WifiConfiguration> networks = new ArrayList<>(getInternalConfiguredNetworks()); - // Remove any permanently disabled networks. + // Remove any permanently or temporarily disabled networks. Iterator<WifiConfiguration> iter = networks.iterator(); while (iter.hasNext()) { WifiConfiguration config = iter.next(); if (config.ephemeral || config.isPasspoint() - || config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled()) { + || config.getNetworkSelectionStatus().isNetworkPermanentlyDisabled() + || config.getNetworkSelectionStatus().isNetworkTemporaryDisabled()) { iter.remove(); } } @@ -2574,10 +2575,12 @@ public class WifiConfigManager { * @param userId The identifier of the user that stopped. */ public void handleUserStop(int userId) { + if (mVerboseLoggingEnabled) { + Log.v(TAG, "Handling user stop for " + userId); + } if (userId == mCurrentUserId && mUserManager.isUserUnlockingOrUnlocked(mCurrentUserId)) { saveToStore(true); - clearInternalData(); - mCurrentUserId = UserHandle.USER_SYSTEM; + clearInternalUserData(mCurrentUserId); } } @@ -2589,6 +2592,7 @@ public class WifiConfigManager { * - List of deleted ephemeral networks. */ private void clearInternalData() { + localLog("clearInternalData: Clearing all internal data"); mConfiguredNetworks.clear(); mDeletedEphemeralSSIDs.clear(); mScanDetailCaches.clear(); @@ -2607,12 +2611,16 @@ public class WifiConfigManager { * removed from memory. */ private Set<Integer> clearInternalUserData(int userId) { + localLog("clearInternalUserData: Clearing user internal data for " + userId); Set<Integer> removedNetworkIds = new HashSet<>(); // Remove any private networks of the old user before switching the userId. for (WifiConfiguration config : getInternalConfiguredNetworks()) { if (!config.shared && WifiConfigurationUtil.doesUidBelongToAnyProfile( config.creatorUid, mUserManager.getProfiles(userId))) { removedNetworkIds.add(config.networkId); + localLog("clearInternalUserData: removed config." + + " netId=" + config.networkId + + " configKey=" + config.configKey()); mConfiguredNetworks.remove(config.networkId); } } diff --git a/com/android/server/wifi/WifiConnectivityManager.java b/com/android/server/wifi/WifiConnectivityManager.java index 7e730c83..458f73ae 100644 --- a/com/android/server/wifi/WifiConnectivityManager.java +++ b/com/android/server/wifi/WifiConnectivityManager.java @@ -30,6 +30,8 @@ import android.net.wifi.WifiScanner.PnoSettings; import android.net.wifi.WifiScanner.ScanSettings; import android.os.Handler; import android.os.Looper; +import android.os.Process; +import android.os.WorkSource; import android.util.LocalLog; import android.util.Log; @@ -214,7 +216,7 @@ public class WifiConnectivityManager { @Override public void onAlarm() { - startSingleScan(mIsFullBandScan); + startSingleScan(mIsFullBandScan, WIFI_WORK_SOURCE); } } @@ -741,7 +743,7 @@ public class WifiConnectivityManager { localLog("connectToNetwork: Connect to " + targetAssociationId + " from " + currentAssociationId); } - mStateMachine.startConnectToNetwork(candidate.networkId, targetBssid); + mStateMachine.startConnectToNetwork(candidate.networkId, Process.WIFI_UID, targetBssid); } } @@ -794,7 +796,7 @@ public class WifiConnectivityManager { localLog("start a single scan from watchdogHandler"); scheduleWatchdogTimer(); - startSingleScan(true); + startSingleScan(true, WIFI_WORK_SOURCE); } } @@ -823,7 +825,7 @@ public class WifiConnectivityManager { } mLastPeriodicSingleScanTimeStamp = currentTimeStamp; - startSingleScan(isFullBandScan); + startSingleScan(isFullBandScan, WIFI_WORK_SOURCE); schedulePeriodicScanTimer(mPeriodicSingleScanInterval); // Set up the next scan interval in an exponential backoff fashion. @@ -850,7 +852,7 @@ public class WifiConnectivityManager { } // Start a single scan - private void startSingleScan(boolean isFullBandScan) { + private void startSingleScan(boolean isFullBandScan, WorkSource workSource) { if (!mWifiEnabled || !mWifiConnectivityManagerEnabled) { return; } @@ -875,7 +877,7 @@ public class WifiConnectivityManager { SingleScanListener singleScanListener = new SingleScanListener(isFullBandScan); - mScanner.startScan(settings, singleScanListener, WIFI_WORK_SOURCE); + mScanner.startScan(settings, singleScanListener, workSource); } // Start a periodic scan when screen is on @@ -1132,11 +1134,11 @@ public class WifiConnectivityManager { /** * Handler for on-demand connectivity scan */ - public void forceConnectivityScan() { - localLog("forceConnectivityScan"); + public void forceConnectivityScan(WorkSource workSource) { + localLog("forceConnectivityScan in request of " + workSource); mWaitForFullBandScanResults = true; - startSingleScan(true); + startSingleScan(true, workSource); } /** diff --git a/com/android/server/wifi/WifiCountryCode.java b/com/android/server/wifi/WifiCountryCode.java index e69fb8e1..66a035f0 100644 --- a/com/android/server/wifi/WifiCountryCode.java +++ b/com/android/server/wifi/WifiCountryCode.java @@ -83,11 +83,9 @@ public class WifiCountryCode { public synchronized void simCardRemoved() { if (DBG) Log.d(TAG, "SIM Card Removed"); // SIM card is removed, we need to reset the country code to phone default. - if (mRevertCountryCodeOnCellularLoss) { - mTelephonyCountryCode = null; - if (mReady) { - updateCountryCode(); - } + mTelephonyCountryCode = null; + if (mReady) { + updateCountryCode(); } } @@ -98,12 +96,9 @@ public class WifiCountryCode { */ public synchronized void airplaneModeEnabled() { if (DBG) Log.d(TAG, "Airplane Mode Enabled"); - mTelephonyCountryCode = null; // Airplane mode is enabled, we need to reset the country code to phone default. - if (mRevertCountryCodeOnCellularLoss) { - mTelephonyCountryCode = null; - // Country code will be set upon when wpa_supplicant starts next time. - } + // Country code will be set upon when wpa_supplicant starts next time. + mTelephonyCountryCode = null; } /** @@ -133,8 +128,10 @@ public class WifiCountryCode { if (DBG) Log.d(TAG, "Receive set country code request: " + countryCode); // Empty country code. if (TextUtils.isEmpty(countryCode)) { - if (DBG) Log.d(TAG, "Received empty country code, reset to default country code"); - mTelephonyCountryCode = null; + if (mRevertCountryCodeOnCellularLoss) { + if (DBG) Log.d(TAG, "Received empty country code, reset to default country code"); + mTelephonyCountryCode = null; + } } else { mTelephonyCountryCode = countryCode.toUpperCase(); } diff --git a/com/android/server/wifi/WifiInjector.java b/com/android/server/wifi/WifiInjector.java index fc3af83e..1cbb29ef 100644 --- a/com/android/server/wifi/WifiInjector.java +++ b/com/android/server/wifi/WifiInjector.java @@ -116,6 +116,7 @@ public class WifiInjector { private final PasspointManager mPasspointManager; private final SIMAccessor mSimAccessor; private HandlerThread mWifiAwareHandlerThread; + private HandlerThread mRttHandlerThread; private HalDeviceManager mHalDeviceManager; private final IBatteryStats mBatteryStats; private final WifiStateTracker mWifiStateTracker; @@ -197,7 +198,7 @@ public class WifiInjector { mWifiConfigManager = new WifiConfigManager(mContext, mClock, UserManager.get(mContext), TelephonyManager.from(mContext), mWifiKeyStore, mWifiConfigStore, mWifiPermissionsUtil, - mWifiPermissionsWrapper, new NetworkListStoreData(), + mWifiPermissionsWrapper, new NetworkListStoreData(mContext), new DeletedEphemeralSsidsStoreData()); mWifiMetrics.setWifiConfigManager(mWifiConfigManager); mWifiConnectivityHelper = new WifiConnectivityHelper(mWifiNative); @@ -225,7 +226,7 @@ public class WifiInjector { new WrongPasswordNotifier(mContext, mFrameworkFacade)); mCertManager = new WifiCertManager(mContext); mOpenNetworkNotifier = new OpenNetworkNotifier(mContext, - mWifiStateMachineHandlerThread.getLooper(), mFrameworkFacade, mClock, + mWifiStateMachineHandlerThread.getLooper(), mFrameworkFacade, mClock, mWifiMetrics, mWifiConfigManager, mWifiConfigStore, mWifiStateMachine, new OpenNetworkRecommender(), new ConnectToNetworkNotificationBuilder(mContext, mFrameworkFacade)); @@ -455,6 +456,19 @@ public class WifiInjector { } /** + * Returns a singleton instance of a HandlerThread for injection. Uses lazy initialization. + * + * TODO: share worker thread with other Wi-Fi handlers (b/27924886) + */ + public HandlerThread getRttHandlerThread() { + if (mRttHandlerThread == null) { // lazy initialization + mRttHandlerThread = new HandlerThread("wifiRttService"); + mRttHandlerThread.start(); + } + return mRttHandlerThread; + } + + /** * Returns a single instance of HalDeviceManager for injection. */ public HalDeviceManager getHalDeviceManager() { diff --git a/com/android/server/wifi/WifiMetrics.java b/com/android/server/wifi/WifiMetrics.java index 5db5ee67..071b4f88 100644 --- a/com/android/server/wifi/WifiMetrics.java +++ b/com/android/server/wifi/WifiMetrics.java @@ -37,6 +37,7 @@ import com.android.server.wifi.hotspot2.PasspointManager; import com.android.server.wifi.hotspot2.PasspointMatch; import com.android.server.wifi.hotspot2.PasspointProvider; import com.android.server.wifi.nano.WifiMetricsProto; +import com.android.server.wifi.nano.WifiMetricsProto.ConnectToNetworkNotificationAndActionCount; import com.android.server.wifi.nano.WifiMetricsProto.PnoScanMetrics; import com.android.server.wifi.nano.WifiMetricsProto.StaEvent; import com.android.server.wifi.nano.WifiMetricsProto.StaEvent.ConfigInfo; @@ -83,6 +84,7 @@ public class WifiMetrics { public static final int MAX_CONNECTABLE_BSSID_NETWORK_BUCKET = 50; public static final int MAX_TOTAL_SCAN_RESULT_SSIDS_BUCKET = 100; public static final int MAX_TOTAL_SCAN_RESULTS_BUCKET = 250; + private static final int CONNECT_TO_NETWORK_NOTIFICATION_ACTION_KEY_MULTIPLIER = 1000; private Clock mClock; private boolean mScreenOn; private int mWifiState; @@ -150,6 +152,15 @@ public class WifiMetrics { private final SparseIntArray mAvailableSavedPasspointProviderBssidsInScanHistogram = new SparseIntArray(); + /** Mapping of "Connect to Network" notifications to counts. */ + private final SparseIntArray mConnectToNetworkNotificationCount = new SparseIntArray(); + /** Mapping of "Connect to Network" notification user actions to counts. */ + private final SparseIntArray mConnectToNetworkNotificationActionCount = new SparseIntArray(); + private int mOpenNetworkRecommenderBlacklistSize = 0; + private boolean mIsWifiNetworksAvailableNotificationOn = false; + private int mNumOpenNetworkConnectMessageFailedToSend = 0; + private int mNumOpenNetworkRecommendationUpdates = 0; + class RouterFingerPrint { private WifiMetricsProto.RouterFingerPrint mRouterFingerPrintProto; RouterFingerPrint() { @@ -1237,6 +1248,55 @@ public class WifiMetrics { } } + /** Increments the occurence of a "Connect to Network" notification. */ + public void incrementConnectToNetworkNotification(int notificationType) { + synchronized (mLock) { + int count = mConnectToNetworkNotificationCount.get(notificationType); + mConnectToNetworkNotificationCount.put(notificationType, count + 1); + } + } + + /** Increments the occurence of an "Connect to Network" notification user action. */ + public void incrementConnectToNetworkNotificationAction(int notificationType, int actionType) { + synchronized (mLock) { + int key = notificationType * CONNECT_TO_NETWORK_NOTIFICATION_ACTION_KEY_MULTIPLIER + + actionType; + int count = mConnectToNetworkNotificationActionCount.get(key); + mConnectToNetworkNotificationActionCount.put(key, count + 1); + } + } + + /** + * Sets the number of SSIDs blacklisted from recommendation by the open network notification + * recommender. + */ + public void setOpenNetworkRecommenderBlacklistSize(int size) { + synchronized (mLock) { + mOpenNetworkRecommenderBlacklistSize = size; + } + } + + /** Sets if the available network notification feature is enabled. */ + public void setIsWifiNetworksAvailableNotificationEnabled(boolean enabled) { + synchronized (mLock) { + mIsWifiNetworksAvailableNotificationOn = enabled; + } + } + + /** Increments the occurence of connection attempts that were initiated unsuccessfully */ + public void incrementNumOpenNetworkRecommendationUpdates() { + synchronized (mLock) { + mNumOpenNetworkRecommendationUpdates++; + } + } + + /** Increments the occurence of connection attempts that were initiated unsuccessfully */ + public void incrementNumOpenNetworkConnectMessageFailedToSend() { + synchronized (mLock) { + mNumOpenNetworkConnectMessageFailedToSend++; + } + } + public static final String PROTO_DUMP_ARG = "wifiMetricsProto"; public static final String CLEAN_DUMP_ARG = "clean"; @@ -1488,6 +1548,19 @@ public class WifiMetrics { + mPnoScanMetrics.numPnoScanFailedOverOffload); pw.println("mPnoScanMetrics.numPnoFoundNetworkEvents=" + mPnoScanMetrics.numPnoFoundNetworkEvents); + + pw.println("mWifiLogProto.connectToNetworkNotificationCount=" + + mConnectToNetworkNotificationCount.toString()); + pw.println("mWifiLogProto.connectToNetworkNotificationActionCount=" + + mConnectToNetworkNotificationActionCount.toString()); + pw.println("mWifiLogProto.openNetworkRecommenderBlacklistSize=" + + mOpenNetworkRecommenderBlacklistSize); + pw.println("mWifiLogProto.isWifiNetworksAvailableNotificationOn=" + + mIsWifiNetworksAvailableNotificationOn); + pw.println("mWifiLogProto.numOpenNetworkRecommendationUpdates=" + + mNumOpenNetworkRecommendationUpdates); + pw.println("mWifiLogProto.numOpenNetworkConnectMessageFailedToSend=" + + mNumOpenNetworkConnectMessageFailedToSend); } } } @@ -1698,6 +1771,53 @@ public class WifiMetrics { mWifiLogProto.wifiAwareLog = mWifiAwareMetrics.consolidateProto(); mWifiLogProto.pnoScanMetrics = mPnoScanMetrics; + + /** + * Convert the SparseIntArray of "Connect to Network" notification types and counts to + * proto's repeated IntKeyVal array. + */ + ConnectToNetworkNotificationAndActionCount[] notificationCountArray = + new ConnectToNetworkNotificationAndActionCount[ + mConnectToNetworkNotificationCount.size()]; + for (int i = 0; i < mConnectToNetworkNotificationCount.size(); i++) { + ConnectToNetworkNotificationAndActionCount keyVal = + new ConnectToNetworkNotificationAndActionCount(); + keyVal.notification = mConnectToNetworkNotificationCount.keyAt(i); + keyVal.recommender = + ConnectToNetworkNotificationAndActionCount.RECOMMENDER_OPEN; + keyVal.count = mConnectToNetworkNotificationCount.valueAt(i); + notificationCountArray[i] = keyVal; + } + mWifiLogProto.connectToNetworkNotificationCount = notificationCountArray; + + /** + * Convert the SparseIntArray of "Connect to Network" notification types and counts to + * proto's repeated IntKeyVal array. + */ + ConnectToNetworkNotificationAndActionCount[] notificationActionCountArray = + new ConnectToNetworkNotificationAndActionCount[ + mConnectToNetworkNotificationActionCount.size()]; + for (int i = 0; i < mConnectToNetworkNotificationActionCount.size(); i++) { + ConnectToNetworkNotificationAndActionCount keyVal = + new ConnectToNetworkNotificationAndActionCount(); + int key = mConnectToNetworkNotificationActionCount.keyAt(i); + keyVal.notification = key / CONNECT_TO_NETWORK_NOTIFICATION_ACTION_KEY_MULTIPLIER; + keyVal.action = key % CONNECT_TO_NETWORK_NOTIFICATION_ACTION_KEY_MULTIPLIER; + keyVal.recommender = + ConnectToNetworkNotificationAndActionCount.RECOMMENDER_OPEN; + keyVal.count = mConnectToNetworkNotificationActionCount.valueAt(i); + notificationActionCountArray[i] = keyVal; + } + mWifiLogProto.connectToNetworkNotificationActionCount = notificationActionCountArray; + + mWifiLogProto.openNetworkRecommenderBlacklistSize = + mOpenNetworkRecommenderBlacklistSize; + mWifiLogProto.isWifiNetworksAvailableNotificationOn = + mIsWifiNetworksAvailableNotificationOn; + mWifiLogProto.numOpenNetworkRecommendationUpdates = + mNumOpenNetworkRecommendationUpdates; + mWifiLogProto.numOpenNetworkConnectMessageFailedToSend = + mNumOpenNetworkConnectMessageFailedToSend; } } @@ -1716,7 +1836,8 @@ public class WifiMetrics { } /** - * Clear all WifiMetrics, except for currentConnectionEvent. + * Clear all WifiMetrics, except for currentConnectionEvent and Open Network Notification + * feature enabled state, blacklist size. */ private void clear() { synchronized (mLock) { @@ -1747,6 +1868,10 @@ public class WifiMetrics { mAvailableSavedPasspointProviderProfilesInScanHistogram.clear(); mAvailableSavedPasspointProviderBssidsInScanHistogram.clear(); mPnoScanMetrics.clear(); + mConnectToNetworkNotificationCount.clear(); + mConnectToNetworkNotificationActionCount.clear(); + mNumOpenNetworkRecommendationUpdates = 0; + mNumOpenNetworkConnectMessageFailedToSend = 0; } } diff --git a/com/android/server/wifi/WifiNative.java b/com/android/server/wifi/WifiNative.java index 5b12a364..0b1719db 100644 --- a/com/android/server/wifi/WifiNative.java +++ b/com/android/server/wifi/WifiNative.java @@ -79,6 +79,10 @@ public class WifiNative { return mInterfaceName; } + public WifiVendorHal getVendorHal() { + return mWifiVendorHal; + } + /** * Enable verbose logging for all sub modules. */ diff --git a/com/android/server/wifi/WifiScoreReport.java b/com/android/server/wifi/WifiScoreReport.java index 894d57cf..324cdba8 100644 --- a/com/android/server/wifi/WifiScoreReport.java +++ b/com/android/server/wifi/WifiScoreReport.java @@ -49,11 +49,13 @@ public class WifiScoreReport { ConnectedScore mConnectedScore; ConnectedScore mAggressiveConnectedScore; + ConnectedScore mFancyConnectedScore; WifiScoreReport(Context context, WifiConfigManager wifiConfigManager, Clock clock) { mClock = clock; mConnectedScore = new LegacyConnectedScore(context, wifiConfigManager, clock); mAggressiveConnectedScore = new AggressiveConnectedScore(context, clock); + mFancyConnectedScore = new VelocityBasedConnectedScore(context, clock); } /** @@ -76,6 +78,7 @@ public class WifiScoreReport { } mConnectedScore.reset(); mAggressiveConnectedScore.reset(); + mFancyConnectedScore.reset(); if (mVerboseLoggingEnabled) Log.d(TAG, "reset"); } @@ -113,18 +116,20 @@ public class WifiScoreReport { int aggressiveHandover, WifiMetrics wifiMetrics) { int score; - long millis = mConnectedScore.getMillis(); + long millis = mClock.getWallClockMillis(); mConnectedScore.updateUsingWifiInfo(wifiInfo, millis); mAggressiveConnectedScore.updateUsingWifiInfo(wifiInfo, millis); + mFancyConnectedScore.updateUsingWifiInfo(wifiInfo, millis); int s0 = mConnectedScore.generateScore(); int s1 = mAggressiveConnectedScore.generateScore(); + int s2 = mFancyConnectedScore.generateScore(); if (aggressiveHandover == 0) { score = s0; } else { - score = s1; + score = s2; } //sanitize boundaries @@ -135,7 +140,7 @@ public class WifiScoreReport { score = 0; } - logLinkMetrics(wifiInfo, s0, s1); + logLinkMetrics(wifiInfo, millis, s0, s1, s2); //report score if (score != wifiInfo.score) { @@ -163,8 +168,7 @@ public class WifiScoreReport { /** * Data logging for dumpsys */ - private void logLinkMetrics(WifiInfo wifiInfo, int s0, int s1) { - long now = mClock.getWallClockMillis(); + private void logLinkMetrics(WifiInfo wifiInfo, long now, int s0, int s1, int s2) { if (now < FIRST_REASONABLE_WALL_CLOCK) return; double rssi = wifiInfo.getRssi(); int freq = wifiInfo.getFrequency(); @@ -176,10 +180,10 @@ public class WifiScoreReport { try { String timestamp = new SimpleDateFormat("MM-dd HH:mm:ss.SSS").format(new Date(now)); String s = String.format(Locale.US, // Use US to avoid comma/decimal confusion - "%s,%d,%.1f,%d,%d,%.2f,%.2f,%.2f,%.2f,%d,%d", + "%s,%d,%.1f,%d,%d,%.2f,%.2f,%.2f,%.2f,%d,%d,%d", timestamp, mSessionNumber, rssi, freq, linkSpeed, txSuccessRate, txRetriesRate, txBadRate, rxSuccessRate, - s0, s1); + s0, s1, s2); mLinkMetricsHistory.add(s); } catch (Exception e) { Log.e(TAG, "format problem", e); @@ -201,7 +205,7 @@ public class WifiScoreReport { * @param args unused */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("time,session,rssi,freq,linkspeed,tx_good,tx_retry,tx_bad,rx,s0,s1"); + pw.println("time,session,rssi,freq,linkspeed,tx_good,tx_retry,tx_bad,rx,s0,s1,s2"); for (String line : mLinkMetricsHistory) { pw.println(line); } diff --git a/com/android/server/wifi/WifiServiceImpl.java b/com/android/server/wifi/WifiServiceImpl.java index bb995b7d..c83f6c6b 100644 --- a/com/android/server/wifi/WifiServiceImpl.java +++ b/com/android/server/wifi/WifiServiceImpl.java @@ -1421,7 +1421,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { public void reconnect() { enforceChangePermission(); mLog.info("reconnect uid=%").c(Binder.getCallingUid()).flush(); - mWifiStateMachine.reconnectCommand(); + mWifiStateMachine.reconnectCommand(new WorkSource(Binder.getCallingUid())); } /** diff --git a/com/android/server/wifi/WifiStateMachine.java b/com/android/server/wifi/WifiStateMachine.java index faad0e2d..1f6cada7 100644 --- a/com/android/server/wifi/WifiStateMachine.java +++ b/com/android/server/wifi/WifiStateMachine.java @@ -1321,7 +1321,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss /** * Initiates connection to a network specified by the user/app. This method checks if the - * requesting app holds the WIFI_CONFIG_OVERRIDE permission. + * requesting app holds the NETWORK_SETTINGS permission. * * @param netId Id network to initiate connection. * @param uid UID of the app requesting the connection. @@ -1350,7 +1350,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss logi("connectToUserSelectNetwork already connecting/connected=" + netId); } else { mWifiConnectivityManager.prepareForForcedConnection(netId); - startConnectToNetwork(netId, SUPPLICANT_BSSID_ANY); + startConnectToNetwork(netId, uid, SUPPLICANT_BSSID_ANY); } return true; } @@ -1873,8 +1873,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss /** * Initiate a reconnection to AP */ - public void reconnectCommand() { - sendMessage(CMD_RECONNECT); + public void reconnectCommand(WorkSource workSource) { + sendMessage(CMD_RECONNECT, workSource); } /** @@ -3980,9 +3980,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss deleteNetworkConfigAndSendReply(message, true); break; case WifiManager.SAVE_NETWORK: - messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, - WifiManager.BUSY); + saveNetworkConfigAndSendReply(message); break; case WifiManager.START_WPS: replyToMessage(message, WifiManager.WPS_FAILED, @@ -4537,7 +4535,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss mEnableAutoJoinWhenAssociated = allowed; if (!old_state && allowed && mScreenOn && getCurrentState() == mConnectedState) { - mWifiConnectivityManager.forceConnectivityScan(); + mWifiConnectivityManager.forceConnectivityScan(WIFI_WORK_SOURCE); } break; case CMD_SELECT_TX_POWER_SCENARIO: @@ -4824,7 +4822,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss // Notify PasspointManager of Passpoint network connected event. WifiConfiguration currentNetwork = getCurrentWifiConfiguration(); - if (currentNetwork.isPasspoint()) { + if (currentNetwork != null && currentNetwork.isPasspoint()) { mPasspointManager.onPasspointNetworkConnected(currentNetwork.FQDN); } } @@ -5160,7 +5158,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss mPasspointManager.getMatchingOsuProviders((ScanResult) message.obj)); break; case CMD_RECONNECT: - mWifiConnectivityManager.forceConnectivityScan(); + WorkSource workSource = (WorkSource) message.obj; + mWifiConnectivityManager.forceConnectivityScan(workSource); break; case CMD_REASSOCIATE: lastConnectAttemptTimestamp = mClock.getWallClockMillis(); @@ -5180,7 +5179,23 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss case CMD_START_CONNECT: /* connect command coming from auto-join */ netId = message.arg1; + int uid = message.arg2; bssid = (String) message.obj; + + synchronized (mWifiReqCountLock) { + if (!hasConnectionRequests()) { + if (mNetworkAgent == null) { + loge("CMD_START_CONNECT but no requests and not connected," + + " bailing"); + break; + } else if (!mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) { + loge("CMD_START_CONNECT but no requests and connected, but app " + + "does not have sufficient permissions, bailing"); + break; + } + } + } + config = mWifiConfigManager.getConfiguredNetworkWithPassword(netId); logd("CMD_START_CONNECT sup state " + mSupplicantStateTracker.getSupplicantStateName() @@ -5270,41 +5285,17 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED); break; case WifiManager.SAVE_NETWORK: - config = (WifiConfiguration) message.obj; - mWifiConnectionStatistics.numWifiManagerJoinAttempt++; - if (config == null) { - loge("SAVE_NETWORK with null configuration" - + mSupplicantStateTracker.getSupplicantStateName() - + " my state " + getCurrentState().getName()); - messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, - WifiManager.ERROR); - break; - } - result = mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid); - if (!result.isSuccess()) { - loge("SAVE_NETWORK adding/updating config=" + config + " failed"); - messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, - WifiManager.ERROR); - break; - } - if (!mWifiConfigManager.enableNetwork( - result.getNetworkId(), false, message.sendingUid)) { - loge("SAVE_NETWORK enabling config=" + config + " failed"); - messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; - replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, - WifiManager.ERROR); - break; - } + result = saveNetworkConfigAndSendReply(message); netId = result.getNetworkId(); - if (mWifiInfo.getNetworkId() == netId) { + if (result.isSuccess() && mWifiInfo.getNetworkId() == netId) { + mWifiConnectionStatistics.numWifiManagerJoinAttempt++; if (result.hasCredentialChanged()) { + config = (WifiConfiguration) message.obj; // The network credentials changed and we're connected to this network, // start a new connection with the updated credentials. logi("SAVE_NETWORK credential changed for config=" + config.configKey() + ", Reconnecting."); - startConnectToNetwork(netId, SUPPLICANT_BSSID_ANY); + startConnectToNetwork(netId, message.sendingUid, SUPPLICANT_BSSID_ANY); } else { if (result.hasProxyChanged()) { log("Reconfiguring proxy on connection"); @@ -5322,8 +5313,6 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } } } - broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config); - replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED); break; case WifiManager.FORGET_NETWORK: if (!deleteNetworkConfigAndSendReply(message, true)) { @@ -5831,8 +5820,15 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss reportConnectionAttemptEnd( WifiMetrics.ConnectionEvent.FAILURE_NONE, WifiMetricsProto.ConnectionEvent.HLF_NONE); - sendConnectedState(); - transitionTo(mConnectedState); + if (getCurrentWifiConfiguration() == null) { + // The current config may have been removed while we were connecting, + // trigger a disconnect to clear up state. + mWifiNative.disconnect(); + transitionTo(mDisconnectingState); + } else { + sendConnectedState(); + transitionTo(mConnectedState); + } break; case CMD_IP_CONFIGURATION_LOST: // Get Link layer stats so that we get fresh tx packet counters. @@ -5999,6 +5995,9 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss case CMD_STOP_RSSI_MONITORING_OFFLOAD: stopRssiMonitoringOffload(); break; + case CMD_RECONNECT: + log(" Ignore CMD_RECONNECT request because wifi is already connected"); + break; case CMD_RESET_SIM_NETWORKS: if (message.arg1 == 0 // sim was removed && mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID) { @@ -6142,7 +6141,7 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss WifiConfiguration config = getCurrentWifiConfiguration(); if (shouldEvaluateWhetherToSendExplicitlySelected(config)) { boolean prompt = - mWifiPermissionsUtil.checkConfigOverridePermission(config.lastConnectUid); + mWifiPermissionsUtil.checkNetworkSettingsPermission(config.lastConnectUid); if (mVerboseLoggingEnabled) { log("Network selected by UID " + config.lastConnectUid + " prompt=" + prompt); } @@ -6851,6 +6850,8 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss case WifiManager.CONNECT_NETWORK: case CMD_ENABLE_NETWORK: case CMD_RECONNECT: + log(" Ignore CMD_RECONNECT request because wps is running"); + return HANDLED; case CMD_REASSOCIATE: deferMessage(message); break; @@ -7118,14 +7119,11 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss * Automatically connect to the network specified * * @param networkId ID of the network to connect to + * @param uid UID of the app triggering the connection. * @param bssid BSSID of the network */ - public void startConnectToNetwork(int networkId, String bssid) { - synchronized (mWifiReqCountLock) { - if (hasConnectionRequests()) { - sendMessage(CMD_START_CONNECT, networkId, 0, bssid); - } - } + public void startConnectToNetwork(int networkId, int uid, String bssid) { + sendMessage(CMD_START_CONNECT, networkId, uid, bssid); } /** @@ -7210,6 +7208,43 @@ public class WifiStateMachine extends StateMachine implements WifiNative.WifiRss } } + /** + * Private method to handle calling WifiConfigManager to add & enable network configs and reply + * to the message from the sender of the outcome. + * + * @return NetworkUpdateResult with networkId of the added/updated configuration. Will return + * {@link WifiConfiguration#INVALID_NETWORK_ID} in case of error. + */ + private NetworkUpdateResult saveNetworkConfigAndSendReply(Message message) { + WifiConfiguration config = (WifiConfiguration) message.obj; + if (config == null) { + loge("SAVE_NETWORK with null configuration " + + mSupplicantStateTracker.getSupplicantStateName() + + " my state " + getCurrentState().getName()); + messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; + replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR); + return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); + } + NetworkUpdateResult result = + mWifiConfigManager.addOrUpdateNetwork(config, message.sendingUid); + if (!result.isSuccess()) { + loge("SAVE_NETWORK adding/updating config=" + config + " failed"); + messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; + replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR); + return result; + } + if (!mWifiConfigManager.enableNetwork( + result.getNetworkId(), false, message.sendingUid)) { + loge("SAVE_NETWORK enabling config=" + config + " failed"); + messageHandlingStatus = MESSAGE_HANDLING_STATUS_FAIL; + replyToMessage(message, WifiManager.SAVE_NETWORK_FAILED, WifiManager.ERROR); + return new NetworkUpdateResult(WifiConfiguration.INVALID_NETWORK_ID); + } + broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_SAVED, config); + replyToMessage(message, WifiManager.SAVE_NETWORK_SUCCEEDED); + return result; + } + private static String getLinkPropertiesSummary(LinkProperties lp) { List<String> attributes = new ArrayList<>(6); if (lp.hasIPv4Address()) { diff --git a/com/android/server/wifi/WifiVendorHal.java b/com/android/server/wifi/WifiVendorHal.java index 12674aa2..e0467d78 100644 --- a/com/android/server/wifi/WifiVendorHal.java +++ b/com/android/server/wifi/WifiVendorHal.java @@ -67,6 +67,7 @@ import android.net.wifi.WifiWakeReasonAndCounts; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; +import android.util.Log; import android.util.MutableBoolean; import android.util.MutableInt; @@ -237,6 +238,11 @@ public class WifiVendorHal { clearState(); } + // TODO: b/65014872 remove - have RttService (RTT2) interact directly with HalDeviceManager + public IWifiRttController getRttController() { + return mIWifiRttController; + } + private WifiNative.VendorHalDeathEventHandler mDeathEventHandler; /** @@ -2582,8 +2588,25 @@ public class WifiVendorHal { // The problem here is that the two threads acquire the locks in opposite order. // If, for example, T2.2 executes between T1.2 and 1.4, then T1 and T2 // will be deadlocked. - eventHandler.onRingBufferData( - ringBufferStatus(status), NativeUtil.byteArrayFromArrayList(data)); + int sizeBefore = data.size(); + boolean conversionFailure = false; + try { + eventHandler.onRingBufferData( + ringBufferStatus(status), NativeUtil.byteArrayFromArrayList(data)); + int sizeAfter = data.size(); + if (sizeAfter != sizeBefore) { + conversionFailure = true; + } + } catch (ArrayIndexOutOfBoundsException e) { + conversionFailure = true; + } + if (conversionFailure) { + Log.wtf("WifiVendorHal", "Conversion failure detected in " + + "onDebugRingBufferDataAvailable. " + + "The input ArrayList |data| is potentially corrupted. " + + "Starting size=" + sizeBefore + ", " + + "final size=" + data.size()); + } }); } diff --git a/com/android/server/wifi/WificondControl.java b/com/android/server/wifi/WificondControl.java index 056777fd..b6104a8c 100644 --- a/com/android/server/wifi/WificondControl.java +++ b/com/android/server/wifi/WificondControl.java @@ -374,8 +374,13 @@ public class WificondControl { new InformationElementUtil.Capabilities(); capabilities.from(ies, result.capability); String flags = capabilities.generateCapabilitiesString(); - NetworkDetail networkDetail = - new NetworkDetail(bssid, ies, null, result.frequency); + NetworkDetail networkDetail; + try { + networkDetail = new NetworkDetail(bssid, ies, null, result.frequency); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Illegal argument for scan result with bssid: " + bssid, e); + continue; + } ScanDetail scanDetail = new ScanDetail(networkDetail, wifiSsid, bssid, flags, result.signalMbm / 100, result.frequency, result.tsf, ies, null); diff --git a/com/android/server/wifi/aware/WifiAwareClientState.java b/com/android/server/wifi/aware/WifiAwareClientState.java index 3570f1d7..987d49ef 100644 --- a/com/android/server/wifi/aware/WifiAwareClientState.java +++ b/com/android/server/wifi/aware/WifiAwareClientState.java @@ -20,7 +20,6 @@ import android.Manifest; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; -import android.net.wifi.RttManager; import android.net.wifi.aware.ConfigRequest; import android.net.wifi.aware.IWifiAwareEventCallback; import android.os.RemoteException; @@ -271,47 +270,6 @@ public class WifiAwareClientState { } /** - * Called on RTT success - forwards call to client. - */ - public void onRangingSuccess(int rangingId, RttManager.ParcelableRttResults results) { - if (VDBG) { - Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId + ", results=" + results); - } - try { - mCallback.onRangingSuccess(rangingId, results); - } catch (RemoteException e) { - Log.w(TAG, "onRangingSuccess: RemoteException - ignored: " + e); - } - } - - /** - * Called on RTT failure - forwards call to client. - */ - public void onRangingFailure(int rangingId, int reason, String description) { - if (VDBG) { - Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId + ", reason=" + reason - + ", description=" + description); - } - try { - mCallback.onRangingFailure(rangingId, reason, description); - } catch (RemoteException e) { - Log.w(TAG, "onRangingFailure: RemoteException - ignored: " + e); - } - } - - /** - * Called on RTT operation aborted - forwards call to client. - */ - public void onRangingAborted(int rangingId) { - if (VDBG) Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId); - try { - mCallback.onRangingAborted(rangingId); - } catch (RemoteException e) { - Log.w(TAG, "onRangingAborted: RemoteException - ignored: " + e); - } - } - - /** * Dump the internal state of the class. */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { diff --git a/com/android/server/wifi/aware/WifiAwareRttStateManager.java b/com/android/server/wifi/aware/WifiAwareRttStateManager.java deleted file mode 100644 index 9d0441f1..00000000 --- a/com/android/server/wifi/aware/WifiAwareRttStateManager.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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.wifi.aware; - -import android.content.Context; -import android.net.wifi.IRttManager; -import android.net.wifi.RttManager; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.util.Log; -import android.util.SparseArray; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.AsyncChannel; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.Arrays; - - -/** - * Manages interactions between the Aware and the RTT service. Duplicates some of the functionality - * of the RttManager. - */ -public class WifiAwareRttStateManager { - private static final String TAG = "WifiAwareRttStateMgr"; - - private static final boolean DBG = false; - private static final boolean VDBG = false; // STOPSHIP if true - - private final SparseArray<WifiAwareClientState> mPendingOperations = new SparseArray<>(); - private AsyncChannel mAsyncChannel; - private Context mContext; - - /** - * Initializes the connection to the RTT service. - */ - public void start(Context context, Looper looper) { - if (VDBG) Log.v(TAG, "start()"); - - IBinder b = ServiceManager.getService(Context.WIFI_RTT_SERVICE); - IRttManager service = IRttManager.Stub.asInterface(b); - if (service == null) { - Log.e(TAG, "start(): not able to get WIFI_RTT_SERVICE"); - return; - } - - startWithRttService(context, looper, service); - } - - /** - * Initializes the connection to the RTT service. - */ - @VisibleForTesting - public void startWithRttService(Context context, Looper looper, IRttManager service) { - Messenger messenger; - try { - messenger = service.getMessenger(null, new int[1]); - } catch (RemoteException e) { - Log.e(TAG, "start(): not able to getMessenger() of WIFI_RTT_SERVICE"); - return; - } - - mAsyncChannel = new AsyncChannel(); - mAsyncChannel.connect(context, new AwareRttHandler(looper), messenger); - mContext = context; - } - - private WifiAwareClientState getAndRemovePendingOperationClient(int rangingId) { - WifiAwareClientState client = mPendingOperations.get(rangingId); - mPendingOperations.delete(rangingId); - return client; - } - - /** - * Start a ranging operation for the client + peer MAC. - */ - public void startRanging(int rangingId, WifiAwareClientState client, - RttManager.RttParams[] params) { - if (VDBG) { - Log.v(TAG, "startRanging: rangingId=" + rangingId + ", parms=" - + Arrays.toString(params)); - } - - if (mAsyncChannel == null) { - Log.d(TAG, "startRanging(): AsyncChannel to RTT service not configured - failing"); - client.onRangingFailure(rangingId, RttManager.REASON_NOT_AVAILABLE, - "Aware service not able to configure connection to RTT service"); - return; - } - - mPendingOperations.put(rangingId, client); - RttManager.ParcelableRttParams pparams = new RttManager.ParcelableRttParams(params); - mAsyncChannel.sendMessage(RttManager.CMD_OP_START_RANGING, 0, rangingId, pparams); - } - - private class AwareRttHandler extends Handler { - AwareRttHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - if (VDBG) Log.v(TAG, "handleMessage(): " + msg.what); - - // channel configuration messages - switch (msg.what) { - case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: - if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { - mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION, - new RttManager.RttClient(mContext.getPackageName())); - } else { - Log.e(TAG, "Failed to set up channel connection to RTT service"); - mAsyncChannel = null; - } - return; - case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED: - /* NOP */ - return; - case AsyncChannel.CMD_CHANNEL_DISCONNECTED: - Log.e(TAG, "Channel connection to RTT service lost"); - mAsyncChannel = null; - return; - } - - // RTT-specific messages - WifiAwareClientState client = getAndRemovePendingOperationClient(msg.arg2); - if (client == null) { - Log.e(TAG, "handleMessage(): RTT message (" + msg.what - + ") -- cannot find registered pending operation client for ID " - + msg.arg2); - return; - } - - switch (msg.what) { - case RttManager.CMD_OP_SUCCEEDED: { - int rangingId = msg.arg2; - RttManager.ParcelableRttResults results = (RttManager.ParcelableRttResults) - msg.obj; - if (VDBG) { - Log.v(TAG, "CMD_OP_SUCCEEDED: rangingId=" + rangingId + ", results=" - + results); - } - for (int i = 0; i < results.mResults.length; ++i) { - /* - * TODO: store peer ID rather than null in the return result. - */ - results.mResults[i].bssid = null; - } - client.onRangingSuccess(rangingId, results); - break; - } - case RttManager.CMD_OP_FAILED: { - int rangingId = msg.arg2; - int reason = msg.arg1; - String description = ((Bundle) msg.obj).getString(RttManager.DESCRIPTION_KEY); - if (VDBG) { - Log.v(TAG, "CMD_OP_FAILED: rangingId=" + rangingId + ", reason=" + reason - + ", description=" + description); - } - client.onRangingFailure(rangingId, reason, description); - break; - } - case RttManager.CMD_OP_ABORTED: { - int rangingId = msg.arg2; - if (VDBG) { - Log.v(TAG, "CMD_OP_ABORTED: rangingId=" + rangingId); - } - client.onRangingAborted(rangingId); - break; - } - default: - Log.e(TAG, "handleMessage(): ignoring message " + msg.what); - break; - } - } - } - - /** - * Dump the internal state of the class. - */ - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("WifiAwareRttStateManager:"); - pw.println(" mPendingOperations: [" + mPendingOperations + "]"); - } -} diff --git a/com/android/server/wifi/aware/WifiAwareServiceImpl.java b/com/android/server/wifi/aware/WifiAwareServiceImpl.java index b77ae635..421d9ac4 100644 --- a/com/android/server/wifi/aware/WifiAwareServiceImpl.java +++ b/com/android/server/wifi/aware/WifiAwareServiceImpl.java @@ -16,15 +16,16 @@ package com.android.server.wifi.aware; +import android.Manifest; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.wifi.V1_0.NanStatusType; -import android.net.wifi.RttManager; import android.net.wifi.aware.Characteristics; import android.net.wifi.aware.ConfigRequest; import android.net.wifi.aware.DiscoverySession; import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback; import android.net.wifi.aware.IWifiAwareEventCallback; +import android.net.wifi.aware.IWifiAwareMacAddressProvider; import android.net.wifi.aware.IWifiAwareManager; import android.net.wifi.aware.PublishConfig; import android.net.wifi.aware.SubscribeConfig; @@ -42,7 +43,7 @@ import com.android.server.wifi.util.WifiPermissionsWrapper; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.Arrays; +import java.util.List; /** * Implementation of the IWifiAwareManager AIDL interface. Performs validity @@ -63,7 +64,6 @@ public class WifiAwareServiceImpl extends IWifiAwareManager.Stub { private final SparseArray<IBinder.DeathRecipient> mDeathRecipientsByClientId = new SparseArray<>(); private int mNextClientId = 1; - private int mNextRangingId = 1; private final SparseIntArray mUidByClientId = new SparseIntArray(); public WifiAwareServiceImpl(Context context) { @@ -134,7 +134,7 @@ public class WifiAwareServiceImpl extends IWifiAwareManager.Stub { } if (configRequest != null) { - enforceConnectivityInternalPermission(); + enforceNetworkStackPermission(); } else { configRequest = new ConfigRequest.Builder().build(); } @@ -326,7 +326,7 @@ public class WifiAwareServiceImpl extends IWifiAwareManager.Stub { enforceChangePermission(); if (retryCount != 0) { - enforceConnectivityInternalPermission(); + enforceNetworkStackPermission(); } if (message != null @@ -352,30 +352,10 @@ public class WifiAwareServiceImpl extends IWifiAwareManager.Stub { } @Override - public int startRanging(int clientId, int sessionId, RttManager.ParcelableRttParams params) { - enforceAccessPermission(); - enforceLocationPermission(); + public void requestMacAddresses(int uid, List peerIds, IWifiAwareMacAddressProvider callback) { + enforceNetworkStackPermission(); - // TODO: b/35676064 restricts access to this API until decide if will open. - enforceConnectivityInternalPermission(); - - int uid = getMockableCallingUid(); - enforceClientValidity(uid, clientId); - if (VDBG) { - Log.v(TAG, "startRanging: clientId=" + clientId + ", sessionId=" + sessionId + ", " - + ", parms=" + Arrays.toString(params.mParams)); - } - - if (params.mParams.length == 0) { - throw new IllegalArgumentException("Empty ranging parameters"); - } - - int rangingId; - synchronized (mLock) { - rangingId = mNextRangingId++; - } - mStateManager.startRanging(clientId, sessionId, params.mParams, rangingId); - return rangingId; + mStateManager.requestMacAddresses(uid, peerIds, callback); } @Override @@ -424,8 +404,7 @@ public class WifiAwareServiceImpl extends IWifiAwareManager.Stub { TAG); } - private void enforceConnectivityInternalPermission() { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL, - TAG); + private void enforceNetworkStackPermission() { + mContext.enforceCallingOrSelfPermission(Manifest.permission.NETWORK_STACK, TAG); } } diff --git a/com/android/server/wifi/aware/WifiAwareStateManager.java b/com/android/server/wifi/aware/WifiAwareStateManager.java index 6ced9486..31bdff8c 100644 --- a/com/android/server/wifi/aware/WifiAwareStateManager.java +++ b/com/android/server/wifi/aware/WifiAwareStateManager.java @@ -21,11 +21,11 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.hardware.wifi.V1_0.NanStatusType; -import android.net.wifi.RttManager; import android.net.wifi.aware.Characteristics; import android.net.wifi.aware.ConfigRequest; import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback; import android.net.wifi.aware.IWifiAwareEventCallback; +import android.net.wifi.aware.IWifiAwareMacAddressProvider; import android.net.wifi.aware.PublishConfig; import android.net.wifi.aware.SubscribeConfig; import android.net.wifi.aware.WifiAwareManager; @@ -48,7 +48,6 @@ import com.android.internal.util.MessageUtils; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.WakeupMessage; -import com.android.server.wifi.util.NativeUtil; import com.android.server.wifi.util.WifiPermissionsWrapper; import libcore.util.HexEncoding; @@ -62,6 +61,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; /** @@ -108,7 +108,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe private static final int COMMAND_TYPE_ENQUEUE_SEND_MESSAGE = 107; private static final int COMMAND_TYPE_ENABLE_USAGE = 108; private static final int COMMAND_TYPE_DISABLE_USAGE = 109; - private static final int COMMAND_TYPE_START_RANGING = 110; private static final int COMMAND_TYPE_GET_CAPABILITIES = 111; private static final int COMMAND_TYPE_CREATE_ALL_DATA_PATH_INTERFACES = 112; private static final int COMMAND_TYPE_DELETE_ALL_DATA_PATH_INTERFACES = 113; @@ -167,7 +166,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe private static final String MESSAGE_BUNDLE_KEY_MAC_ADDRESS = "mac_address"; private static final String MESSAGE_BUNDLE_KEY_MESSAGE_DATA = "message_data"; private static final String MESSAGE_BUNDLE_KEY_REQ_INSTANCE_ID = "req_instance_id"; - private static final String MESSAGE_BUNDLE_KEY_RANGING_ID = "ranging_id"; private static final String MESSAGE_BUNDLE_KEY_SEND_MESSAGE_ENQUEUE_TIME = "message_queue_time"; private static final String MESSAGE_BUNDLE_KEY_RETRY_COUNT = "retry_count"; private static final String MESSAGE_BUNDLE_KEY_SUCCESS_FLAG = "success_flag"; @@ -203,7 +201,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe private volatile Capabilities mCapabilities; private volatile Characteristics mCharacteristics = null; private WifiAwareStateMachine mSm; - private WifiAwareRttStateManager mRtt; public WifiAwareDataPathStateManager mDataPathMgr; private PowerManager mPowerManager; @@ -348,7 +345,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe mSm.setDbg(DBG); mSm.start(); - mRtt = new WifiAwareRttStateManager(); mDataPathMgr = new WifiAwareDataPathStateManager(this); mDataPathMgr.start(mContext, mSm.getHandler().getLooper(), awareMetrics, permissionsWrapper); @@ -417,6 +413,48 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe } /* + * Cross-service API: synchronized but independent of state machine + */ + + /** + * Translate (and return in the callback) the peerId to its MAC address representation. + */ + public void requestMacAddresses(int uid, List<Integer> peerIds, + IWifiAwareMacAddressProvider callback) { + mSm.getHandler().post(() -> { + if (VDBG) Log.v(TAG, "requestMacAddresses: uid=" + uid + ", peerIds=" + peerIds); + Map<Integer, byte[]> peerIdToMacMap = new HashMap<>(); + for (int i = 0; i < mClients.size(); ++i) { + WifiAwareClientState client = mClients.valueAt(i); + if (client.getUid() != uid) { + continue; + } + + SparseArray<WifiAwareDiscoverySessionState> sessions = client.getSessions(); + for (int j = 0; j < sessions.size(); ++j) { + WifiAwareDiscoverySessionState session = sessions.valueAt(j); + + for (int peerId : peerIds) { + WifiAwareDiscoverySessionState.PeerInfo peerInfo = session.getPeerInfo( + peerId); + if (peerInfo != null) { + peerIdToMacMap.put(peerId, peerInfo.mMac); + } + } + } + } + + try { + if (VDBG) Log.v(TAG, "requestMacAddresses: peerIdToMacMap=" + peerIdToMacMap); + callback.macAddress(peerIdToMacMap); + } catch (RemoteException e) { + Log.e(TAG, "requestMacAddress (sync): exception on callback -- " + e); + + } + }); + } + + /* * COMMANDS */ @@ -553,20 +591,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe } /** - * Place a request to range a peer on the discovery session on the state machine queue. - */ - public void startRanging(int clientId, int sessionId, RttManager.RttParams[] params, - int rangingId) { - Message msg = mSm.obtainMessage(MESSAGE_TYPE_COMMAND); - msg.arg1 = COMMAND_TYPE_START_RANGING; - msg.arg2 = clientId; - msg.obj = params; - msg.getData().putInt(MESSAGE_BUNDLE_KEY_SESSION_ID, sessionId); - msg.getData().putInt(MESSAGE_BUNDLE_KEY_RANGING_ID, rangingId); - mSm.sendMessage(msg); - } - - /** * Enable usage of Aware. Doesn't actually turn on Aware (form clusters) - that * only happens when a connection is created. */ @@ -1525,18 +1549,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe case COMMAND_TYPE_DISABLE_USAGE: waitForResponse = disableUsageLocal(mCurrentTransactionId); break; - case COMMAND_TYPE_START_RANGING: { - Bundle data = msg.getData(); - - int clientId = msg.arg2; - RttManager.RttParams[] params = (RttManager.RttParams[]) msg.obj; - int sessionId = data.getInt(MESSAGE_BUNDLE_KEY_SESSION_ID); - int rangingId = data.getInt(MESSAGE_BUNDLE_KEY_RANGING_ID); - - startRangingLocal(clientId, sessionId, params, rangingId); - waitForResponse = false; - break; - } case COMMAND_TYPE_GET_CAPABILITIES: if (mCapabilities == null) { waitForResponse = mWifiAwareNativeApi.getCapabilities( @@ -1614,7 +1626,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe break; case COMMAND_TYPE_DELAYED_INITIALIZATION: mWifiAwareNativeManager.start(); - mRtt.start(mContext, mSm.getHandler().getLooper()); waitForResponse = false; break; default: @@ -1827,9 +1838,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe case COMMAND_TYPE_DISABLE_USAGE: Log.wtf(TAG, "processTimeout: DISABLE_USAGE - shouldn't be waiting!"); break; - case COMMAND_TYPE_START_RANGING: - Log.wtf(TAG, "processTimeout: START_RANGING - shouldn't be waiting!"); - break; case COMMAND_TYPE_GET_CAPABILITIES: Log.e(TAG, "processTimeout: GET_CAPABILITIES timed-out - strange, will try again" @@ -2308,49 +2316,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe return callDispatched; } - private void startRangingLocal(int clientId, int sessionId, RttManager.RttParams[] params, - int rangingId) { - if (VDBG) { - Log.v(TAG, "startRangingLocal: clientId=" + clientId + ", sessionId=" + sessionId - + ", parms=" + Arrays.toString(params) + ", rangingId=" + rangingId); - } - - WifiAwareClientState client = mClients.get(clientId); - if (client == null) { - Log.e(TAG, "startRangingLocal: no client exists for clientId=" + clientId); - return; - } - - WifiAwareDiscoverySessionState session = client.getSession(sessionId); - if (session == null) { - Log.e(TAG, "startRangingLocal: no session exists for clientId=" + clientId - + ", sessionId=" + sessionId); - client.onRangingFailure(rangingId, RttManager.REASON_INVALID_REQUEST, - "Invalid session ID"); - return; - } - - for (RttManager.RttParams param : params) { - String peerIdStr = param.bssid; - try { - WifiAwareDiscoverySessionState.PeerInfo peerInfo = session.getPeerInfo( - Integer.parseInt(peerIdStr)); - if (peerInfo == null || peerInfo.mMac == null) { - Log.d(TAG, "startRangingLocal: no MAC address for peer ID=" + peerIdStr); - param.bssid = ""; - } else { - param.bssid = NativeUtil.macAddressFromByteArray(peerInfo.mMac); - } - } catch (NumberFormatException e) { - Log.e(TAG, "startRangingLocal: invalid peer ID specification (in bssid field): '" - + peerIdStr + "'"); - param.bssid = ""; - } - } - - mRtt.startRanging(rangingId, client, params); - } - private boolean initiateDataPathSetupLocal(short transactionId, WifiAwareNetworkSpecifier networkSpecifier, int peerId, int channelRequestType, int channel, byte[] peer, String interfaceName, byte[] pmk, String passphrase, @@ -3061,7 +3026,6 @@ public class WifiAwareStateManager implements WifiAwareShellCommand.DelegatedShe } pw.println(" mSettableParameters: " + mSettableParameters); mSm.dump(fd, pw, args); - mRtt.dump(fd, pw, args); mDataPathMgr.dump(fd, pw, args); mWifiAwareNativeApi.dump(fd, pw, args); } diff --git a/com/android/server/wifi/rtt/RttNative.java b/com/android/server/wifi/rtt/RttNative.java new file mode 100644 index 00000000..d30fe915 --- /dev/null +++ b/com/android/server/wifi/rtt/RttNative.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2017 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.wifi.rtt; + +import android.hardware.wifi.V1_0.IWifiRttController; +import android.hardware.wifi.V1_0.IWifiRttControllerEventCallback; +import android.hardware.wifi.V1_0.RttBw; +import android.hardware.wifi.V1_0.RttConfig; +import android.hardware.wifi.V1_0.RttPeerType; +import android.hardware.wifi.V1_0.RttPreamble; +import android.hardware.wifi.V1_0.RttResult; +import android.hardware.wifi.V1_0.RttStatus; +import android.hardware.wifi.V1_0.RttType; +import android.hardware.wifi.V1_0.WifiChannelWidthInMhz; +import android.hardware.wifi.V1_0.WifiStatus; +import android.hardware.wifi.V1_0.WifiStatusCode; +import android.net.wifi.ScanResult; +import android.net.wifi.rtt.RangingRequest; +import android.net.wifi.rtt.RangingResult; +import android.net.wifi.rtt.RangingResultCallback; +import android.os.RemoteException; +import android.util.Log; + +import com.android.server.wifi.HalDeviceManager; +import com.android.server.wifi.WifiNative; +import com.android.server.wifi.WifiVendorHal; +import com.android.server.wifi.util.NativeUtil; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + +/** + * TBD + */ +public class RttNative extends IWifiRttControllerEventCallback.Stub { + private static final String TAG = "RttNative"; + private static final boolean VDBG = true; // STOPSHIP if true + + private final RttServiceImpl mRttService; + private final HalDeviceManager mHalDeviceManager; + private final WifiVendorHal mWifiVendorHal; + + private boolean mIsInitialized = false; + + public RttNative(RttServiceImpl rttService, HalDeviceManager halDeviceManager, + WifiNative wifiNative) { + mRttService = rttService; + mHalDeviceManager = halDeviceManager; + mWifiVendorHal = wifiNative.getVendorHal(); + } + + /** + * Issue a range request to the HAL. + * + * @param cmdId Command ID for the request. Will be used in the corresponding + * {@link #onResults(int, ArrayList)}. + * @param request Range request. + * @return Success status: true for success, false for failure. + */ + public boolean rangeRequest(int cmdId, RangingRequest request) { + if (VDBG) Log.v(TAG, "rangeRequest: cmdId=" + cmdId + ", request=" + request); + // TODO: b/65014872 replace by direct access to HalDeviceManager + IWifiRttController rttController = mWifiVendorHal.getRttController(); + if (rttController == null) { + Log.e(TAG, "rangeRequest: RttController is null"); + return false; + } + if (!mIsInitialized) { + try { + WifiStatus status = rttController.registerEventCallback(this); + if (status.code != WifiStatusCode.SUCCESS) { + Log.e(TAG, + "rangeRequest: cannot register event callback -- code=" + status.code); + return false; + } + } catch (RemoteException e) { + Log.e(TAG, "rangeRequest: exception registering callback: " + e); + return false; + } + mIsInitialized = true; + } + + ArrayList<RttConfig> rttConfig = convertRangingRequestToRttConfigs(request); + if (rttConfig == null) { + Log.e(TAG, "rangeRequest: invalid request parameters"); + return false; + } + + try { + WifiStatus status = rttController.rangeRequest(cmdId, rttConfig); + if (status.code != WifiStatusCode.SUCCESS) { + Log.e(TAG, "rangeRequest: cannot issue range request -- code=" + status.code); + return false; + } + } catch (RemoteException e) { + Log.e(TAG, "rangeRequest: exception issuing range request: " + e); + return false; + } + + return true; + } + + /** + * Callback from HAL with range results. + * + * @param cmdId Command ID specified in the original request + * {@link #rangeRequest(int, RangingRequest)}. + * @param halResults A list of range results. + */ + @Override + public void onResults(int cmdId, ArrayList<RttResult> halResults) { + if (VDBG) Log.v(TAG, "onResults: cmdId=" + cmdId + ", # of results=" + halResults.size()); + List<RangingResult> results = new ArrayList<>(halResults.size()); + + for (RttResult halResult: halResults) { + results.add(new RangingResult( + halResult.status == RttStatus.SUCCESS ? RangingResultCallback.STATUS_SUCCESS + : RangingResultCallback.STATUS_FAIL, halResult.addr, + halResult.distanceInMm / 10, halResult.distanceSdInMm / 10, halResult.rssi, + halResult.timeStampInUs)); + } + + mRttService.onRangingResults(cmdId, results); + } + + private static ArrayList<RttConfig> convertRangingRequestToRttConfigs(RangingRequest request) { + ArrayList<RttConfig> rttConfigs = new ArrayList<>(request.mRttPeers.size()); + + // Skipping any configurations which have an error (printing out a message). + // The caller will only get results for valid configurations. + for (RangingRequest.RttPeer peer: request.mRttPeers) { + if (peer instanceof RangingRequest.RttPeerAp) { + ScanResult scanResult = ((RangingRequest.RttPeerAp) peer).scanResult; + RttConfig config = new RttConfig(); + + byte[] addr = NativeUtil.macAddressToByteArray(scanResult.BSSID); + if (addr.length != config.addr.length) { + Log.e(TAG, "Invalid configuration: unexpected BSSID length -- " + scanResult); + continue; + } + for (int i = 0; i < config.addr.length; ++i) { + config.addr[i] = addr[i]; + } + + try { + config.type = + scanResult.is80211mcResponder() ? RttType.TWO_SIDED : RttType.ONE_SIDED; + config.peer = RttPeerType.AP; + config.channel.width = halChannelWidthFromScanResult( + scanResult.channelWidth); + config.channel.centerFreq = scanResult.frequency; + if (scanResult.centerFreq0 > 0) { + config.channel.centerFreq0 = scanResult.centerFreq0; + } + if (scanResult.centerFreq1 > 0) { + config.channel.centerFreq1 = scanResult.centerFreq1; + } + config.burstPeriod = 0; + config.numBurst = 0; + config.numFramesPerBurst = 8; + config.numRetriesPerRttFrame = 0; + config.numRetriesPerFtmr = 0; + config.mustRequestLci = false; + config.mustRequestLcr = false; + config.burstDuration = 15; + if (config.channel.centerFreq > 5000) { + config.preamble = RttPreamble.VHT; + } else { + config.preamble = RttPreamble.HT; + } + config.bw = halChannelBandwidthFromScanResult(scanResult.channelWidth); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Invalid configuration: " + e.getMessage()); + continue; + } + + rttConfigs.add(config); + } else { + Log.e(TAG, "convertRangingRequestToRttConfigs: unknown request type -- " + + peer.getClass().getCanonicalName()); + return null; + } + } + + + return rttConfigs; + } + + static int halChannelWidthFromScanResult(int scanResultChannelWidth) { + switch (scanResultChannelWidth) { + case ScanResult.CHANNEL_WIDTH_20MHZ: + return WifiChannelWidthInMhz.WIDTH_20; + case ScanResult.CHANNEL_WIDTH_40MHZ: + return WifiChannelWidthInMhz.WIDTH_40; + case ScanResult.CHANNEL_WIDTH_80MHZ: + return WifiChannelWidthInMhz.WIDTH_80; + case ScanResult.CHANNEL_WIDTH_160MHZ: + return WifiChannelWidthInMhz.WIDTH_160; + case ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ: + return WifiChannelWidthInMhz.WIDTH_80P80; + default: + throw new IllegalArgumentException( + "halChannelWidthFromScanResult: bad " + scanResultChannelWidth); + } + } + + static int halChannelBandwidthFromScanResult(int scanResultChannelWidth) { + switch (scanResultChannelWidth) { + case ScanResult.CHANNEL_WIDTH_20MHZ: + return RttBw.BW_20MHZ; + case ScanResult.CHANNEL_WIDTH_40MHZ: + return RttBw.BW_40MHZ; + case ScanResult.CHANNEL_WIDTH_80MHZ: + return RttBw.BW_80MHZ; + case ScanResult.CHANNEL_WIDTH_160MHZ: + return RttBw.BW_160MHZ; + case ScanResult.CHANNEL_WIDTH_80MHZ_PLUS_MHZ: + return RttBw.BW_160MHZ; + default: + throw new IllegalArgumentException( + "halChannelBandwidthFromScanResult: bad " + scanResultChannelWidth); + } + } + + /** + * Dump the internal state of the class. + */ + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("RttNative:"); + pw.println(" mIsInitialized: " + mIsInitialized); + } +} diff --git a/com/android/server/wifi/rtt/RttService.java b/com/android/server/wifi/rtt/RttService.java new file mode 100644 index 00000000..71e8a0ac --- /dev/null +++ b/com/android/server/wifi/rtt/RttService.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2017 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.wifi.rtt; + +import android.content.Context; +import android.os.HandlerThread; +import android.util.Log; + +import com.android.server.SystemService; +import com.android.server.wifi.HalDeviceManager; +import com.android.server.wifi.WifiInjector; +import com.android.server.wifi.WifiNative; +import com.android.server.wifi.util.WifiPermissionsUtil; + +/** + * TBD. + */ +public class RttService extends SystemService { + private static final String TAG = "RttService"; + private RttServiceImpl mImpl; + + public RttService(Context context) { + super(context); + mImpl = new RttServiceImpl(context); + } + + @Override + public void onStart() { + Log.i(TAG, "Registering " + Context.WIFI_RTT2_SERVICE); + publishBinderService(Context.WIFI_RTT2_SERVICE, mImpl); + } + + @Override + public void onBootPhase(int phase) { + if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { + Log.i(TAG, "Starting " + Context.WIFI_RTT2_SERVICE); + + WifiInjector wifiInjector = WifiInjector.getInstance(); + if (wifiInjector == null) { + Log.e(TAG, "onBootPhase(PHASE_SYSTEM_SERVICES_READY): NULL injector!"); + return; + } + + HalDeviceManager halDeviceManager = wifiInjector.getHalDeviceManager(); + HandlerThread handlerThread = wifiInjector.getRttHandlerThread(); + WifiNative wifiNative = wifiInjector.getWifiNative(); + WifiPermissionsUtil wifiPermissionsUtil = wifiInjector.getWifiPermissionsUtil(); + + RttNative rttNative = new RttNative(mImpl, halDeviceManager, wifiNative); + mImpl.start(handlerThread.getLooper(), rttNative, wifiPermissionsUtil); + } + } +} diff --git a/com/android/server/wifi/rtt/RttServiceImpl.java b/com/android/server/wifi/rtt/RttServiceImpl.java new file mode 100644 index 00000000..d248768c --- /dev/null +++ b/com/android/server/wifi/rtt/RttServiceImpl.java @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2017 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.wifi.rtt; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.net.wifi.rtt.IRttCallback; +import android.net.wifi.rtt.IWifiRttManager; +import android.net.wifi.rtt.RangingRequest; +import android.net.wifi.rtt.RangingResult; +import android.net.wifi.rtt.RangingResultCallback; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.util.Log; + +import com.android.server.wifi.util.NativeUtil; +import com.android.server.wifi.util.WifiPermissionsUtil; + +import libcore.util.HexEncoding; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Set; + +/** + * Implementation of the IWifiRttManager AIDL interface and of the RttService state manager. + */ +public class RttServiceImpl extends IWifiRttManager.Stub { + private static final String TAG = "RttServiceImpl"; + private static final boolean VDBG = true; // STOPSHIP if true + + private final Context mContext; + private WifiPermissionsUtil mWifiPermissionsUtil; + + private RttServiceSynchronized mRttServiceSynchronized; + + + public RttServiceImpl(Context context) { + mContext = context; + } + + /* + * INITIALIZATION + */ + + /** + * Initializes the RTT service (usually with objects from an injector). + * + * @param looper The looper on which to synchronize operations. + * @param rttNative The Native interface to the HAL. + * @param wifiPermissionsUtil Utility for permission checks. + */ + public void start(Looper looper, RttNative rttNative, WifiPermissionsUtil wifiPermissionsUtil) { + mWifiPermissionsUtil = wifiPermissionsUtil; + mRttServiceSynchronized = new RttServiceSynchronized(looper, rttNative); + } + + /* + * ASYNCHRONOUS DOMAIN - can be called from different threads! + */ + + /** + * Proxy for the final native call of the parent class. Enables mocking of + * the function. + */ + public int getMockableCallingUid() { + return getCallingUid(); + } + + /** + * Binder interface API to start a ranging operation. Called on binder thread, operations needs + * to be posted to handler thread. + */ + @Override + public void startRanging(IBinder binder, String callingPackage, RangingRequest request, + IRttCallback callback) throws RemoteException { + if (VDBG) { + Log.v(TAG, "startRanging: binder=" + binder + ", callingPackage=" + callingPackage + + ", request=" + request + ", callback=" + callback); + } + // verify arguments + if (binder == null) { + throw new IllegalArgumentException("Binder must not be null"); + } + if (request == null || request.mRttPeers == null || request.mRttPeers.size() == 0) { + throw new IllegalArgumentException("Request must not be null or empty"); + } + for (RangingRequest.RttPeer peer: request.mRttPeers) { + if (peer == null) { + throw new IllegalArgumentException( + "Request must not contain empty peer specifications"); + } + if (!(peer instanceof RangingRequest.RttPeerAp)) { + throw new IllegalArgumentException( + "Request contains unknown peer specification types"); + } + } + if (callback == null) { + throw new IllegalArgumentException("Callback must not be null"); + } + request.enforceValidity(); + + final int uid = getMockableCallingUid(); + + // permission check + enforceAccessPermission(); + enforceChangePermission(); + enforceLocationPermission(callingPackage, uid); + + // register for binder death + IBinder.DeathRecipient dr = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + if (VDBG) Log.v(TAG, "binderDied: uid=" + uid); + binder.unlinkToDeath(this, 0); + + mRttServiceSynchronized.mHandler.post(() -> { + mRttServiceSynchronized.cleanUpOnClientDeath(uid); + }); + } + }; + + try { + binder.linkToDeath(dr, 0); + } catch (RemoteException e) { + Log.e(TAG, "Error on linkToDeath - " + e); + } + + mRttServiceSynchronized.mHandler.post(() -> { + mRttServiceSynchronized.queueRangingRequest(uid, binder, dr, callingPackage, request, + callback); + }); + } + + /** + * Called by HAL to report ranging results. Called on HAL thread - needs to post to local + * thread. + */ + public void onRangingResults(int cmdId, List<RangingResult> results) { + if (VDBG) Log.v(TAG, "onRangingResults: cmdId=" + cmdId); + mRttServiceSynchronized.mHandler.post(() -> { + mRttServiceSynchronized.onRangingResults(cmdId, results); + }); + } + + private void enforceAccessPermission() { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE, TAG); + } + + private void enforceChangePermission() { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_WIFI_STATE, TAG); + } + + private void enforceLocationPermission(String callingPackage, int uid) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION, + TAG); + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump RttService from pid=" + + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); + return; + } + pw.println("Wi-Fi RTT Service"); + mRttServiceSynchronized.dump(fd, pw, args); + } + + /* + * SYNCHRONIZED DOMAIN + */ + + /** + * RTT service implementation - synchronized on a single thread. All commands should be posted + * to the exposed handler. + */ + private class RttServiceSynchronized { + public Handler mHandler; + + private RttNative mRttNative; + private int mNextCommandId = 1000; + private List<RttRequestInfo> mRttRequestQueue = new LinkedList<>(); + + RttServiceSynchronized(Looper looper, RttNative rttNative) { + mRttNative = rttNative; + + mHandler = new Handler(looper); + } + + private void cleanUpOnClientDeath(int uid) { + if (VDBG) { + Log.v(TAG, "RttServiceSynchronized.cleanUpOnClientDeath: uid=" + uid + + ", mRttRequestQueue=" + mRttRequestQueue); + } + ListIterator<RttRequestInfo> it = mRttRequestQueue.listIterator(); + while (it.hasNext()) { + RttRequestInfo rri = it.next(); + if (rri.uid == uid) { + // TODO: actually abort operation - though API is not clear or clean + if (rri.cmdId == 0) { + // Until that happens we will get results for the last operation: which is + // why we don't dispatch a new range request off the queue and keep the + // currently running operation in the queue + it.remove(); + } + } + } + + if (VDBG) { + Log.v(TAG, "RttServiceSynchronized.cleanUpOnClientDeath: uid=" + uid + + ", after cleanup - mRttRequestQueue=" + mRttRequestQueue); + } + } + + private void queueRangingRequest(int uid, IBinder binder, IBinder.DeathRecipient dr, + String callingPackage, RangingRequest request, IRttCallback callback) { + RttRequestInfo newRequest = new RttRequestInfo(); + newRequest.uid = uid; + newRequest.binder = binder; + newRequest.dr = dr; + newRequest.callingPackage = callingPackage; + newRequest.request = request; + newRequest.callback = callback; + mRttRequestQueue.add(newRequest); + + if (VDBG) { + Log.v(TAG, "RttServiceSynchronized.queueRangingRequest: newRequest=" + newRequest); + } + + executeNextRangingRequestIfPossible(); + } + + private void executeNextRangingRequestIfPossible() { + if (mRttRequestQueue.size() == 0) { + if (VDBG) Log.v(TAG, "executeNextRangingRequestIfPossible: no requests pending"); + return; + } + + RttRequestInfo nextRequest = mRttRequestQueue.get(0); + if (nextRequest.cmdId != 0) { + if (VDBG) { + Log.v(TAG, "executeNextRangingRequestIfPossible: called but a command is " + + "executing. topOfQueue=" + nextRequest); + } + return; + } + + nextRequest.cmdId = mNextCommandId++; + startRanging(nextRequest); + } + + private void startRanging(RttRequestInfo nextRequest) { + if (VDBG) { + Log.v(TAG, "RttServiceSynchronized.startRanging: nextRequest=" + nextRequest); + } + + if (!mRttNative.rangeRequest(nextRequest.cmdId, nextRequest.request)) { + Log.w(TAG, "RttServiceSynchronized.startRanging: native rangeRequest call failed"); + try { + nextRequest.callback.onRangingResults(RangingResultCallback.STATUS_FAIL, null); + } catch (RemoteException e) { + Log.e(TAG, "RttServiceSynchronized.startRanging: HAL request failed, callback " + + "failed -- " + e); + } + + mRttRequestQueue.remove(0); + executeNextRangingRequestIfPossible(); + } + } + + private void onRangingResults(int cmdId, List<RangingResult> results) { + if (mRttRequestQueue.size() == 0) { + Log.e(TAG, "RttServiceSynchronized.onRangingResults: no current RTT request " + + "pending!?"); + return; + } + RttRequestInfo topOfQueueRequest = mRttRequestQueue.get(0); + + if (VDBG) { + Log.v(TAG, "RttServiceSynchronized.onRangingResults: cmdId=" + cmdId + + ", topOfQueueRequest=" + topOfQueueRequest + ", results=" + + Arrays.toString(results.toArray())); + } + + if (topOfQueueRequest.cmdId != cmdId) { + Log.e(TAG, "RttServiceSynchronized.onRangingResults: cmdId=" + cmdId + + ", does not match pending RTT request cmdId=" + topOfQueueRequest.cmdId); + return; + } + + boolean permissionGranted = mWifiPermissionsUtil.checkCallersLocationPermission( + topOfQueueRequest.callingPackage, topOfQueueRequest.uid); + try { + if (permissionGranted) { + addMissingEntries(topOfQueueRequest.request, results); + topOfQueueRequest.callback.onRangingResults( + RangingResultCallback.STATUS_SUCCESS, results); + } else { + Log.w(TAG, "RttServiceSynchronized.onRangingResults: location permission " + + "revoked - not forwarding results"); + topOfQueueRequest.callback.onRangingResults(RangingResultCallback.STATUS_FAIL, + null); + } + } catch (RemoteException e) { + Log.e(TAG, + "RttServiceSynchronized.onRangingResults: callback exception -- " + e); + } + + // clean-up binder death listener: the callback for results is a onetime event - now + // done with the binder. + topOfQueueRequest.binder.unlinkToDeath(topOfQueueRequest.dr, 0); + + mRttRequestQueue.remove(0); + executeNextRangingRequestIfPossible(); + } + + /* + * Make sure the results contain an entry for each request. Add results with FAIL status + * if missing. + */ + private void addMissingEntries(RangingRequest request, + List<RangingResult> results) { + Set<String> resultEntries = new HashSet<>(results.size()); + for (RangingResult result: results) { + resultEntries.add(new String(HexEncoding.encode(result.getMacAddress()))); + } + + for (RangingRequest.RttPeer peer: request.mRttPeers) { + byte[] addr; + if (peer instanceof RangingRequest.RttPeerAp) { + addr = NativeUtil.macAddressToByteArray( + ((RangingRequest.RttPeerAp) peer).scanResult.BSSID); + } else { + continue; + } + String canonicString = new String(HexEncoding.encode(addr)); + + if (!resultEntries.contains(canonicString)) { + if (VDBG) { + Log.v(TAG, "padRangingResultsWithMissingResults: missing=" + canonicString); + } + results.add(new RangingResult(RangingResultCallback.STATUS_FAIL, addr, 0, 0, 0, + 0)); + } + } + } + + // dump call (asynchronous most likely) + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println(" mNextCommandId: " + mNextCommandId); + pw.println(" mRttRequestQueue: " + mRttRequestQueue); + mRttNative.dump(fd, pw, args); + } + } + + private static class RttRequestInfo { + public int uid; + public IBinder binder; + public IBinder.DeathRecipient dr; + public String callingPackage; + public RangingRequest request; + public byte[] mac; + public IRttCallback callback; + + public int cmdId = 0; // uninitialized cmdId value + + @Override + public String toString() { + return new StringBuilder("RttRequestInfo: uid=").append(uid).append(", binder=").append( + binder).append(", dr=").append(dr).append(", callingPackage=").append( + callingPackage).append(", request=").append(request.toString()).append( + ", callback=").append(callback).append(", cmdId=").append(cmdId).toString(); + } + } +} diff --git a/com/android/server/wifi/scanner/ChannelHelper.java b/com/android/server/wifi/scanner/ChannelHelper.java index d87df072..6a01f0c8 100644 --- a/com/android/server/wifi/scanner/ChannelHelper.java +++ b/com/android/server/wifi/scanner/ChannelHelper.java @@ -242,7 +242,7 @@ public abstract class ChannelHelper { if (scanSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) { return toString(scanSettings.channels); } else { - return toString(scanSettings.band); + return bandToString(scanSettings.band); } } @@ -255,7 +255,7 @@ public abstract class ChannelHelper { if (bucketSettings.band == WifiScanner.WIFI_BAND_UNSPECIFIED) { return toString(bucketSettings.channels, bucketSettings.num_channels); } else { - return toString(bucketSettings.band); + return bandToString(bucketSettings.band); } } @@ -293,7 +293,10 @@ public abstract class ChannelHelper { return sb.toString(); } - private static String toString(int band) { + /** + * Converts a WifiScanner.WIFI_BAND_* constant to a meaningful String + */ + public static String bandToString(int band) { switch (band) { case WifiScanner.WIFI_BAND_UNSPECIFIED: return "unspecified"; @@ -310,7 +313,6 @@ public abstract class ChannelHelper { case WifiScanner.WIFI_BAND_BOTH_WITH_DFS: return "24Ghz & 5Ghz (DFS incl)"; } - return "invalid band"; } } diff --git a/com/android/server/wifi/scanner/HalWifiScannerImpl.java b/com/android/server/wifi/scanner/HalWifiScannerImpl.java index 7d0ccbaf..890f72e6 100644 --- a/com/android/server/wifi/scanner/HalWifiScannerImpl.java +++ b/com/android/server/wifi/scanner/HalWifiScannerImpl.java @@ -131,11 +131,6 @@ public class HalWifiScannerImpl extends WifiScannerImpl implements Handler.Callb } @Override - public boolean shouldScheduleBackgroundScanForHwPno() { - return mWificondScannerDelegate.shouldScheduleBackgroundScanForHwPno(); - } - - @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { mWificondScannerDelegate.dump(fd, pw, args); } diff --git a/com/android/server/wifi/scanner/WifiScannerImpl.java b/com/android/server/wifi/scanner/WifiScannerImpl.java index 5281b3ab..dacb0074 100644 --- a/com/android/server/wifi/scanner/WifiScannerImpl.java +++ b/com/android/server/wifi/scanner/WifiScannerImpl.java @@ -155,11 +155,5 @@ public abstract class WifiScannerImpl { */ public abstract boolean isHwPnoSupported(boolean isConnectedPno); - /** - * This returns whether a background scan should be running for HW PNO scan or not. - * @return true if background scan needs to be started, false otherwise. - */ - public abstract boolean shouldScheduleBackgroundScanForHwPno(); - protected abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args); } diff --git a/com/android/server/wifi/scanner/WifiScanningServiceImpl.java b/com/android/server/wifi/scanner/WifiScanningServiceImpl.java index 4b8e284c..02462f67 100644 --- a/com/android/server/wifi/scanner/WifiScanningServiceImpl.java +++ b/com/android/server/wifi/scanner/WifiScanningServiceImpl.java @@ -75,7 +75,6 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private static final String TAG = WifiScanningService.TAG; private static final boolean DBG = false; - private static final int MIN_PERIOD_PER_CHANNEL_MS = 200; // DFS needs 120 ms private static final int UNKNOWN_PID = -1; private final LocalLog mLocalLog = new LocalLog(512); @@ -240,10 +239,6 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { private static final int CMD_SCAN_RESULTS_AVAILABLE = BASE + 0; private static final int CMD_FULL_SCAN_RESULTS = BASE + 1; - private static final int CMD_HOTLIST_AP_FOUND = BASE + 2; - private static final int CMD_HOTLIST_AP_LOST = BASE + 3; - private static final int CMD_WIFI_CHANGE_DETECTED = BASE + 4; - private static final int CMD_WIFI_CHANGE_TIMEOUT = BASE + 5; private static final int CMD_DRIVER_LOADED = BASE + 6; private static final int CMD_DRIVER_UNLOADED = BASE + 7; private static final int CMD_SCAN_PAUSED = BASE + 8; @@ -1724,10 +1719,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { } logScanRequest("addHwPnoScanRequest", ci, handler, null, scanSettings, pnoSettings); addPnoScanRequest(ci, handler, scanSettings, pnoSettings); - // HW PNO is supported, check if we need a background scan running for this. - if (mScannerImpl.shouldScheduleBackgroundScanForHwPno()) { - addBackgroundScanRequest(scanSettings); - } + return true; } @@ -2213,7 +2205,7 @@ public class WifiScanningServiceImpl extends IWifiScanner.Stub { static String describeTo(StringBuilder sb, ScanSettings scanSettings) { sb.append("ScanSettings { ") - .append(" band:").append(scanSettings.band) + .append(" band:").append(ChannelHelper.bandToString(scanSettings.band)) .append(" period:").append(scanSettings.periodInMs) .append(" reportEvents:").append(scanSettings.reportEvents) .append(" numBssidsPerScan:").append(scanSettings.numBssidsPerScan) diff --git a/com/android/server/wifi/scanner/WificondScannerImpl.java b/com/android/server/wifi/scanner/WificondScannerImpl.java index 12a0bdee..fb878e67 100644 --- a/com/android/server/wifi/scanner/WificondScannerImpl.java +++ b/com/android/server/wifi/scanner/WificondScannerImpl.java @@ -34,7 +34,6 @@ import com.android.server.wifi.scanner.ChannelHelper.ChannelCollection; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -50,7 +49,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call private static final String TAG = "WificondScannerImpl"; private static final boolean DBG = false; - public static final String BACKGROUND_PERIOD_ALARM_TAG = TAG + " Background Scan Period"; public static final String TIMEOUT_ALARM_TAG = TAG + " Scan Timeout"; // Max number of networks that can be specified to wificond per scan request public static final int MAX_HIDDEN_NETWORK_IDS_PER_SCAN = 16; @@ -69,20 +67,9 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call private final Object mSettingsLock = new Object(); // Next scan settings to apply when the previous scan completes - private WifiNative.ScanSettings mPendingBackgroundScanSettings = null; - private WifiNative.ScanEventHandler mPendingBackgroundScanEventHandler = null; private WifiNative.ScanSettings mPendingSingleScanSettings = null; private WifiNative.ScanEventHandler mPendingSingleScanEventHandler = null; - // Active background scan settings/state - private WifiNative.ScanSettings mBackgroundScanSettings = null; - private WifiNative.ScanEventHandler mBackgroundScanEventHandler = null; - private int mNextBackgroundScanPeriod = 0; - private int mNextBackgroundScanId = 0; - private boolean mBackgroundScanPeriodPending = false; - private boolean mBackgroundScanPaused = false; - private ScanBuffer mBackgroundScanBuffer = new ScanBuffer(SCAN_BUFFER_CAPACITY); - private ArrayList<ScanDetail> mNativeScanResults; private WifiScanner.ScanData mLatestSingleScanResult = new WifiScanner.ScanData(0, 0, new ScanResult[0]); @@ -110,14 +97,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call */ private static final long SCAN_TIMEOUT_MS = 15000; - AlarmManager.OnAlarmListener mScanPeriodListener = new AlarmManager.OnAlarmListener() { - public void onAlarm() { - synchronized (mSettingsLock) { - handleScanPeriod(); - } - } - }; - AlarmManager.OnAlarmListener mScanTimeoutListener = new AlarmManager.OnAlarmListener() { public void onAlarm() { synchronized (mSettingsLock) { @@ -161,7 +140,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call mPendingSingleScanSettings = null; mPendingSingleScanEventHandler = null; stopHwPnoScan(); - stopBatchedScan(); mLastScanSettings = null; // finally clear any active scan } } @@ -210,111 +188,23 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call @Override public boolean startBatchedScan(WifiNative.ScanSettings settings, WifiNative.ScanEventHandler eventHandler) { - if (settings == null || eventHandler == null) { - Log.w(TAG, "Invalid arguments for startBatched: settings=" + settings - + ",eventHandler=" + eventHandler); - return false; - } - - if (settings.max_ap_per_scan < 0 || settings.max_ap_per_scan > MAX_APS_PER_SCAN) { - return false; - } - if (settings.num_buckets < 0 || settings.num_buckets > MAX_SCAN_BUCKETS) { - return false; - } - if (settings.report_threshold_num_scans < 0 - || settings.report_threshold_num_scans > SCAN_BUFFER_CAPACITY) { - return false; - } - if (settings.report_threshold_percent < 0 || settings.report_threshold_percent > 100) { - return false; - } - if (settings.base_period_ms <= 0) { - return false; - } - for (int i = 0; i < settings.num_buckets; ++i) { - WifiNative.BucketSettings bucket = settings.buckets[i]; - if (bucket.period_ms % settings.base_period_ms != 0) { - return false; - } - } - - synchronized (mSettingsLock) { - stopBatchedScan(); - if (DBG) { - Log.d(TAG, "Starting scan num_buckets=" + settings.num_buckets + ", base_period=" - + settings.base_period_ms + " ms"); - } - mPendingBackgroundScanSettings = settings; - mPendingBackgroundScanEventHandler = eventHandler; - handleScanPeriod(); // Try to start scan immediately - return true; - } + Log.w(TAG, "startBatchedScan() is not supported"); + return false; } @Override public void stopBatchedScan() { - synchronized (mSettingsLock) { - if (DBG) Log.d(TAG, "Stopping scan"); - mBackgroundScanSettings = null; - mBackgroundScanEventHandler = null; - mPendingBackgroundScanSettings = null; - mPendingBackgroundScanEventHandler = null; - mBackgroundScanPaused = false; - mBackgroundScanPeriodPending = false; - unscheduleScansLocked(); - } - processPendingScans(); + Log.w(TAG, "stopBatchedScan() is not supported"); } @Override public void pauseBatchedScan() { - synchronized (mSettingsLock) { - if (DBG) Log.d(TAG, "Pausing scan"); - // if there isn't a pending scan then make the current scan pending - if (mPendingBackgroundScanSettings == null) { - mPendingBackgroundScanSettings = mBackgroundScanSettings; - mPendingBackgroundScanEventHandler = mBackgroundScanEventHandler; - } - mBackgroundScanSettings = null; - mBackgroundScanEventHandler = null; - mBackgroundScanPeriodPending = false; - mBackgroundScanPaused = true; - - unscheduleScansLocked(); - - WifiScanner.ScanData[] results = getLatestBatchedScanResults(/* flush = */ true); - if (mPendingBackgroundScanEventHandler != null) { - mPendingBackgroundScanEventHandler.onScanPaused(results); - } - } - processPendingScans(); + Log.w(TAG, "pauseBatchedScan() is not supported"); } @Override public void restartBatchedScan() { - synchronized (mSettingsLock) { - if (DBG) Log.d(TAG, "Restarting scan"); - if (mPendingBackgroundScanEventHandler != null) { - mPendingBackgroundScanEventHandler.onScanRestarted(); - } - mBackgroundScanPaused = false; - handleScanPeriod(); - } - } - - private void unscheduleScansLocked() { - mAlarmManager.cancel(mScanPeriodListener); - if (mLastScanSettings != null) { - mLastScanSettings.backgroundScanActive = false; - } - } - - private void handleScanPeriod() { - synchronized (mSettingsLock) { - mBackgroundScanPeriodPending = true; - processPendingScans(); - } + Log.w(TAG, "restartBatchedScan() is not supported"); } private void handleScanTimeout() { @@ -342,56 +232,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call final LastScanSettings newScanSettings = new LastScanSettings(mClock.getElapsedSinceBootMillis()); - // Update scan settings if there is a pending scan - if (!mBackgroundScanPaused) { - if (mPendingBackgroundScanSettings != null) { - mBackgroundScanSettings = mPendingBackgroundScanSettings; - mBackgroundScanEventHandler = mPendingBackgroundScanEventHandler; - mNextBackgroundScanPeriod = 0; - mPendingBackgroundScanSettings = null; - mPendingBackgroundScanEventHandler = null; - mBackgroundScanPeriodPending = true; - } - if (mBackgroundScanPeriodPending && mBackgroundScanSettings != null) { - int reportEvents = WifiScanner.REPORT_EVENT_NO_BATCH; // default to no batch - for (int bucket_id = 0; bucket_id < mBackgroundScanSettings.num_buckets; - ++bucket_id) { - WifiNative.BucketSettings bucket = - mBackgroundScanSettings.buckets[bucket_id]; - if (mNextBackgroundScanPeriod % (bucket.period_ms - / mBackgroundScanSettings.base_period_ms) == 0) { - if ((bucket.report_events - & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) != 0) { - reportEvents |= WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN; - } - if ((bucket.report_events - & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) { - reportEvents |= WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT; - } - // only no batch if all buckets specify it - if ((bucket.report_events - & WifiScanner.REPORT_EVENT_NO_BATCH) == 0) { - reportEvents &= ~WifiScanner.REPORT_EVENT_NO_BATCH; - } - - allFreqs.addChannels(bucket); - } - } - if (!allFreqs.isEmpty()) { - newScanSettings.setBackgroundScan(mNextBackgroundScanId++, - mBackgroundScanSettings.max_ap_per_scan, reportEvents, - mBackgroundScanSettings.report_threshold_num_scans, - mBackgroundScanSettings.report_threshold_percent); - } - mNextBackgroundScanPeriod++; - mBackgroundScanPeriodPending = false; - mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, - mClock.getElapsedSinceBootMillis() - + mBackgroundScanSettings.base_period_ms, - BACKGROUND_PERIOD_ALARM_TAG, mScanPeriodListener, mEventHandler); - } - } - if (mPendingSingleScanSettings != null) { boolean reportFullResults = false; ChannelCollection singleScanFreqs = mChannelHelper.createChannelCollection(); @@ -422,16 +262,26 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call mPendingSingleScanEventHandler = null; } - if ((newScanSettings.backgroundScanActive || newScanSettings.singleScanActive) - && !allFreqs.isEmpty()) { - pauseHwPnoScan(); - Set<Integer> freqs = allFreqs.getScanFreqs(); - boolean success = mWifiNative.scan(freqs, hiddenNetworkSSIDSet); + if (newScanSettings.singleScanActive) { + boolean success = false; + Set<Integer> freqs; + if (!allFreqs.isEmpty()) { + pauseHwPnoScan(); + freqs = allFreqs.getScanFreqs(); + success = mWifiNative.scan(freqs, hiddenNetworkSSIDSet); + if (!success) { + Log.e(TAG, "Failed to start scan, freqs=" + freqs); + } + } else { + // There is a scan request but no available channels could be scanned for. + // We regard it as a scan failure in this case. + Log.e(TAG, "Failed to start scan because there is " + + "no available channel to scan for"); + } if (success) { // TODO handle scan timeout if (DBG) { Log.d(TAG, "Starting wifi scan for freqs=" + freqs - + ", background=" + newScanSettings.backgroundScanActive + ", single=" + newScanSettings.singleScanActive); } mLastScanSettings = newScanSettings; @@ -439,7 +289,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call mClock.getElapsedSinceBootMillis() + SCAN_TIMEOUT_MS, TIMEOUT_ALARM_TAG, mScanTimeoutListener, mEventHandler); } else { - Log.e(TAG, "Failed to start scan, freqs=" + freqs); // indicate scan failure async mEventHandler.post(new Runnable() { public void run() { @@ -449,7 +298,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call } } }); - // TODO(b/27769665) background scans should be failed too if scans fail enough } } else if (isHwPnoScanRequired()) { newScanSettings.setHwPnoScan(mPnoSettings.networkList, mPnoEventHandler); @@ -512,7 +360,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call mLastScanSettings.singleScanEventHandler .onScanStatus(WifiNative.WIFI_SCAN_FAILED); } - // TODO(b/27769665) background scans should be failed too if scans fail enough mLastScanSettings = null; } } @@ -597,18 +444,13 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call return; } - if (DBG) Log.d(TAG, "Polling scan data for scan: " + mLastScanSettings.scanId); mNativeScanResults = mWifiNative.getScanResults(); List<ScanResult> singleScanResults = new ArrayList<>(); - List<ScanResult> backgroundScanResults = new ArrayList<>(); int numFilteredScanResults = 0; for (int i = 0; i < mNativeScanResults.size(); ++i) { ScanResult result = mNativeScanResults.get(i).getScanResult(); long timestamp_ms = result.timestamp / 1000; // convert us -> ms if (timestamp_ms > mLastScanSettings.startTime) { - if (mLastScanSettings.backgroundScanActive) { - backgroundScanResults.add(result); - } if (mLastScanSettings.singleScanActive && mLastScanSettings.singleScanFreqs.containsChannel( result.frequency)) { @@ -622,49 +464,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call Log.d(TAG, "Filtering out " + numFilteredScanResults + " scan results."); } - if (mLastScanSettings.backgroundScanActive) { - if (mBackgroundScanEventHandler != null) { - if ((mLastScanSettings.reportEvents - & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0) { - for (ScanResult scanResult : backgroundScanResults) { - // TODO(b/27506257): Fill in correct bucketsScanned value - mBackgroundScanEventHandler.onFullScanResult(scanResult, 0); - } - } - } - - Collections.sort(backgroundScanResults, SCAN_RESULT_SORT_COMPARATOR); - ScanResult[] scanResultsArray = new ScanResult[Math.min(mLastScanSettings.maxAps, - backgroundScanResults.size())]; - for (int i = 0; i < scanResultsArray.length; ++i) { - scanResultsArray[i] = backgroundScanResults.get(i); - } - - if ((mLastScanSettings.reportEvents & WifiScanner.REPORT_EVENT_NO_BATCH) == 0) { - // TODO(b/27506257): Fill in correct bucketsScanned value - mBackgroundScanBuffer.add(new WifiScanner.ScanData(mLastScanSettings.scanId, 0, - scanResultsArray)); - } - - if (mBackgroundScanEventHandler != null) { - if ((mLastScanSettings.reportEvents - & WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT) != 0 - || (mLastScanSettings.reportEvents - & WifiScanner.REPORT_EVENT_AFTER_EACH_SCAN) != 0 - || (mLastScanSettings.reportEvents - == WifiScanner.REPORT_EVENT_AFTER_BUFFER_FULL - && (mBackgroundScanBuffer.size() - >= (mBackgroundScanBuffer.capacity() - * mLastScanSettings.reportPercentThreshold - / 100) - || mBackgroundScanBuffer.size() - >= mLastScanSettings.reportNumScansThreshold))) { - mBackgroundScanEventHandler - .onScanStatus(WifiNative.WIFI_SCAN_RESULTS_AVAILABLE); - } - } - } - if (mLastScanSettings.singleScanActive && mLastScanSettings.singleScanEventHandler != null) { if (mLastScanSettings.reportSingleScanFullResults) { @@ -675,7 +474,7 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call } } Collections.sort(singleScanResults, SCAN_RESULT_SORT_COMPARATOR); - mLatestSingleScanResult = new WifiScanner.ScanData(mLastScanSettings.scanId, 0, 0, + mLatestSingleScanResult = new WifiScanner.ScanData(0, 0, 0, isAllChannelsScanned(mLastScanSettings.singleScanFreqs), singleScanResults.toArray(new ScanResult[singleScanResults.size()])); mLastScanSettings.singleScanEventHandler @@ -689,13 +488,7 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call @Override public WifiScanner.ScanData[] getLatestBatchedScanResults(boolean flush) { - synchronized (mSettingsLock) { - WifiScanner.ScanData[] results = mBackgroundScanBuffer.get(); - if (flush) { - mBackgroundScanBuffer.clear(); - } - return results; - } + return null; } private boolean startHwPnoScan(WifiNative.PnoSettings pnoSettings) { @@ -768,11 +561,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call } @Override - public boolean shouldScheduleBackgroundScanForHwPno() { - return false; - } - - @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { synchronized (mSettingsLock) { pw.println("Latest native scan results:"); @@ -813,24 +601,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call this.startTime = startTime; } - // Background settings - public boolean backgroundScanActive = false; - public int scanId; - public int maxAps; - public int reportEvents; - public int reportNumScansThreshold; - public int reportPercentThreshold; - - public void setBackgroundScan(int scanId, int maxAps, int reportEvents, - int reportNumScansThreshold, int reportPercentThreshold) { - this.backgroundScanActive = true; - this.scanId = scanId; - this.maxAps = maxAps; - this.reportEvents = reportEvents; - this.reportNumScansThreshold = reportNumScansThreshold; - this.reportPercentThreshold = reportPercentThreshold; - } - // Single scan settings public boolean singleScanActive = false; public boolean reportSingleScanFullResults; @@ -859,44 +629,6 @@ public class WificondScannerImpl extends WifiScannerImpl implements Handler.Call } } - - private static class ScanBuffer { - private final ArrayDeque<WifiScanner.ScanData> mBuffer; - private int mCapacity; - - ScanBuffer(int capacity) { - mCapacity = capacity; - mBuffer = new ArrayDeque<>(mCapacity); - } - - public int size() { - return mBuffer.size(); - } - - public int capacity() { - return mCapacity; - } - - public boolean isFull() { - return size() == mCapacity; - } - - public void add(WifiScanner.ScanData scanData) { - if (isFull()) { - mBuffer.pollFirst(); - } - mBuffer.offerLast(scanData); - } - - public void clear() { - mBuffer.clear(); - } - - public WifiScanner.ScanData[] get() { - return mBuffer.toArray(new WifiScanner.ScanData[mBuffer.size()]); - } - } - /** * HW PNO Debouncer is used to debounce PNO requests. This guards against toggling the PNO * state too often which is not handled very well by some drivers. diff --git a/com/android/server/wifi/util/KalmanFilter.java b/com/android/server/wifi/util/KalmanFilter.java new file mode 100644 index 00000000..b961ed80 --- /dev/null +++ b/com/android/server/wifi/util/KalmanFilter.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2017 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.wifi.util; + +/** + * Utility providiing a basic Kalman filter + * + * For background, see https://en.wikipedia.org/wiki/Kalman_filter + */ +public class KalmanFilter { + public Matrix mF; // stateTransition + public Matrix mQ; // processNoiseCovariance + public Matrix mH; // observationModel + public Matrix mR; // observationNoiseCovariance + public Matrix mP; // aPosterioriErrorCovariance + public Matrix mx; // stateEstimate + + /** + * Performs the prediction phase of the filter, using the state estimate to produce + * a new estimate for the current timestep. + */ + public void predict() { + mx = mF.dot(mx); + mP = mF.dot(mP).dotTranspose(mF).plus(mQ); + } + + /** + * Updates the state estimate to incorporate the new observation z. + */ + public void update(Matrix z) { + Matrix y = z.minus(mH.dot(mx)); + Matrix tS = mH.dot(mP).dotTranspose(mH).plus(mR); + Matrix tK = mP.dotTranspose(mH).dot(tS.inverse()); + mx = mx.plus(tK.dot(y)); + mP = mP.minus(tK.dot(mH).dot(mP)); + } + + @Override + public String toString() { + return "{F: " + mF + + " Q: " + mQ + + " H: " + mH + + " R: " + mR + + " P: " + mP + + " x: " + mx + + "}"; + } +} diff --git a/com/android/server/wm/AppWindowAnimator.java b/com/android/server/wm/AppWindowAnimator.java index c76b9059..5365e27d 100644 --- a/com/android/server/wm/AppWindowAnimator.java +++ b/com/android/server/wm/AppWindowAnimator.java @@ -16,10 +16,10 @@ package com.android.server.wm; -import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; import static com.android.server.wm.AppTransition.TRANSIT_UNSET; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -78,11 +78,8 @@ public class AppWindowAnimator { // requires that the duration of the two animations are the same. SurfaceControl thumbnail; int thumbnailTransactionSeq; - // TODO(b/62029108): combine both members into a private one. Create a member function to set - // the thumbnail layer to +1 to the highest layer position and replace all setter instances - // with this function. Remove all unnecessary calls to both variables in other classes. - int thumbnailLayer; - int thumbnailForceAboveLayer; + private int mThumbnailLayer; + Animation thumbnailAnimation; final Transformation thumbnailTransformation = new Transformation(); // This flag indicates that the destruction of the thumbnail surface is synchronized with @@ -256,7 +253,7 @@ public class AppWindowAnimator { private void updateLayers() { mAppToken.getDisplayContent().assignWindowLayers(false /* relayoutNeeded */); - thumbnailLayer = mAppToken.getHighestAnimLayer(); + updateThumbnailLayer(); } private void stepThumbnailAnimation(long currentTime) { @@ -280,26 +277,35 @@ public class AppWindowAnimator { thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]); if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail, "thumbnail", "alpha=" + thumbnailTransformation.getAlpha() - + " layer=" + thumbnailLayer + + " layer=" + mThumbnailLayer + " matrix=[" + tmpFloats[Matrix.MSCALE_X] + "," + tmpFloats[Matrix.MSKEW_Y] + "][" + tmpFloats[Matrix.MSKEW_X] + "," + tmpFloats[Matrix.MSCALE_Y] + "]"); thumbnail.setAlpha(thumbnailTransformation.getAlpha()); - if (thumbnailForceAboveLayer > 0) { - thumbnail.setLayer(thumbnailForceAboveLayer + 1); - } else { - // The thumbnail is layered below the window immediately above this - // token's anim layer. - thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER - - WindowManagerService.LAYER_OFFSET_THUMBNAIL); - } + updateThumbnailLayer(); thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y], tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]); thumbnail.setWindowCrop(thumbnailTransformation.getClipRect()); } /** + * Updates the thumbnail layer z order to just above the highest animation layer if changed + */ + void updateThumbnailLayer() { + if (thumbnail != null) { + final int layer = mAppToken.getHighestAnimLayer(); + if (layer != mThumbnailLayer) { + if (DEBUG_LAYERS) Slog.v(TAG, + "Setting thumbnail layer " + mAppToken + ": layer=" + layer); + thumbnail.setLayer(layer + WindowManagerService.WINDOW_LAYER_MULTIPLIER + - WindowManagerService.LAYER_OFFSET_THUMBNAIL); + mThumbnailLayer = layer; + } + } + } + + /** * Sometimes we need to synchronize the first frame of animation with some external event, e.g. * Recents hiding some of its content. To achieve this, we prolong the start of the animaiton * and keep producing the first frame of the animation. @@ -473,7 +479,7 @@ public class AppWindowAnimator { } if (thumbnail != null) { pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail); - pw.print(" layer="); pw.println(thumbnailLayer); + pw.print(" layer="); pw.println(mThumbnailLayer); pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation); pw.print(prefix); pw.print("thumbnailTransformation="); pw.println(thumbnailTransformation.toShortString()); diff --git a/com/android/server/wm/AppWindowToken.java b/com/android/server/wm/AppWindowToken.java index ea2f3059..a1eeff84 100644 --- a/com/android/server/wm/AppWindowToken.java +++ b/com/android/server/wm/AppWindowToken.java @@ -16,8 +16,7 @@ package com.android.server.wm; -import static android.app.ActivityManager.StackId; -import static android.app.ActivityManager.StackId.PINNED_STACK_ID; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; @@ -53,6 +52,7 @@ import static com.android.server.wm.WindowManagerService.logWithStack; import static com.android.server.wm.proto.AppWindowTokenProto.NAME; import static com.android.server.wm.proto.AppWindowTokenProto.WINDOW_TOKEN; +import android.annotation.CallSuper; import android.annotation.NonNull; import android.app.Activity; import android.content.res.Configuration; @@ -1170,7 +1170,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree wAppAnimator.thumbnail.destroy(); } wAppAnimator.thumbnail = tAppAnimator.thumbnail; - wAppAnimator.thumbnailLayer = tAppAnimator.thumbnailLayer; wAppAnimator.thumbnailAnimation = tAppAnimator.thumbnailAnimation; tAppAnimator.thumbnail = null; } @@ -1311,7 +1310,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // Notify the pinned stack upon all windows drawn. If there was an animation in // progress then this signal will resume that animation. - final TaskStack pinnedStack = mDisplayContent.getStackById(PINNED_STACK_ID); + final TaskStack pinnedStack = + mDisplayContent.getStack(WINDOWING_MODE_PINNED); if (pinnedStack != null) { pinnedStack.onAllWindowsDrawn(); } @@ -1620,8 +1620,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } } + @CallSuper @Override - void writeToProto(ProtoOutputStream proto, long fieldId) { + public void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); writeNameToProto(proto, NAME); super.writeToProto(proto, WINDOW_TOKEN); diff --git a/com/android/server/wm/ConfigurationContainer.java b/com/android/server/wm/ConfigurationContainer.java index 28ba9b3d..9e028d38 100644 --- a/com/android/server/wm/ConfigurationContainer.java +++ b/com/android/server/wm/ConfigurationContainer.java @@ -21,12 +21,19 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.app.WindowConfiguration.activityTypeToString; +import static com.android.server.wm.proto.ConfigurationContainerProto.FULL_CONFIGURATION; +import static com.android.server.wm.proto.ConfigurationContainerProto.MERGED_OVERRIDE_CONFIGURATION; +import static com.android.server.wm.proto.ConfigurationContainerProto.OVERRIDE_CONFIGURATION; +import android.annotation.CallSuper; import android.app.WindowConfiguration; import android.content.res.Configuration; +import android.util.proto.ProtoOutputStream; import java.util.ArrayList; @@ -147,6 +154,17 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { onOverrideConfigurationChanged(mTmpConfig); } + /** + * Returns true if this container is currently in multi-window mode. I.e. sharing the screen + * with another activity. + */ + public boolean inMultiWindowMode() { + /*@WindowConfiguration.WindowingMode*/ int windowingMode = + mFullConfiguration.windowConfiguration.getWindowingMode(); + return windowingMode != WINDOWING_MODE_FULLSCREEN + && windowingMode != WINDOWING_MODE_UNDEFINED; + } + /** Returns true if this container is currently in split-screen windowing mode. */ public boolean inSplitScreenWindowingMode() { /*@WindowConfiguration.WindowingMode*/ int windowingMode = @@ -170,7 +188,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { * {@link WindowConfiguration##WINDOWING_MODE_SPLIT_SCREEN_SECONDARY} windowing modes based on * its current state. */ - public boolean supportSplitScreenWindowingMode() { + public boolean supportsSplitScreenWindowingMode() { return mFullConfiguration.windowConfiguration.supportSplitScreenWindowingMode(); } @@ -220,9 +238,48 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { /*@WindowConfiguration.ActivityType*/ int thisType = getActivityType(); /*@WindowConfiguration.ActivityType*/ int otherType = other.getActivityType(); - return thisType == otherType - || thisType == ACTIVITY_TYPE_UNDEFINED - || otherType == ACTIVITY_TYPE_UNDEFINED; + if (thisType == otherType) { + return true; + } + if (thisType == ACTIVITY_TYPE_ASSISTANT) { + // Assistant activities are only compatible with themselves... + return false; + } + // Otherwise we are compatible if us or other is not currently defined. + return thisType == ACTIVITY_TYPE_UNDEFINED || otherType == ACTIVITY_TYPE_UNDEFINED; + } + + /** + * Returns true if this container is compatible with the input windowing mode and activity type. + * The container is compatible: + * - If {@param activityType} and {@param windowingMode} match this container activity type and + * windowing mode. + * - If {@param activityType} is {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or + * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} and this containers activity type is also + * standard or undefined and its windowing mode matches {@param windowingMode}. + * - If {@param activityType} isn't {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or + * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} or this containers activity type isn't + * also standard or undefined and its activity type matches {@param activityType} regardless of + * if {@param windowingMode} matches the containers windowing mode. + */ + public boolean isCompatible(int windowingMode, int activityType) { + final int thisActivityType = getActivityType(); + final int thisWindowingMode = getWindowingMode(); + final boolean sameActivityType = thisActivityType == activityType; + final boolean sameWindowingMode = thisWindowingMode == windowingMode; + + if (sameActivityType && sameWindowingMode) { + return true; + } + + if ((activityType != ACTIVITY_TYPE_UNDEFINED && activityType != ACTIVITY_TYPE_STANDARD) + || !isActivityTypeStandardOrUndefined()) { + // Only activity type need to match for non-standard activity types that are defined. + return sameActivityType; + } + + // Otherwise we are compatible if the windowing mode is the same. + return sameWindowingMode; } public void registerConfigurationChangeListener(ConfigurationContainerListener listener) { @@ -252,6 +309,24 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { } } + /** + * Write to a protocol buffer output stream. Protocol buffer message definition is at + * {@link com.android.server.wm.proto.ConfigurationContainerProto}. + * + * @param protoOutputStream Stream to write the ConfigurationContainer object to. + * @param fieldId Field Id of the ConfigurationContainer as defined in the parent + * message. + * @hide + */ + @CallSuper + public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) { + final long token = protoOutputStream.start(fieldId); + mOverrideConfiguration.writeToProto(protoOutputStream, OVERRIDE_CONFIGURATION); + mFullConfiguration.writeToProto(protoOutputStream, FULL_CONFIGURATION); + mMergedOverrideConfiguration.writeToProto(protoOutputStream, MERGED_OVERRIDE_CONFIGURATION); + protoOutputStream.end(token); + } + abstract protected int getChildCount(); abstract protected E getChildAt(int index); diff --git a/com/android/server/wm/DisplayContent.java b/com/android/server/wm/DisplayContent.java index 6cf608ab..0e68a8f6 100644 --- a/com/android/server/wm/DisplayContent.java +++ b/com/android/server/wm/DisplayContent.java @@ -18,9 +18,11 @@ package com.android.server.wm; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -112,10 +114,11 @@ import static com.android.server.wm.proto.DisplayProto.PINNED_STACK_CONTROLLER; import static com.android.server.wm.proto.DisplayProto.ROTATION; import static com.android.server.wm.proto.DisplayProto.SCREEN_ROTATION_ANIMATION; import static com.android.server.wm.proto.DisplayProto.STACKS; +import static com.android.server.wm.proto.DisplayProto.WINDOW_CONTAINER; +import android.annotation.CallSuper; import android.annotation.NonNull; import android.app.ActivityManager.StackId; -import android.app.WindowConfiguration; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; @@ -389,10 +392,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo != mTmpWindowAnimator.mAnimTransactionSequence) { appAnimator.thumbnailTransactionSeq = mTmpWindowAnimator.mAnimTransactionSequence; - appAnimator.thumbnailLayer = 0; - } - if (appAnimator.thumbnailLayer < winAnimator.mAnimLayer) { - appAnimator.thumbnailLayer = winAnimator.mAnimLayer; } } }; @@ -966,10 +965,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final int lastOrientation = mLastOrientation; final boolean oldAltOrientation = mAltOrientation; int rotation = mService.mPolicy.rotationForOrientationLw(lastOrientation, oldRotation); - final boolean rotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(oldRotation, + boolean mayRotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(oldRotation, rotation); - if (rotateSeamlessly) { + if (mayRotateSeamlessly) { final WindowState seamlessRotated = getWindow((w) -> w.mSeamlesslyRotated); if (seamlessRotated != null) { // We can't rotate (seamlessly or not) while waiting for the last seamless rotation @@ -978,7 +977,20 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // window-removal. return false; } + + // In the presence of the PINNED stack or System Alert + // windows we unforuntately can not seamlessly rotate. + if (getStackById(PINNED_STACK_ID) != null) { + mayRotateSeamlessly = false; + } + for (int i = 0; i < mService.mSessions.size(); i++) { + if (mService.mSessions.valueAt(i).hasAlertWindowSurfaces()) { + mayRotateSeamlessly = false; + break; + } + } } + final boolean rotateSeamlessly = mayRotateSeamlessly; // TODO: Implement forced rotation changes. // Set mAltOrientation to indicate that the application is receiving @@ -1455,16 +1467,38 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return null; } + /** + * Returns the topmost stack on the display that is compatible with the input windowing mode. + * Null is no compatible stack on the display. + */ + TaskStack getStack(int windowingMode) { + return getStack(windowingMode, ACTIVITY_TYPE_UNDEFINED); + } + + /** + * Returns the topmost stack on the display that is compatible with the input windowing mode and + * activity type. Null is no compatible stack on the display. + */ + TaskStack getStack(int windowingMode, int activityType) { + for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) { + final TaskStack stack = mTaskStackContainers.get(i); + if (stack.isCompatible(windowingMode, activityType)) { + return stack; + } + } + return null; + } + @VisibleForTesting int getStackCount() { return mTaskStackContainers.size(); } @VisibleForTesting - int getStackPosById(int stackId) { + int getStackPosition(int windowingMode, int activityType) { for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) { final TaskStack stack = mTaskStackContainers.get(i); - if (stack.mStackId == stackId) { + if (stack.isCompatible(windowingMode, activityType)) { return i; } } @@ -1499,7 +1533,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // If there was no pinned stack, we still need to notify the controller of the display info // update as a result of the config change. We do this here to consolidate the flow between // changes when there is and is not a stack. - if (getStackById(PINNED_STACK_ID) == null) { + if (getStack(WINDOWING_MODE_PINNED) == null) { mPinnedStackControllerLocked.onDisplayInfoChanged(); } } @@ -1720,21 +1754,22 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo out.set(mContentRect); } - TaskStack addStackToDisplay(int stackId, boolean onTop) { + TaskStack addStackToDisplay(int stackId, boolean onTop, StackWindowController controller) { if (DEBUG_STACK) Slog.d(TAG_WM, "Create new stackId=" + stackId + " on displayId=" + mDisplayId); TaskStack stack = getStackById(stackId); if (stack != null) { - // It's already attached to the display...clear mDeferRemoval and move stack to - // appropriate z-order on display as needed. + // It's already attached to the display...clear mDeferRemoval, set controller, and move + // stack to appropriate z-order on display as needed. stack.mDeferRemoval = false; + stack.setController(controller); // We're not moving the display to front when we're adding stacks, only when // requested to change the position of stack explicitly. mTaskStackContainers.positionChildAt(onTop ? POSITION_TOP : POSITION_BOTTOM, stack, false /* includingParents */); } else { - stack = new TaskStack(mService, stackId); + stack = new TaskStack(mService, stackId, controller); mTaskStackContainers.addStackToDisplay(stack, onTop); } @@ -2023,8 +2058,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) { final TaskStack stack = mTaskStackContainers.get(i); final boolean isDockedOnBottom = stack.getDockSide() == DOCKED_BOTTOM; - if (stack.isVisible() && (imeOnBottom || isDockedOnBottom) && - StackId.isStackAffectedByDragResizing(stack.mStackId)) { + if (stack.isVisible() && (imeOnBottom || isDockedOnBottom) + && stack.inSplitScreenWindowingMode()) { stack.setAdjustedForIme(imeWin, imeOnBottom && imeHeightChanged); } else { stack.resetAdjustedForIme(false); @@ -2119,8 +2154,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } - void writeToProto(ProtoOutputStream proto, long fieldId) { + @CallSuper + @Override + public void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); + super.writeToProto(proto, WINDOW_CONTAINER); proto.write(ID, mDisplayId); for (int stackNdx = mTaskStackContainers.size() - 1; stackNdx >= 0; --stackNdx) { final TaskStack stack = mTaskStackContainers.get(stackNdx); @@ -2232,7 +2270,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * @return The docked stack, but only if it is visible, and {@code null} otherwise. */ TaskStack getDockedStackLocked() { - final TaskStack stack = getStackById(DOCKED_STACK_ID); + final TaskStack stack = getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); return (stack != null && stack.isVisible()) ? stack : null; } @@ -2241,7 +2279,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * visible. */ TaskStack getDockedStackIgnoringVisibility() { - return getStackById(DOCKED_STACK_ID); + return getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); } /** Find the visible, touch-deliverable window under the given point */ @@ -3350,7 +3388,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * @see WindowManagerService#addStackToDisplay(int, int, boolean) */ void addStackToDisplay(TaskStack stack, boolean onTop) { - if (stack.mStackId == HOME_STACK_ID) { + if (stack.isActivityTypeHome()) { if (mHomeStack != null) { throw new IllegalArgumentException("attachStack: HOME_STACK_ID (0) not first."); } @@ -3415,11 +3453,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo : requestedPosition >= topChildPosition; int targetPosition = requestedPosition; - if (toTop && stack.mStackId != PINNED_STACK_ID - && getStackById(PINNED_STACK_ID) != null) { + if (toTop && stack.getWindowingMode() != WINDOWING_MODE_PINNED + && getStack(WINDOWING_MODE_PINNED) != null) { // The pinned stack is always the top most stack (always-on-top) when it is present. TaskStack topStack = mChildren.get(topChildPosition); - if (topStack.mStackId != PINNED_STACK_ID) { + if (topStack.getWindowingMode() != WINDOWING_MODE_PINNED) { throw new IllegalStateException("Pinned stack isn't top stack??? " + mChildren); } diff --git a/com/android/server/wm/DockedStackDividerController.java b/com/android/server/wm/DockedStackDividerController.java index 030b986e..6f441b98 100644 --- a/com/android/server/wm/DockedStackDividerController.java +++ b/com/android/server/wm/DockedStackDividerController.java @@ -17,8 +17,10 @@ package com.android.server.wm; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; -import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.view.Surface.ROTATION_270; @@ -330,7 +332,7 @@ public class DockedStackDividerController implements DimLayerUser { mLastVisibility = visible; notifyDockedDividerVisibilityChanged(visible); if (!visible) { - setResizeDimLayer(false, INVALID_STACK_ID, 0f); + setResizeDimLayer(false, WINDOWING_MODE_UNDEFINED, 0f); } } @@ -456,7 +458,8 @@ public class DockedStackDividerController implements DimLayerUser { boolean isHomeStackResizable) { long animDuration = 0; if (animate) { - final TaskStack stack = mDisplayContent.getStackById(DOCKED_STACK_ID); + final TaskStack stack = + mDisplayContent.getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); final long transitionDuration = isAnimationMaximizing() ? mService.mAppTransition.getLastClipRevealTransitionDuration() : DEFAULT_APP_TRANSITION_DURATION; @@ -517,9 +520,18 @@ public class DockedStackDividerController implements DimLayerUser { } - void setResizeDimLayer(boolean visible, int targetStackId, float alpha) { + /** + * Shows a dim layer with {@param alpha} if {@param visible} is true and + * {@param targetWindowingMode} isn't + * {@link android.app.WindowConfiguration#WINDOWING_MODE_UNDEFINED} and there is a stack on the + * display in that windowing mode. + */ + void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) { mService.openSurfaceTransaction(); - final TaskStack stack = mDisplayContent.getStackById(targetStackId); + // TODO: Maybe only allow split-screen windowing modes? + final TaskStack stack = targetWindowingMode != WINDOWING_MODE_UNDEFINED + ? mDisplayContent.getStack(targetWindowingMode) + : null; final TaskStack dockedStack = mDisplayContent.getDockedStackLocked(); boolean visibleAndValid = visible && stack != null && dockedStack != null; if (visibleAndValid) { @@ -605,8 +617,8 @@ public class DockedStackDividerController implements DimLayerUser { if (mMinimizedDock && mService.mPolicy.isKeyguardShowingAndNotOccluded()) { return; } - final TaskStack fullscreenStack = - mDisplayContent.getStackById(FULLSCREEN_WORKSPACE_STACK_ID); + final TaskStack fullscreenStack = mDisplayContent.getStack( + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); final boolean homeVisible = homeTask.getTopVisibleAppToken() != null; final boolean homeBehind = fullscreenStack != null && fullscreenStack.isVisible(); setMinimizedDockedStack(homeVisible && !homeBehind, animate); @@ -801,7 +813,8 @@ public class DockedStackDividerController implements DimLayerUser { } private boolean animateForMinimizedDockedStack(long now) { - final TaskStack stack = mDisplayContent.getStackById(DOCKED_STACK_ID); + final TaskStack stack = + mDisplayContent.getStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); if (!mAnimationStarted) { mAnimationStarted = true; mAnimationStartTime = now; diff --git a/com/android/server/wm/DragResizeMode.java b/com/android/server/wm/DragResizeMode.java index 8ab04065..c0bf1e89 100644 --- a/com/android/server/wm/DragResizeMode.java +++ b/com/android/server/wm/DragResizeMode.java @@ -16,11 +16,7 @@ package com.android.server.wm; -import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; -import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.HOME_STACK_ID; -import static android.app.ActivityManager.StackId.RECENTS_STACK_ID; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; /** * Describes the mode in which a window is drag resizing. @@ -39,15 +35,12 @@ class DragResizeMode { */ static final int DRAG_RESIZE_MODE_DOCKED_DIVIDER = 1; - static boolean isModeAllowedForStack(int stackId, int mode) { + static boolean isModeAllowedForStack(TaskStack stack, int mode) { switch (mode) { case DRAG_RESIZE_MODE_FREEFORM: - return stackId == FREEFORM_WORKSPACE_STACK_ID; + return stack.getWindowingMode() == WINDOWING_MODE_FREEFORM; case DRAG_RESIZE_MODE_DOCKED_DIVIDER: - return stackId == DOCKED_STACK_ID - || stackId == FULLSCREEN_WORKSPACE_STACK_ID - || stackId == HOME_STACK_ID - || stackId == RECENTS_STACK_ID; + return stack.inSplitScreenWindowingMode(); default: return false; } diff --git a/com/android/server/wm/PinnedStackController.java b/com/android/server/wm/PinnedStackController.java index 1e7140a1..ef31598f 100644 --- a/com/android/server/wm/PinnedStackController.java +++ b/com/android/server/wm/PinnedStackController.java @@ -16,7 +16,8 @@ package com.android.server.wm; -import static android.app.ActivityManager.StackId.PINNED_STACK_ID; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.util.TypedValue.COMPLEX_UNIT_DIP; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -404,27 +405,29 @@ class PinnedStackController { */ private void notifyMovementBoundsChanged(boolean fromImeAdjustement) { synchronized (mService.mWindowMap) { - if (mPinnedStackListener != null) { - try { - final Rect insetBounds = new Rect(); - getInsetBounds(insetBounds); - final Rect normalBounds = getDefaultBounds(); - if (isValidPictureInPictureAspectRatio(mAspectRatio)) { - transformBoundsToAspectRatio(normalBounds, mAspectRatio, - false /* useCurrentMinEdgeSize */); - } - final Rect animatingBounds = mTmpAnimatingBoundsRect; - final TaskStack pinnedStack = mDisplayContent.getStackById(PINNED_STACK_ID); - if (pinnedStack != null) { - pinnedStack.getAnimationOrCurrentBounds(animatingBounds); - } else { - animatingBounds.set(normalBounds); - } - mPinnedStackListener.onMovementBoundsChanged(insetBounds, normalBounds, - animatingBounds, fromImeAdjustement, mDisplayInfo.rotation); - } catch (RemoteException e) { - Slog.e(TAG_WM, "Error delivering actions changed event.", e); + if (mPinnedStackListener == null) { + return; + } + try { + final Rect insetBounds = new Rect(); + getInsetBounds(insetBounds); + final Rect normalBounds = getDefaultBounds(); + if (isValidPictureInPictureAspectRatio(mAspectRatio)) { + transformBoundsToAspectRatio(normalBounds, mAspectRatio, + false /* useCurrentMinEdgeSize */); + } + final Rect animatingBounds = mTmpAnimatingBoundsRect; + final TaskStack pinnedStack = + mDisplayContent.getStack(WINDOWING_MODE_PINNED); + if (pinnedStack != null) { + pinnedStack.getAnimationOrCurrentBounds(animatingBounds); + } else { + animatingBounds.set(normalBounds); } + mPinnedStackListener.onMovementBoundsChanged(insetBounds, normalBounds, + animatingBounds, fromImeAdjustement, mDisplayInfo.rotation); + } catch (RemoteException e) { + Slog.e(TAG_WM, "Error delivering actions changed event.", e); } } } @@ -491,7 +494,7 @@ class PinnedStackController { pw.println(prefix + "PinnedStackController"); pw.print(prefix + " defaultBounds="); getDefaultBounds().printShortString(pw); pw.println(); - mService.getStackBounds(PINNED_STACK_ID, mTmpRect); + mService.getStackBounds(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mTmpRect); pw.print(prefix + " movementBounds="); getMovementBounds(mTmpRect).printShortString(pw); pw.println(); pw.println(prefix + " mIsImeShowing=" + mIsImeShowing); @@ -512,7 +515,7 @@ class PinnedStackController { void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); getDefaultBounds().writeToProto(proto, DEFAULT_BOUNDS); - mService.getStackBounds(PINNED_STACK_ID, mTmpRect); + mService.getStackBounds(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mTmpRect); getMovementBounds(mTmpRect).writeToProto(proto, MOVEMENT_BOUNDS); proto.end(token); } diff --git a/com/android/server/wm/PinnedStackWindowController.java b/com/android/server/wm/PinnedStackWindowController.java index 590ac6ed..41f076d7 100644 --- a/com/android/server/wm/PinnedStackWindowController.java +++ b/com/android/server/wm/PinnedStackWindowController.java @@ -16,19 +16,16 @@ package com.android.server.wm; -import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; - +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS; import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END; import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START; import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState; import android.app.RemoteAction; -import android.content.res.Configuration; import android.graphics.Rect; -import com.android.server.UiThread; - import java.util.List; /** @@ -40,8 +37,8 @@ public class PinnedStackWindowController extends StackWindowController { private Rect mTmpToBounds = new Rect(); public PinnedStackWindowController(int stackId, PinnedStackWindowListener listener, - int displayId, boolean onTop, Rect outBounds) { - super(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance()); + int displayId, boolean onTop, Rect outBounds, WindowManagerService service) { + super(stackId, listener, displayId, onTop, outBounds, service); } /** @@ -101,7 +98,8 @@ public class PinnedStackWindowController extends StackWindowController { } schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START; - mService.getStackBounds(FULLSCREEN_WORKSPACE_STACK_ID, mTmpToBounds); + mService.getStackBounds( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mTmpToBounds); if (!mTmpToBounds.isEmpty()) { // If there is a fullscreen bounds, use that toBounds = new Rect(mTmpToBounds); diff --git a/com/android/server/wm/RootWindowContainer.java b/com/android/server/wm/RootWindowContainer.java index 8a749762..7832f5de 100644 --- a/com/android/server/wm/RootWindowContainer.java +++ b/com/android/server/wm/RootWindowContainer.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import android.annotation.CallSuper; import android.content.res.Configuration; import android.graphics.Rect; import android.hardware.power.V1_0.PowerHint; @@ -89,8 +90,9 @@ import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN; import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION; import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING; import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE; -import static com.android.server.wm.proto.WindowManagerServiceProto.DISPLAYS; -import static com.android.server.wm.proto.WindowManagerServiceProto.WINDOWS; +import static com.android.server.wm.proto.RootWindowContainerProto.DISPLAYS; +import static com.android.server.wm.proto.RootWindowContainerProto.WINDOWS; +import static com.android.server.wm.proto.RootWindowContainerProto.WINDOW_CONTAINER; /** Root {@link WindowContainer} for the device. */ class RootWindowContainer extends WindowContainer<DisplayContent> { @@ -420,6 +422,17 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { return null; } + TaskStack getStack(int windowingMode, int activityType) { + for (int i = mChildren.size() - 1; i >= 0; i--) { + final DisplayContent dc = mChildren.get(i); + final TaskStack stack = dc.getStack(windowingMode, activityType); + if (stack != null) { + return stack; + } + } + return null; + } + void setSecureSurfaceState(int userId, boolean disabled) { forAllWindows((w) -> { if (w.mHasSurface && userId == UserHandle.getUserId(w.mOwnerUid)) { @@ -1077,7 +1090,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { } } - void writeToProto(ProtoOutputStream proto) { + @CallSuper + @Override + public void writeToProto(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + super.writeToProto(proto, WINDOW_CONTAINER); if (mService.mDisplayReady) { final int count = mChildren.size(); for (int i = 0; i < count; ++i) { @@ -1088,6 +1105,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { forAllWindows((w) -> { w.writeIdentifierToProto(proto, WINDOWS); }, true); + proto.end(token); } @Override diff --git a/com/android/server/wm/Session.java b/com/android/server/wm/Session.java index 17812477..4dd147e5 100644 --- a/com/android/server/wm/Session.java +++ b/com/android/server/wm/Session.java @@ -719,4 +719,8 @@ public class Session extends IWindowSession.Stub public String toString() { return mStringName; } + + boolean hasAlertWindowSurfaces() { + return !mAlertWindowSurfaces.isEmpty(); + } } diff --git a/com/android/server/wm/StackWindowController.java b/com/android/server/wm/StackWindowController.java index a50ed71a..c0a4cb72 100644 --- a/com/android/server/wm/StackWindowController.java +++ b/com/android/server/wm/StackWindowController.java @@ -18,8 +18,6 @@ package com.android.server.wm; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; -import android.app.ActivityManager.StackId; -import android.app.WindowConfiguration; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Handler; @@ -76,8 +74,7 @@ public class StackWindowController + " to unknown displayId=" + displayId); } - final TaskStack stack = dc.addStackToDisplay(stackId, onTop); - stack.setController(this); + dc.addStackToDisplay(stackId, onTop, this); getRawBounds(outBounds); } } diff --git a/com/android/server/wm/Task.java b/com/android/server/wm/Task.java index 55b6c912..7e8d1308 100644 --- a/com/android/server/wm/Task.java +++ b/com/android/server/wm/Task.java @@ -23,6 +23,7 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSC import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; +import static android.content.res.Configuration.EMPTY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static com.android.server.EventLogTags.WM_TASK_REMOVED; @@ -34,8 +35,9 @@ import static com.android.server.wm.proto.TaskProto.BOUNDS; import static com.android.server.wm.proto.TaskProto.FILLS_PARENT; import static com.android.server.wm.proto.TaskProto.ID; import static com.android.server.wm.proto.TaskProto.TEMP_INSET_BOUNDS; +import static com.android.server.wm.proto.TaskProto.WINDOW_CONTAINER; -import android.app.ActivityManager.StackId; +import android.annotation.CallSuper; import android.app.ActivityManager.TaskDescription; import android.content.pm.ActivityInfo; import android.content.res.Configuration; @@ -278,15 +280,9 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU // WindowConfiguration long term. private int setBounds(Rect bounds, Configuration overrideConfig) { if (overrideConfig == null) { - overrideConfig = Configuration.EMPTY; - } - if (bounds == null && !Configuration.EMPTY.equals(overrideConfig)) { - throw new IllegalArgumentException("null bounds but non empty configuration: " - + overrideConfig); - } - if (bounds != null && Configuration.EMPTY.equals(overrideConfig)) { - throw new IllegalArgumentException("non null bounds, but empty configuration"); + overrideConfig = EMPTY; } + boolean oldFullscreen = mFillsParent; int rotation = Surface.ROTATION_0; final DisplayContent displayContent = mStack.getDisplayContent(); @@ -321,7 +317,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU if (displayContent != null) { displayContent.mDimLayerController.updateDimLayer(this); } - onOverrideConfigurationChanged(mFillsParent ? Configuration.EMPTY : overrideConfig); + onOverrideConfigurationChanged(overrideConfig); return boundsChange; } @@ -404,7 +400,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU * the adjusted bounds's top. */ void alignToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds, boolean alignBottom) { - if (!isResizeable() || Configuration.EMPTY.equals(getOverrideConfiguration())) { + if (!isResizeable() || EMPTY.equals(getOverrideConfiguration())) { return; } @@ -529,7 +525,7 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU void setDragResizing(boolean dragResizing, int dragResizeMode) { if (mDragResizing != dragResizing) { - if (!DragResizeMode.isModeAllowedForStack(mStack.mStackId, dragResizeMode)) { + if (!DragResizeMode.isModeAllowedForStack(mStack, dragResizeMode)) { throw new IllegalArgumentException("Drag resize mode not allow for stack stackId=" + mStack.mStackId + " dragResizeMode=" + dragResizeMode); } @@ -552,7 +548,9 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU return; } if (mFillsParent) { - setBounds(null, Configuration.EMPTY); + // TODO: Yeah...not sure if this works with WindowConfiguration, but shouldn't be a + // problem once we move mBounds into WindowConfiguration. + setBounds(null, getOverrideConfiguration()); return; } final int newRotation = displayContent.getDisplayInfo().rotation; @@ -735,8 +733,11 @@ class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerU return "Task=" + mTaskId; } - void writeToProto(ProtoOutputStream proto, long fieldId) { + @CallSuper + @Override + public void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); + super.writeToProto(proto, WINDOW_CONTAINER); proto.write(ID, mTaskId); for (int i = mChildren.size() - 1; i >= 0; i--) { final AppWindowToken appWindowToken = mChildren.get(i); diff --git a/com/android/server/wm/TaskSnapshotController.java b/com/android/server/wm/TaskSnapshotController.java index 46324022..bff24f6e 100644 --- a/com/android/server/wm/TaskSnapshotController.java +++ b/com/android/server/wm/TaskSnapshotController.java @@ -254,7 +254,7 @@ class TaskSnapshotController { @VisibleForTesting int getSnapshotMode(Task task) { final AppWindowToken topChild = task.getTopChild(); - if (StackId.isHomeOrRecentsStack(task.mStack.mStackId)) { + if (!task.isActivityTypeStandardOrUndefined()) { return SNAPSHOT_MODE_NONE; } else if (topChild != null && topChild.shouldUseAppThemeSnapshot()) { return SNAPSHOT_MODE_APP_THEME; @@ -297,7 +297,7 @@ class TaskSnapshotController { return new TaskSnapshot(hwBitmap.createGraphicBufferHandle(), topChild.getConfiguration().orientation, mainWindow.mStableInsets, - false /* reduced */, 1.0f /* scale */); + ActivityManager.isLowRamDeviceStatic() /* reduced */, 1.0f /* scale */); } /** diff --git a/com/android/server/wm/TaskStack.java b/com/android/server/wm/TaskStack.java index 4664dcbb..65278837 100644 --- a/com/android/server/wm/TaskStack.java +++ b/com/android/server/wm/TaskStack.java @@ -19,8 +19,11 @@ package com.android.server.wm; import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; -import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; @@ -41,8 +44,9 @@ import static com.android.server.wm.proto.StackProto.BOUNDS; import static com.android.server.wm.proto.StackProto.FILLS_PARENT; import static com.android.server.wm.proto.StackProto.ID; import static com.android.server.wm.proto.StackProto.TASKS; +import static com.android.server.wm.proto.StackProto.WINDOW_CONTAINER; -import android.app.ActivityManager.StackId; +import android.annotation.CallSuper; import android.content.res.Configuration; import android.graphics.Rect; import android.graphics.Region; @@ -147,9 +151,10 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye Rect mPreAnimationBounds = new Rect(); - TaskStack(WindowManagerService service, int stackId) { + TaskStack(WindowManagerService service, int stackId, StackWindowController controller) { mService = service; mStackId = stackId; + setController(controller); mDockedStackMinimizeThickness = service.mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.docked_stack_minimize_thickness); EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId); @@ -733,7 +738,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye outTempTaskBounds.setEmpty(); // When the home stack is resizable, should always have the same stack and task bounds - if (mStackId == HOME_STACK_ID) { + if (isActivityTypeHome()) { final Task homeTask = findHomeTask(); if (homeTask != null && homeTask.isResizeable()) { // Calculate the home stack bounds when in docked mode and the home stack is @@ -918,7 +923,9 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye void resetAnimationBackgroundAnimator() { mAnimationBackgroundAnimator = null; - mAnimationBackgroundSurface.hide(); + if (mAnimationBackgroundSurface != null) { + mAnimationBackgroundSurface.hide(); + } } void setAnimationBackground(WindowStateAnimator winAnimator, int color) { @@ -1005,7 +1012,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye mAdjustImeAmount = 0f; mAdjustDividerAmount = 0f; updateAdjustedBounds(); - mService.setResizeDimLayer(false, mStackId, 1.0f); + mService.setResizeDimLayer(false, getWindowingMode(), 1.0f); } else { mImeGoingAway |= mAdjustedForIme; } @@ -1201,7 +1208,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye if (mAdjustedForIme && adjust && !isImeTarget) { final float alpha = Math.max(mAdjustImeAmount, mAdjustDividerAmount) * IME_ADJUST_DIM_AMOUNT; - mService.setResizeDimLayer(true, mStackId, alpha); + mService.setResizeDimLayer(true, getWindowingMode(), alpha); } } @@ -1219,8 +1226,11 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye return mMinimizeAmount != 0f; } - void writeToProto(ProtoOutputStream proto, long fieldId) { + @CallSuper + @Override + public void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); + super.writeToProto(proto, WINDOW_CONTAINER); proto.write(ID, mStackId); for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) { mChildren.get(taskNdx).writeToProto(proto, TASKS); @@ -1277,7 +1287,7 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye @Override public boolean dimFullscreen() { - return StackId.isHomeOrRecentsStack(mStackId) || fillsParent(); + return !isActivityTypeStandard() || fillsParent(); } @Override @@ -1657,7 +1667,15 @@ public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLaye @Override int getOrientation() { - return (StackId.canSpecifyOrientation(mStackId)) - ? super.getOrientation() : SCREEN_ORIENTATION_UNSET; + return (canSpecifyOrientation()) ? super.getOrientation() : SCREEN_ORIENTATION_UNSET; + } + + private boolean canSpecifyOrientation() { + final int windowingMode = getWindowingMode(); + final int activityType = getActivityType(); + return windowingMode == WINDOWING_MODE_FULLSCREEN + || activityType == ACTIVITY_TYPE_HOME + || activityType == ACTIVITY_TYPE_RECENTS + || activityType == ACTIVITY_TYPE_ASSISTANT; } } diff --git a/com/android/server/wm/WindowContainer.java b/com/android/server/wm/WindowContainer.java index 926719dd..40923c82 100644 --- a/com/android/server/wm/WindowContainer.java +++ b/com/android/server/wm/WindowContainer.java @@ -19,12 +19,14 @@ package com.android.server.wm; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static android.content.res.Configuration.EMPTY; +import static com.android.server.wm.proto.WindowContainerProto.CONFIGURATION_CONTAINER; +import static com.android.server.wm.proto.WindowContainerProto.ORIENTATION; import android.annotation.CallSuper; import android.content.res.Configuration; import android.util.Pools; +import android.util.proto.ProtoOutputStream; import com.android.internal.util.ToBooleanFunction; import java.util.Comparator; @@ -685,6 +687,23 @@ a * Returns whether this child is on top of the window hierarchy. } } + /** + * Write to a protocol buffer output stream. Protocol buffer message definition is at + * {@link com.android.server.wm.proto.WindowContainerProto}. + * + * @param protoOutputStream Stream to write the WindowContainer object to. + * @param fieldId Field Id of the WindowContainer as defined in the parent message. + * @hide + */ + @CallSuper + @Override + public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) { + final long token = protoOutputStream.start(fieldId); + super.writeToProto(protoOutputStream, CONFIGURATION_CONTAINER); + protoOutputStream.write(ORIENTATION, mOrientation); + protoOutputStream.end(token); + } + String getName() { return toString(); } diff --git a/com/android/server/wm/WindowLayersController.java b/com/android/server/wm/WindowLayersController.java index 857b13d2..7caf2fe9 100644 --- a/com/android/server/wm/WindowLayersController.java +++ b/com/android/server/wm/WindowLayersController.java @@ -21,11 +21,8 @@ import android.util.Slog; import java.util.ArrayDeque; import java.util.function.Consumer; -import static android.app.ActivityManager.StackId; -import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID; -import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; -import static android.app.ActivityManager.StackId.INVALID_STACK_ID; -import static android.app.ActivityManager.StackId.PINNED_STACK_ID; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS; @@ -187,12 +184,13 @@ class WindowLayersController { } } - final int stackId = w.getAppToken() != null ? w.getStackId() : INVALID_STACK_ID; - if (stackId == PINNED_STACK_ID) { + final int windowingMode = w.getWindowingMode(); + if (windowingMode == WINDOWING_MODE_PINNED) { mPinnedWindows.add(w); - } else if (stackId == DOCKED_STACK_ID) { + } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { mDockedWindows.add(w); - } else if (stackId == ASSISTANT_STACK_ID) { + } + if (w.isActivityTypeAssistant()) { mAssistantWindows.add(w); } } @@ -268,20 +266,8 @@ class WindowLayersController { w.mLayer = layer; w.mWinAnimator.mAnimLayer = w.getAnimLayerAdjustment() + w.getSpecialWindowAnimLayerAdjustment(); - if (w.mAppToken != null && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer > 0) { - if (w.mWinAnimator.mAnimLayer > w.mAppToken.mAppAnimator.thumbnailForceAboveLayer) { - w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = w.mWinAnimator.mAnimLayer; - } - // TODO(b/62029108): the entire contents of the if statement should call the refactored - // function to set the thumbnail layer for w.AppToken - int highestLayer = w.mAppToken.getHighestAnimLayer(); - if (highestLayer > 0) { - if (w.mAppToken.mAppAnimator.thumbnail != null - && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer != highestLayer) { - w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = highestLayer; - w.mAppToken.mAppAnimator.thumbnail.setLayer(highestLayer + 1); - } - } + if (w.mAppToken != null) { + w.mAppToken.mAppAnimator.updateThumbnailLayer(); } } } diff --git a/com/android/server/wm/WindowManagerService.java b/com/android/server/wm/WindowManagerService.java index 32ee51c8..1fb21887 100644 --- a/com/android/server/wm/WindowManagerService.java +++ b/com/android/server/wm/WindowManagerService.java @@ -109,6 +109,7 @@ import static com.android.server.wm.proto.WindowManagerServiceProto.FOCUSED_WIND import static com.android.server.wm.proto.WindowManagerServiceProto.INPUT_METHOD_WINDOW; import static com.android.server.wm.proto.WindowManagerServiceProto.LAST_ORIENTATION; import static com.android.server.wm.proto.WindowManagerServiceProto.POLICY; +import static com.android.server.wm.proto.WindowManagerServiceProto.ROOT_WINDOW_CONTAINER; import static com.android.server.wm.proto.WindowManagerServiceProto.ROTATION; import android.Manifest; @@ -246,6 +247,7 @@ import com.android.server.Watchdog; import com.android.server.input.InputManagerService; import com.android.server.power.BatterySaverPolicy.ServiceType; import com.android.server.power.ShutdownThread; +import com.android.server.utils.PriorityDump; import java.io.BufferedWriter; import java.io.DataInputStream; @@ -390,6 +392,18 @@ public class WindowManagerService extends IWindowManager.Stub }; final WindowSurfacePlacer mWindowPlacerLocked; + private final PriorityDump.PriorityDumper mPriorityDumper = new PriorityDump.PriorityDumper() { + @Override + public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args) { + doDump(fd, pw, new String[] {"-a"}); + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + doDump(fd, pw, args); + } + }; + /** * Current user when multi-user is enabled. Don't show windows of * non-current user. Also see mCurrentProfileIds. @@ -2029,10 +2043,14 @@ public class WindowManagerService extends IWindowManager.Stub Slog.i(TAG_WM, "Relayout " + win + ": oldVis=" + oldVisibility + " newVis=" + viewVisibility, stack); } - if (viewVisibility == View.VISIBLE && - (win.mAppToken == null || win.mAttrs.type == TYPE_APPLICATION_STARTING - || !win.mAppToken.isClientHidden())) { + // We should only relayout if the view is visible, it is a starting window, or the + // associated appToken is not hidden. + final boolean shouldRelayout = viewVisibility == View.VISIBLE && + (win.mAppToken == null || win.mAttrs.type == TYPE_APPLICATION_STARTING + || !win.mAppToken.isClientHidden()); + + if (shouldRelayout) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1"); // We are about to create a surface, but we didn't run a layout yet. So better run @@ -2179,8 +2197,18 @@ public class WindowManagerService extends IWindowManager.Stub // and needs process it before handling the corresponding window frame. the variable // {@code mergedConfiguration} is an out parameter that will be passed back to the // client over IPC and checked there. - win.getMergedConfiguration(mergedConfiguration); - win.setReportedConfiguration(mergedConfiguration); + // Note: in the cases where the window is tied to an activity, we should not send a + // configuration update when the window has requested to be hidden. Doing so can lead + // to the client erroneously accepting a configuration that would have otherwise caused + // an activity restart. We instead hand back the last reported + // {@link MergedConfiguration}. + if (shouldRelayout) { + win.getMergedConfiguration(mergedConfiguration); + } else { + win.getLastReportedMergedConfiguration(mergedConfiguration); + } + + win.setLastReportedMergedConfiguration(mergedConfiguration); outFrame.set(win.mCompatFrame); outOverscanInsets.set(win.mOverscanInsets); @@ -2881,9 +2909,9 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void getStackBounds(int stackId, Rect bounds) { + public void getStackBounds(int windowingMode, int activityType, Rect bounds) { synchronized (mWindowMap) { - final TaskStack stack = mRoot.getStackById(stackId); + final TaskStack stack = mRoot.getStack(windowingMode, activityType); if (stack != null) { stack.getBounds(bounds); return; @@ -6505,7 +6533,7 @@ public class WindowManagerService extends IWindowManager.Stub private void writeToProtoLocked(ProtoOutputStream proto) { mPolicy.writeToProto(proto, POLICY); - mRoot.writeToProto(proto); + mRoot.writeToProto(proto, ROOT_WINDOW_CONTAINER); if (mCurrentFocus != null) { mCurrentFocus.writeIdentifierToProto(proto, FOCUSED_WINDOW); } @@ -6793,8 +6821,11 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + PriorityDump.dump(mPriorityDumper, fd, pw, args); + } + private void doDump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; boolean dumpAll = false; boolean useProto = false; @@ -7124,10 +7155,10 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void setResizeDimLayer(boolean visible, int targetStackId, float alpha) { + public void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) { synchronized (mWindowMap) { getDefaultDisplayContentLocked().getDockedDividerController().setResizeDimLayer( - visible, targetStackId, alpha); + visible, targetWindowingMode, alpha); } } diff --git a/com/android/server/wm/WindowState.java b/com/android/server/wm/WindowState.java index 1b055664..4ff0f391 100644 --- a/com/android/server/wm/WindowState.java +++ b/com/android/server/wm/WindowState.java @@ -116,7 +116,9 @@ import static com.android.server.wm.proto.WindowStateProto.IDENTIFIER; import static com.android.server.wm.proto.WindowStateProto.PARENT_FRAME; import static com.android.server.wm.proto.WindowStateProto.STACK_ID; import static com.android.server.wm.proto.WindowStateProto.SURFACE_INSETS; +import static com.android.server.wm.proto.WindowStateProto.WINDOW_CONTAINER; +import android.annotation.CallSuper; import android.app.AppOpsManager; import android.content.Context; import android.content.res.Configuration; @@ -255,7 +257,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP * We'll send configuration to client only if it is different from the last applied one and * client won't perform unnecessary updates. */ - private final Configuration mLastReportedConfiguration = new Configuration(); + private final MergedConfiguration mLastReportedConfiguration = new MergedConfiguration(); /** * Actual position of the surface shown on-screen (may be modified by animation). These are @@ -1242,7 +1244,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // this is not necessarily what the client has processed yet. Find a // better indicator consistent with the client. return (mOrientationChanging || (isVisible() - && getConfiguration().orientation != mLastReportedConfiguration.orientation)) + && getConfiguration().orientation != getLastReportedConfiguration().orientation)) && !mSeamlesslyRotated && !mOrientationChangeTimedOut; } @@ -1756,7 +1758,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP /** Returns true if last applied config was not yet requested by client. */ boolean isConfigChanged() { - return !mLastReportedConfiguration.equals(getConfiguration()); + return !getLastReportedConfiguration().equals(getConfiguration()); } void onWindowReplacementTimeout() { @@ -2310,8 +2312,16 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP outConfiguration.setConfiguration(globalConfig, overrideConfig); } - void setReportedConfiguration(MergedConfiguration config) { - mLastReportedConfiguration.setTo(config.getMergedConfiguration()); + void setLastReportedMergedConfiguration(MergedConfiguration config) { + mLastReportedConfiguration.setTo(config); + } + + void getLastReportedMergedConfiguration(MergedConfiguration config) { + config.setTo(mLastReportedConfiguration); + } + + private Configuration getLastReportedConfiguration() { + return mLastReportedConfiguration.getMergedConfiguration(); } void adjustStartingWindowFlags() { @@ -2851,7 +2861,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP new MergedConfiguration(mService.mRoot.getConfiguration(), getMergedOverrideConfiguration()); - setReportedConfiguration(mergedConfiguration); + setLastReportedMergedConfiguration(mergedConfiguration); if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == DRAW_PENDING) Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING"); @@ -3078,7 +3088,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (task == null) { return false; } - if (!StackId.isStackAffectedByDragResizing(getStackId())) { + if (!inSplitScreenWindowingMode()) { return false; } if (mAttrs.width != MATCH_PARENT || mAttrs.height != MATCH_PARENT) { @@ -3124,8 +3134,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP || (isChildWindow() && getParentWindow().isDockedResizing()); } - void writeToProto(ProtoOutputStream proto, long fieldId) { + @CallSuper + @Override + public void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); + super.writeToProto(proto, WINDOW_CONTAINER); writeIdentifierToProto(proto, IDENTIFIER); proto.write(DISPLAY_ID, getDisplayId()); proto.write(STACK_ID, getStackId()); @@ -3168,7 +3181,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP pw.print(" mShowToOwnerOnly="); pw.print(mShowToOwnerOnly); pw.print(" package="); pw.print(mAttrs.packageName); pw.print(" appop="); pw.println(AppOpsManager.opToName(mAppOp)); - pw.print(prefix); pw.print("mAttrs="); pw.println(mAttrs); + pw.print(prefix); pw.print("mAttrs="); pw.println(mAttrs.toString(prefix)); pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth); pw.print(" h="); pw.print(mRequestedHeight); pw.print(" mLayoutSeq="); pw.println(mLayoutSeq); @@ -3249,7 +3262,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } pw.print(prefix); pw.print("mFullConfiguration="); pw.println(getConfiguration()); pw.print(prefix); pw.print("mLastReportedConfiguration="); - pw.println(mLastReportedConfiguration); + pw.println(getLastReportedConfiguration()); } pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface); pw.print(" mShownPosition="); mShownPosition.printShortString(pw); @@ -3309,7 +3322,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP pw.print(prefix); pw.print("mOrientationChanging="); pw.print(mOrientationChanging); pw.print(" configOrientationChanging="); - pw.print(mLastReportedConfiguration.orientation + pw.print(getLastReportedConfiguration().orientation != getConfiguration().orientation); pw.print(" mAppFreezing="); pw.print(mAppFreezing); pw.print(" mTurnOnScreen="); pw.print(mTurnOnScreen); diff --git a/com/android/server/wm/WindowSurfacePlacer.java b/com/android/server/wm/WindowSurfacePlacer.java index 88625d35..af1fa2fe 100644 --- a/com/android/server/wm/WindowSurfacePlacer.java +++ b/com/android/server/wm/WindowSurfacePlacer.java @@ -342,10 +342,7 @@ class WindowSurfacePlacer { mTmpLayerAndToken.token = null; handleClosingApps(transit, animLp, voiceInteraction, mTmpLayerAndToken); final AppWindowToken topClosingApp = mTmpLayerAndToken.token; - final int topClosingLayer = mTmpLayerAndToken.layer; - - final AppWindowToken topOpeningApp = handleOpeningApps(transit, - animLp, voiceInteraction, topClosingLayer); + final AppWindowToken topOpeningApp = handleOpeningApps(transit, animLp, voiceInteraction); mService.mAppTransition.setLastAppTransition(transit, topOpeningApp, topClosingApp); @@ -387,8 +384,9 @@ class WindowSurfacePlacer { } private AppWindowToken handleOpeningApps(int transit, LayoutParams animLp, - boolean voiceInteraction, int topClosingLayer) { + boolean voiceInteraction) { AppWindowToken topOpeningApp = null; + int topOpeningLayer = Integer.MIN_VALUE; final int appsCount = mService.mOpeningApps.size(); for (int i = 0; i < appsCount; i++) { AppWindowToken wtoken = mService.mOpeningApps.valueAt(i); @@ -422,7 +420,6 @@ class WindowSurfacePlacer { } mService.mAnimator.mAppWindowAnimating |= appAnimator.isAnimating(); - int topOpeningLayer = 0; if (animLp != null) { final int layer = wtoken.getHighestAnimLayer(); if (topOpeningApp == null || layer > topOpeningLayer) { @@ -431,7 +428,7 @@ class WindowSurfacePlacer { } } if (mService.mAppTransition.isNextAppTransitionThumbnailUp()) { - createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer); + createThumbnailAppAnimator(transit, wtoken); } } return topOpeningApp; @@ -473,7 +470,7 @@ class WindowSurfacePlacer { } } if (mService.mAppTransition.isNextAppTransitionThumbnailDown()) { - createThumbnailAppAnimator(transit, wtoken, 0, layerAndToken.layer); + createThumbnailAppAnimator(transit, wtoken); } } } @@ -666,8 +663,7 @@ class WindowSurfacePlacer { } } - private void createThumbnailAppAnimator(int transit, AppWindowToken appToken, - int openingLayer, int closingLayer) { + private void createThumbnailAppAnimator(int transit, AppWindowToken appToken) { AppWindowAnimator openingAppAnimator = (appToken == null) ? null : appToken.mAppAnimator; if (openingAppAnimator == null || openingAppAnimator.animation == null) { return; @@ -724,7 +720,6 @@ class WindowSurfacePlacer { anim = mService.mAppTransition.createThumbnailAspectScaleAnimationLocked(appRect, insets, thumbnailHeader, taskId, displayConfig.uiMode, displayConfig.orientation); - openingAppAnimator.thumbnailForceAboveLayer = Math.max(openingLayer, closingLayer); openingAppAnimator.deferThumbnailDestruction = !mService.mAppTransition.isNextThumbnailTransitionScaleUp(); } else { @@ -734,8 +729,8 @@ class WindowSurfacePlacer { anim.restrictDuration(MAX_ANIMATION_DURATION); anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked()); + openingAppAnimator.updateThumbnailLayer(); openingAppAnimator.thumbnail = surfaceControl; - openingAppAnimator.thumbnailLayer = openingLayer; openingAppAnimator.thumbnailAnimation = anim; mService.mAppTransition.getNextAppTransitionStartRect(taskId, mTmpStartRect); } catch (Surface.OutOfResourcesException e) { diff --git a/com/android/server/wm/WindowToken.java b/com/android/server/wm/WindowToken.java index 422615b1..943448ee 100644 --- a/com/android/server/wm/WindowToken.java +++ b/com/android/server/wm/WindowToken.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import android.annotation.CallSuper; import android.util.proto.ProtoOutputStream; import java.util.Comparator; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; @@ -28,6 +29,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import static com.android.server.wm.proto.WindowTokenProto.HASH_CODE; import static com.android.server.wm.proto.WindowTokenProto.WINDOWS; +import static com.android.server.wm.proto.WindowTokenProto.WINDOW_CONTAINER; import android.os.Debug; import android.os.IBinder; @@ -263,8 +265,11 @@ class WindowToken extends WindowContainer<WindowState> { super.onDisplayChanged(dc); } - void writeToProto(ProtoOutputStream proto, long fieldId) { + @CallSuper + @Override + public void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); + super.writeToProto(proto, WINDOW_CONTAINER); proto.write(HASH_CODE, System.identityHashCode(this)); for (int i = 0; i < mChildren.size(); i++) { final WindowState w = mChildren.get(i); diff --git a/com/android/settingslib/applications/ApplicationsState.java b/com/android/settingslib/applications/ApplicationsState.java index 40c2b1f3..fa2499f6 100644 --- a/com/android/settingslib/applications/ApplicationsState.java +++ b/com/android/settingslib/applications/ApplicationsState.java @@ -52,6 +52,11 @@ import android.util.SparseArray; import com.android.internal.R; import com.android.internal.util.ArrayUtils; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnDestroy; +import com.android.settingslib.core.lifecycle.events.OnPause; +import com.android.settingslib.core.lifecycle.events.OnResume; import java.io.File; import java.io.IOException; @@ -180,7 +185,11 @@ public class ApplicationsState { } public Session newSession(Callbacks callbacks) { - Session s = new Session(callbacks); + return newSession(callbacks, null); + } + + public Session newSession(Callbacks callbacks, Lifecycle lifecycle) { + Session s = new Session(callbacks, lifecycle); synchronized (mEntriesMap) { mSessions.add(s); } @@ -586,7 +595,7 @@ public class ApplicationsState { .replaceAll("").toLowerCase(); } - public class Session { + public class Session implements LifecycleObserver, OnPause, OnResume, OnDestroy { final Callbacks mCallbacks; boolean mResumed; @@ -600,11 +609,19 @@ public class ApplicationsState { ArrayList<AppEntry> mLastAppList; boolean mRebuildForeground; - Session(Callbacks callbacks) { + private final boolean mHasLifecycle; + + Session(Callbacks callbacks, Lifecycle lifecycle) { mCallbacks = callbacks; + if (lifecycle != null) { + lifecycle.addObserver(this); + mHasLifecycle = true; + } else { + mHasLifecycle = false; + } } - public void resume() { + public void onResume() { if (DEBUG_LOCKING) Log.v(TAG, "resume about to acquire lock..."); synchronized (mEntriesMap) { if (!mResumed) { @@ -616,7 +633,7 @@ public class ApplicationsState { if (DEBUG_LOCKING) Log.v(TAG, "...resume releasing lock"); } - public void pause() { + public void onPause() { if (DEBUG_LOCKING) Log.v(TAG, "pause about to acquire lock..."); synchronized (mEntriesMap) { if (mResumed) { @@ -735,8 +752,11 @@ public class ApplicationsState { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); } - public void release() { - pause(); + public void onDestroy() { + if (!mHasLifecycle) { + // TODO: Legacy, remove this later once all usages are switched to Lifecycle + onPause(); + } synchronized (mEntriesMap) { mSessions.remove(this); } diff --git a/com/android/settingslib/applications/StorageStatsSource.java b/com/android/settingslib/applications/StorageStatsSource.java index 8fc9fa6a..9fbadee3 100644 --- a/com/android/settingslib/applications/StorageStatsSource.java +++ b/com/android/settingslib/applications/StorageStatsSource.java @@ -131,7 +131,7 @@ public class StorageStatsSource { } public long getTotalBytes() { - return mStats.getCacheBytes() + mStats.getCodeBytes() + mStats.getDataBytes(); + return mStats.getAppBytes() + mStats.getDataBytes(); } } }
\ No newline at end of file diff --git a/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java b/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java index 47cbb77a..6aae226b 100644 --- a/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java +++ b/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java @@ -28,17 +28,20 @@ import android.support.v7.preference.PreferenceScreen; import android.support.v7.preference.TwoStatePreference; import android.text.TextUtils; -import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.ConfirmationDialogController; -public abstract class AbstractEnableAdbPreferenceController extends AbstractPreferenceController - implements ConfirmationDialogController { +public abstract class AbstractEnableAdbPreferenceController extends + DeveloperOptionsPreferenceController implements ConfirmationDialogController { private static final String KEY_ENABLE_ADB = "enable_adb"; public static final String ACTION_ENABLE_ADB_STATE_CHANGED = "com.android.settingslib.development.AbstractEnableAdbController." + "ENABLE_ADB_STATE_CHANGED"; - private SwitchPreference mPreference; + public static final int ADB_SETTING_ON = 1; + public static final int ADB_SETTING_OFF = 0; + + + protected SwitchPreference mPreference; public AbstractEnableAdbPreferenceController(Context context) { super(context); @@ -64,12 +67,13 @@ public abstract class AbstractEnableAdbPreferenceController extends AbstractPref private boolean isAdbEnabled() { final ContentResolver cr = mContext.getContentResolver(); - return Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0) != 0; + return Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, ADB_SETTING_OFF) + != ADB_SETTING_OFF; } @Override public void updateState(Preference preference) { - ((TwoStatePreference)preference).setChecked(isAdbEnabled()); + ((TwoStatePreference) preference).setChecked(isAdbEnabled()); } public void enablePreference(boolean enabled) { @@ -105,7 +109,7 @@ public abstract class AbstractEnableAdbPreferenceController extends AbstractPref protected void writeAdbSetting(boolean enabled) { Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.ADB_ENABLED, enabled ? 1 : 0); + Settings.Global.ADB_ENABLED, enabled ? ADB_SETTING_ON : ADB_SETTING_OFF); notifyStateChanged(); } diff --git a/com/android/settingslib/development/AbstractLogdSizePreferenceController.java b/com/android/settingslib/development/AbstractLogdSizePreferenceController.java index c1677236..7998b2ef 100644 --- a/com/android/settingslib/development/AbstractLogdSizePreferenceController.java +++ b/com/android/settingslib/development/AbstractLogdSizePreferenceController.java @@ -26,10 +26,9 @@ import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceScreen; import com.android.settingslib.R; -import com.android.settingslib.core.AbstractPreferenceController; -public abstract class AbstractLogdSizePreferenceController extends AbstractPreferenceController - implements Preference.OnPreferenceChangeListener { +public abstract class AbstractLogdSizePreferenceController extends + DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener { public static final String ACTION_LOGD_SIZE_UPDATED = "com.android.settingslib.development." + "AbstractLogdSizePreferenceController.LOGD_SIZE_UPDATED"; public static final String EXTRA_CURRENT_LOGD_VALUE = "CURRENT_LOGD_VALUE"; @@ -57,11 +56,6 @@ public abstract class AbstractLogdSizePreferenceController extends AbstractPrefe } @Override - public boolean isAvailable() { - return true; - } - - @Override public String getPreferenceKey() { return SELECT_LOGD_SIZE_KEY; } diff --git a/com/android/settingslib/development/AbstractLogpersistPreferenceController.java b/com/android/settingslib/development/AbstractLogpersistPreferenceController.java index 502fb174..67553adc 100644 --- a/com/android/settingslib/development/AbstractLogpersistPreferenceController.java +++ b/com/android/settingslib/development/AbstractLogpersistPreferenceController.java @@ -30,16 +30,15 @@ import android.support.v7.preference.PreferenceScreen; import android.text.TextUtils; import com.android.settingslib.R; -import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.ConfirmationDialogController; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnCreate; import com.android.settingslib.core.lifecycle.events.OnDestroy; -public abstract class AbstractLogpersistPreferenceController extends AbstractPreferenceController - implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnCreate, OnDestroy, - ConfirmationDialogController { +public abstract class AbstractLogpersistPreferenceController extends + DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener, + LifecycleObserver, OnCreate, OnDestroy, ConfirmationDialogController { private static final String SELECT_LOGPERSIST_KEY = "select_logpersist"; private static final String SELECT_LOGPERSIST_PROPERTY = "persist.logd.logpersistd"; diff --git a/com/android/settingslib/development/DeveloperOptionsPreferenceController.java b/com/android/settingslib/development/DeveloperOptionsPreferenceController.java new file mode 100644 index 00000000..f68c04f9 --- /dev/null +++ b/com/android/settingslib/development/DeveloperOptionsPreferenceController.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2017 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.settingslib.development; + +import android.content.Context; + +import com.android.settingslib.core.AbstractPreferenceController; + +/** + * This controller is used handle changes for the master switch in the developer options page. + * + * All Preference Controllers that are a part of the developer options page should inherit this + * class. + */ +public abstract class DeveloperOptionsPreferenceController extends + AbstractPreferenceController { + + public DeveloperOptionsPreferenceController(Context context) { + super(context); + } + + /** + * Child classes should override this method to create custom logic for hiding preferences. + * + * @return true if the preference is to be displayed. + */ + @Override + public boolean isAvailable() { + return true; + } + + /** + * Called when developer options is enabled + */ + public void onDeveloperOptionsEnabled() { + if (isAvailable()) { + onDeveloperOptionsSwitchEnabled(); + } + } + + /** + * Called when developer options is disabled + */ + public void onDeveloperOptionsDisabled() { + if (isAvailable()) { + onDeveloperOptionsSwitchDisabled(); + } + } + + /** + * Called when developer options is enabled and the preference is available + */ + protected void onDeveloperOptionsSwitchEnabled() { + } + + /** + * Called when developer options is disabled and the preference is available + */ + protected void onDeveloperOptionsSwitchDisabled() { + } + +} diff --git a/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java b/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java new file mode 100644 index 00000000..ff7536ad --- /dev/null +++ b/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 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.settingslib.deviceinfo; + +import android.content.Context; +import android.os.Build; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; +import android.text.TextUtils; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.settingslib.core.AbstractPreferenceController; + +/** + * Preference controller for displaying device serial number. Wraps {@link Build#getSerial()}. + */ +public class AbstractSerialNumberPreferenceController extends AbstractPreferenceController { + private static final String KEY_SERIAL_NUMBER = "serial_number"; + + private final String mSerialNumber; + + public AbstractSerialNumberPreferenceController(Context context) { + this(context, Build.getSerial()); + } + + @VisibleForTesting + AbstractSerialNumberPreferenceController(Context context, String serialNumber) { + super(context); + mSerialNumber = serialNumber; + } + + @Override + public boolean isAvailable() { + return !TextUtils.isEmpty(mSerialNumber); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + final Preference pref = screen.findPreference(KEY_SERIAL_NUMBER); + if (pref != null) { + pref.setSummary(mSerialNumber); + } + } + + @Override + public String getPreferenceKey() { + return KEY_SERIAL_NUMBER; + } +} diff --git a/com/android/settingslib/drawer/TileUtils.java b/com/android/settingslib/drawer/TileUtils.java index 9b75c00a..35ba6ae9 100644 --- a/com/android/settingslib/drawer/TileUtils.java +++ b/com/android/settingslib/drawer/TileUtils.java @@ -26,7 +26,6 @@ import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.graphics.drawable.Icon; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; @@ -320,6 +319,15 @@ public class TileUtils { Context context, UserHandle user, Intent intent, Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles, boolean usePriority, boolean checkCategory, boolean forceTintExternalIcon) { + getTilesForIntent(context, user, intent, addedCache, defaultCategory, outTiles, + usePriority, checkCategory, forceTintExternalIcon, false /* shouldUpdateTiles */); + } + + public static void getTilesForIntent( + Context context, UserHandle user, Intent intent, + Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles, + boolean usePriority, boolean checkCategory, boolean forceTintExternalIcon, + boolean shouldUpdateTiles) { PackageManager pm = context.getPackageManager(); List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent, PackageManager.GET_META_DATA, user.getIdentifier()); @@ -357,9 +365,11 @@ public class TileUtils { updateTileData(context, tile, activityInfo, activityInfo.applicationInfo, pm, providerMap, forceTintExternalIcon); if (DEBUG) Log.d(LOG_TAG, "Adding tile " + tile.title); - addedCache.put(key, tile); + } else if (shouldUpdateTiles) { + updateSummaryAndTitle(context, providerMap, tile); } + if (!tile.userHandle.contains(user)) { tile.userHandle.add(user); } @@ -380,7 +390,6 @@ public class TileUtils { String summary = null; String keyHint = null; boolean isIconTintable = false; - RemoteViews remoteViews = null; // Get the activity's meta-data try { @@ -428,7 +437,8 @@ public class TileUtils { } if (metaData.containsKey(META_DATA_PREFERENCE_CUSTOM_VIEW)) { int layoutId = metaData.getInt(META_DATA_PREFERENCE_CUSTOM_VIEW); - remoteViews = new RemoteViews(applicationInfo.packageName, layoutId); + tile.remoteViews = new RemoteViews(applicationInfo.packageName, layoutId); + updateSummaryAndTitle(context, providerMap, tile); } } } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) { @@ -462,7 +472,6 @@ public class TileUtils { // Suggest a key for this tile tile.key = keyHint; tile.isIconTintable = isIconTintable; - tile.remoteViews = remoteViews; return true; } @@ -470,6 +479,26 @@ public class TileUtils { return false; } + private static void updateSummaryAndTitle( + Context context, Map<String, IContentProvider> providerMap, Tile tile) { + if (tile == null || tile.metaData == null + || !tile.metaData.containsKey(META_DATA_PREFERENCE_SUMMARY_URI)) { + return; + } + + String uriString = tile.metaData.getString(META_DATA_PREFERENCE_SUMMARY_URI); + Bundle bundle = getBundleFromUri(context, uriString, providerMap); + String overrideSummary = getString(bundle, META_DATA_PREFERENCE_SUMMARY); + String overrideTitle = getString(bundle, META_DATA_PREFERENCE_TITLE); + if (overrideSummary != null) { + tile.remoteViews.setTextViewText(android.R.id.summary, overrideSummary); + } + + if (overrideTitle != null) { + tile.remoteViews.setTextViewText(android.R.id.title, overrideTitle); + } + } + /** * Gets the icon package name and resource id from content provider. * @param context context @@ -535,37 +564,6 @@ public class TileUtils { } } - public static void updateTileUsingSummaryUri(Context context, final Tile tile) { - if (tile == null || tile.metaData == null || - !tile.metaData.containsKey(META_DATA_PREFERENCE_SUMMARY_URI)) { - return; - } - - new AsyncTask<Void, Void, Bundle>() { - @Override - protected Bundle doInBackground(Void... params) { - return getBundleFromUri(context, - tile.metaData.getString(META_DATA_PREFERENCE_SUMMARY_URI), new HashMap<>()); - } - - @Override - protected void onPostExecute(Bundle bundle) { - if (bundle == null) { - return; - } - final String overrideSummary = getString(bundle, META_DATA_PREFERENCE_SUMMARY); - final String overrideTitle = getString(bundle, META_DATA_PREFERENCE_TITLE); - - if (overrideSummary != null) { - tile.remoteViews.setTextViewText(android.R.id.summary, overrideSummary); - } - if (overrideTitle != null) { - tile.remoteViews.setTextViewText(android.R.id.title, overrideTitle); - } - } - }.execute(); - } - private static String getString(Bundle bundle, String key) { return bundle == null ? null : bundle.getString(key); } diff --git a/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java b/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java index b7fd4048..3c5ac8df 100644 --- a/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java +++ b/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java @@ -73,7 +73,7 @@ public class BluetoothDeviceLayerDrawable extends LayerDrawable { final Drawable deviceDrawable = context.getDrawable(resId); final BatteryMeterDrawable batteryDrawable = new BatteryMeterDrawable(context, - R.color.meter_background_color, batteryLevel); + context.getColor(R.color.meter_background_color), batteryLevel); final int pad = context.getResources().getDimensionPixelSize(R.dimen.bt_battery_padding); batteryDrawable.setPadding(pad, pad, pad, pad); @@ -107,6 +107,8 @@ public class BluetoothDeviceLayerDrawable extends LayerDrawable { @VisibleForTesting static class BatteryMeterDrawable extends BatteryMeterDrawableBase { private final float mAspectRatio; + @VisibleForTesting + int mFrameColor; public BatteryMeterDrawable(Context context, int frameColor, int batteryLevel) { super(context, frameColor); @@ -118,6 +120,7 @@ public class BluetoothDeviceLayerDrawable extends LayerDrawable { final int tintColor = Utils.getColorAttr(context, android.R.attr.colorControlNormal); setColorFilter(new PorterDuffColorFilter(tintColor, PorterDuff.Mode.SRC_IN)); setBatteryLevel(batteryLevel); + mFrameColor = frameColor; } @Override diff --git a/com/android/settingslib/suggestions/SuggestionParser.java b/com/android/settingslib/suggestions/SuggestionParser.java index 00f32b28..56b84415 100644 --- a/com/android/settingslib/suggestions/SuggestionParser.java +++ b/com/android/settingslib/suggestions/SuggestionParser.java @@ -195,7 +195,7 @@ public class SuggestionParser { intent.setPackage(category.pkg); } TileUtils.getTilesForIntent(mContext, new UserHandle(UserHandle.myUserId()), intent, - mAddCache, null, suggestions, true, false, false); + mAddCache, null, suggestions, true, false, false, true /* shouldUpdateTiles */); filterSuggestions(suggestions, countBefore, isSmartSuggestionEnabled); if (!category.multiple && suggestions.size() > (countBefore + 1)) { // If there are too many, remove them all and only re-add the one with the highest diff --git a/com/android/settingslib/wifi/WifiTracker.java b/com/android/settingslib/wifi/WifiTracker.java index 664dcfcb..12455d85 100644 --- a/com/android/settingslib/wifi/WifiTracker.java +++ b/com/android/settingslib/wifi/WifiTracker.java @@ -24,7 +24,6 @@ import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; -import android.net.NetworkInfo.DetailedState; import android.net.NetworkKey; import android.net.NetworkRequest; import android.net.NetworkScoreManager; @@ -36,10 +35,14 @@ import android.net.wifi.WifiManager; import android.net.wifi.WifiNetworkScoreCache; import android.net.wifi.WifiNetworkScoreCache.CacheListener; import android.os.Handler; +import android.os.HandlerThread; import android.os.Looper; import android.os.Message; +import android.os.Process; import android.provider.Settings; import android.support.annotation.GuardedBy; +import android.support.annotation.NonNull; +import android.support.annotation.VisibleForTesting; import android.text.format.DateUtils; import android.util.ArraySet; import android.util.Log; @@ -47,8 +50,12 @@ import android.util.SparseArray; import android.util.SparseIntArray; import android.widget.Toast; -import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.R; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnDestroy; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; import java.io.PrintWriter; import java.util.ArrayList; @@ -64,7 +71,7 @@ import java.util.concurrent.atomic.AtomicBoolean; /** * Tracks saved or available wifi networks and their state. */ -public class WifiTracker { +public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestroy { /** * Default maximum age in millis of cached scored networks in * {@link AccessPoint#mScoredNetworkCache} to be used for speed label generation. @@ -80,7 +87,7 @@ public class WifiTracker { * and used so as to assist with in-the-field WiFi connectivity debugging */ public static boolean sVerboseLogging; - // TODO(b/36733768): Remove flag includeSaved and includePasspoints. + // TODO(b/36733768): Remove flag includeSaved // TODO: Allow control of this? // Combo scans can take 5-6s to complete - set to 10s. @@ -96,9 +103,9 @@ public class WifiTracker { private final WifiListener mListener; private final boolean mIncludeSaved; private final boolean mIncludeScans; - private final boolean mIncludePasspoints; - @VisibleForTesting final MainHandler mMainHandler; - @VisibleForTesting final WorkHandler mWorkHandler; + @VisibleForTesting MainHandler mMainHandler; + @VisibleForTesting WorkHandler mWorkHandler; + private HandlerThread mWorkThread; private WifiTrackerNetworkCallback mNetworkCallback; @@ -142,7 +149,7 @@ public class WifiTracker { private WifiInfo mLastInfo; private final NetworkScoreManager mNetworkScoreManager; - private final WifiNetworkScoreCache mScoreCache; + private WifiNetworkScoreCache mScoreCache; private boolean mNetworkScoringUiEnabled; private long mMaxSpeedLabelScoreCacheAge; @@ -169,51 +176,43 @@ public class WifiTracker { return filter; } + /** + * Use the lifecycle constructor below whenever possible + */ + @Deprecated public WifiTracker(Context context, WifiListener wifiListener, boolean includeSaved, boolean includeScans) { - this(context, wifiListener, null, includeSaved, includeScans); - } - - public WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper, - boolean includeSaved, boolean includeScans) { - this(context, wifiListener, workerLooper, includeSaved, includeScans, false); + this(context, wifiListener, includeSaved, includeScans, + context.getSystemService(WifiManager.class), + context.getSystemService(ConnectivityManager.class), + context.getSystemService(NetworkScoreManager.class), + newIntentFilter()); } public WifiTracker(Context context, WifiListener wifiListener, - boolean includeSaved, boolean includeScans, boolean includePasspoints) { - this(context, wifiListener, null, includeSaved, includeScans, includePasspoints); - } - - public WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper, - boolean includeSaved, boolean includeScans, boolean includePasspoints) { - this(context, wifiListener, workerLooper, includeSaved, includeScans, includePasspoints, + @NonNull Lifecycle lifecycle, boolean includeSaved, boolean includeScans) { + this(context, wifiListener, includeSaved, includeScans, context.getSystemService(WifiManager.class), context.getSystemService(ConnectivityManager.class), context.getSystemService(NetworkScoreManager.class), - Looper.myLooper(), newIntentFilter()); + newIntentFilter()); + lifecycle.addObserver(this); } @VisibleForTesting - WifiTracker(Context context, WifiListener wifiListener, Looper workerLooper, - boolean includeSaved, boolean includeScans, boolean includePasspoints, - WifiManager wifiManager, ConnectivityManager connectivityManager, - NetworkScoreManager networkScoreManager, Looper currentLooper, - IntentFilter filter) { + WifiTracker(Context context, WifiListener wifiListener, + boolean includeSaved, boolean includeScans, + WifiManager wifiManager, ConnectivityManager connectivityManager, + NetworkScoreManager networkScoreManager, + IntentFilter filter) { if (!includeSaved && !includeScans) { throw new IllegalArgumentException("Must include either saved or scans"); } mContext = context; - if (currentLooper == null) { - // When we aren't on a looper thread, default to the main. - currentLooper = Looper.getMainLooper(); - } - mMainHandler = new MainHandler(currentLooper); - mWorkHandler = new WorkHandler( - workerLooper != null ? workerLooper : currentLooper); + mMainHandler = new MainHandler(Looper.getMainLooper()); mWifiManager = wifiManager; mIncludeSaved = includeSaved; mIncludeScans = includeScans; - mIncludePasspoints = includePasspoints; mListener = wifiListener; mConnectivityManager = connectivityManager; @@ -229,7 +228,22 @@ public class WifiTracker { mNetworkScoreManager = networkScoreManager; - mScoreCache = new WifiNetworkScoreCache(context, new CacheListener(mWorkHandler) { + final HandlerThread workThread = new HandlerThread(TAG + + "{" + Integer.toHexString(System.identityHashCode(this)) + "}", + Process.THREAD_PRIORITY_BACKGROUND); + workThread.start(); + setWorkThread(workThread); + } + + /** + * Sanity warning: this wipes out mScoreCache, so use with extreme caution + * @param workThread substitute Handler thread, for testing purposes only + */ + @VisibleForTesting + void setWorkThread(HandlerThread workThread) { + mWorkThread = workThread; + mWorkHandler = new WorkHandler(workThread.getLooper()); + mScoreCache = new WifiNetworkScoreCache(mContext, new CacheListener(mWorkHandler) { @Override public void networkCacheUpdated(List<ScoredNetwork> networks) { synchronized (mLock) { @@ -244,6 +258,11 @@ public class WifiTracker { }); } + @Override + public void onDestroy() { + mWorkThread.quit(); + } + /** Synchronously update the list of access points with the latest information. */ @MainThread public void forceUpdate() { @@ -312,8 +331,9 @@ public class WifiTracker { * <p>Registers listeners and starts scanning for wifi networks. If this is not called * then forceUpdate() must be called to populate getAccessPoints(). */ + @Override @MainThread - public void startTracking() { + public void onStart() { synchronized (mLock) { registerScoreCache(); @@ -361,15 +381,16 @@ public class WifiTracker { /** * Stop tracking wifi networks and scores. * - * <p>This should always be called when done with a WifiTracker (if startTracking was called) to + * <p>This should always be called when done with a WifiTracker (if onStart was called) to * ensure proper cleanup and prevent any further callbacks from occurring. * * <p>Calling this method will set the {@link #mStaleScanResults} bit, which prevents * {@link WifiListener#onAccessPointsChanged()} callbacks from being invoked (until the bit * is unset on the next SCAN_RESULTS_AVAILABLE_ACTION). */ + @Override @MainThread - public void stopTracking() { + public void onStop() { synchronized (mLock) { if (mRegistered) { mContext.unregisterReceiver(mReceiver); @@ -769,9 +790,8 @@ public class WifiTracker { } public static List<AccessPoint> getCurrentAccessPoints(Context context, boolean includeSaved, - boolean includeScans, boolean includePasspoints) { - WifiTracker tracker = new WifiTracker(context, - null, null, includeSaved, includeScans, includePasspoints); + boolean includeScans) { + WifiTracker tracker = new WifiTracker(context, null, includeSaved, includeScans); tracker.forceUpdate(); tracker.copyAndNotifyListeners(false /*notifyListeners*/); return tracker.getAccessPoints(); diff --git a/com/android/settingslib/wifi/WifiTrackerFactory.java b/com/android/settingslib/wifi/WifiTrackerFactory.java index 79cee046..8b5863ae 100644 --- a/com/android/settingslib/wifi/WifiTrackerFactory.java +++ b/com/android/settingslib/wifi/WifiTrackerFactory.java @@ -16,8 +16,10 @@ package com.android.settingslib.wifi; import android.content.Context; -import android.os.Looper; import android.support.annotation.Keep; +import android.support.annotation.NonNull; + +import com.android.settingslib.core.lifecycle.Lifecycle; /** * Factory method used to inject WifiTracker instances. @@ -31,12 +33,11 @@ public class WifiTrackerFactory { } public static WifiTracker create( - Context context, WifiTracker.WifiListener wifiListener, Looper workerLooper, - boolean includeSaved, boolean includeScans, boolean includePasspoints) { + Context context, WifiTracker.WifiListener wifiListener, @NonNull Lifecycle lifecycle, + boolean includeSaved, boolean includeScans) { if(sTestingWifiTracker != null) { return sTestingWifiTracker; } - return new WifiTracker( - context, wifiListener, workerLooper, includeSaved, includeScans, includePasspoints); + return new WifiTracker(context, wifiListener, lifecycle, includeSaved, includeScans); } } diff --git a/com/android/settingslib/wrapper/PackageManagerWrapper.java b/com/android/settingslib/wrapper/PackageManagerWrapper.java index cd62bc36..b1f3f3ce 100644 --- a/com/android/settingslib/wrapper/PackageManagerWrapper.java +++ b/com/android/settingslib/wrapper/PackageManagerWrapper.java @@ -60,6 +60,13 @@ public class PackageManagerWrapper { } /** + * Calls {@code PackageManager.getInstalledPackagesAsUser} + */ + public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) { + return mPm.getInstalledPackagesAsUser(flags, userId); + } + + /** * Calls {@code PackageManager.hasSystemFeature()}. * * @see android.content.pm.PackageManager#hasSystemFeature @@ -132,11 +139,11 @@ public class PackageManagerWrapper { /** * Gets information about a particular package from the package manager. + * * @param packageName The name of the package we would like information about. - * @param i additional options flags. see javadoc for - * {@link PackageManager#getPackageInfo(String, int)} + * @param i additional options flags. see javadoc for + * {@link PackageManager#getPackageInfo(String, int)} * @return The PackageInfo for the requested package - * @throws NameNotFoundException */ public PackageInfo getPackageInfo(String packageName, int i) throws NameNotFoundException { return mPm.getPackageInfo(packageName, i); @@ -144,6 +151,7 @@ public class PackageManagerWrapper { /** * Retrieves the icon associated with this particular set of ApplicationInfo + * * @param info The ApplicationInfo to retrieve the icon for * @return The icon as a drawable. */ @@ -154,6 +162,7 @@ public class PackageManagerWrapper { /** * Retrieves the label associated with the particular set of ApplicationInfo + * * @param app The ApplicationInfo to retrieve the label for * @return the label as a CharSequence */ @@ -190,4 +199,48 @@ public class PackageManagerWrapper { throws PackageManager.NameNotFoundException { return mPm.getPackageUidAsUser(pkg, userId); } + + /** + * Calls {@code PackageManager.setApplicationEnabledSetting} + */ + public void setApplicationEnabledSetting(String packageName, int newState, int flags) { + mPm.setApplicationEnabledSetting(packageName, newState, flags); + } + + /** + * Calls {@code PackageManager.getApplicationEnabledSetting} + */ + public int getApplicationEnabledSetting(String packageName) { + return mPm.getApplicationEnabledSetting(packageName); + } + + /** + * Calls {@code PackageManager.setComponentEnabledSetting} + */ + public void setComponentEnabledSetting(ComponentName componentName, int newState, int flags) { + mPm.setComponentEnabledSetting(componentName, newState, flags); + } + + /** + * Calls {@code PackageManager.getApplicationInfo} + */ + public ApplicationInfo getApplicationInfo(String packageName, int flags) + throws NameNotFoundException { + return mPm.getApplicationInfo(packageName, flags); + } + + /** + * Calls {@code PackageManager.getApplicationLabel} + */ + public CharSequence getApplicationLabel(ApplicationInfo info) { + return mPm.getApplicationLabel(info); + } + + /** + * Calls {@code PackageManager.queryBroadcastReceivers} + */ + public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) { + return mPm.queryBroadcastReceivers(intent, flags); + } } + diff --git a/com/android/setupwizardlib/test/util/DrawingTestActivity.java b/com/android/setupwizardlib/test/util/DrawingTestActivity.java index 154339a7..3d11e128 100644 --- a/com/android/setupwizardlib/test/util/DrawingTestActivity.java +++ b/com/android/setupwizardlib/test/util/DrawingTestActivity.java @@ -16,15 +16,14 @@ package com.android.setupwizardlib.test.util; -import android.support.v7.app.AppCompatActivity; +import android.app.Activity; /** * Activity to test view and drawable drawing behaviors. This is used to make sure that the drawing - * behavior tested is the same as it would when inflated as part of an {@link AppCompatActivity}, - * including custom layout inflaters and theme values that the support library injects to the - * activity. + * behavior tested is the same as it would when inflated as part of an activity, including any + * injected layout inflater factories and custom themes etc. * * @see DrawingTestHelper */ -public class DrawingTestActivity extends AppCompatActivity { +public class DrawingTestActivity extends Activity { } diff --git a/com/android/setupwizardlib/util/LinkAccessibilityHelper.java b/com/android/setupwizardlib/util/LinkAccessibilityHelper.java index 1e663d6a..1fb3a373 100644 --- a/com/android/setupwizardlib/util/LinkAccessibilityHelper.java +++ b/com/android/setupwizardlib/util/LinkAccessibilityHelper.java @@ -19,6 +19,8 @@ package com.android.setupwizardlib.util; import android.graphics.Rect; import android.os.Build; import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.VisibleForTesting; import android.support.v4.view.AccessibilityDelegateCompat; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat; @@ -38,12 +40,11 @@ import java.util.List; /** * An accessibility delegate that allows {@link android.text.style.ClickableSpan} to be focused and * clicked by accessibility services. - * <p> - * <strong>Note: </strong> From Android O on, there is native support for ClickableSpan - * accessibility, so this class is not needed (and indeed has no effect.) - * </p> * - * <p />Sample usage: + * <p><strong>Note:</strong> This class is a no-op on Android O or above since there is native + * support for ClickableSpan accessibility. + * + * <p>Sample usage: * <pre> * LinkAccessibilityHelper mAccessibilityHelper; * @@ -68,294 +69,255 @@ public class LinkAccessibilityHelper extends AccessibilityDelegateCompat { private static final String TAG = "LinkAccessibilityHelper"; - private final TextView mView; - private final Rect mTempRect = new Rect(); - private final ExploreByTouchHelper mExploreByTouchHelper; + private final AccessibilityDelegateCompat mDelegate; public LinkAccessibilityHelper(TextView view) { - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) { - // Pre-O, we essentially extend ExploreByTouchHelper to expose a virtual view hierarchy - mExploreByTouchHelper = new ExploreByTouchHelper(view) { - @Override - protected int getVirtualViewAt(float x, float y) { - return LinkAccessibilityHelper.this.getVirtualViewAt(x, y); - } - - @Override - protected void getVisibleVirtualViews(List<Integer> virtualViewIds) { - LinkAccessibilityHelper.this.getVisibleVirtualViews(virtualViewIds); - } - - @Override - protected void onPopulateEventForVirtualView(int virtualViewId, - AccessibilityEvent event) { - LinkAccessibilityHelper - .this.onPopulateEventForVirtualView(virtualViewId, event); - } - - @Override - protected void onPopulateNodeForVirtualView(int virtualViewId, - AccessibilityNodeInfoCompat infoCompat) { - LinkAccessibilityHelper - .this.onPopulateNodeForVirtualView(virtualViewId, infoCompat); - - } + this(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + // Platform support was added in O. This helper will be no-op + ? new AccessibilityDelegateCompat() + // Pre-O, we extend ExploreByTouchHelper to expose a virtual view hierarchy + : new PreOLinkAccessibilityHelper(view)); + } - @Override - protected boolean onPerformActionForVirtualView(int virtualViewId, int action, - Bundle arguments) { - return LinkAccessibilityHelper.this - .onPerformActionForVirtualView(virtualViewId, action, arguments); - } - }; - } else { - mExploreByTouchHelper = null; - } - mView = view; + @VisibleForTesting + LinkAccessibilityHelper(@NonNull AccessibilityDelegateCompat delegate) { + mDelegate = delegate; } @Override public void sendAccessibilityEvent(View host, int eventType) { - if (mExploreByTouchHelper != null) { - mExploreByTouchHelper.sendAccessibilityEvent(host, eventType); - } else { - super.sendAccessibilityEvent(host, eventType); - } + mDelegate.sendAccessibilityEvent(host, eventType); } @Override public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) { - if (mExploreByTouchHelper != null) { - mExploreByTouchHelper.sendAccessibilityEventUnchecked(host, event); - } else { - super.sendAccessibilityEventUnchecked(host, event); - } + mDelegate.sendAccessibilityEventUnchecked(host, event); } @Override public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) { - return (mExploreByTouchHelper != null) - ? mExploreByTouchHelper.dispatchPopulateAccessibilityEvent(host, event) - : super.dispatchPopulateAccessibilityEvent(host, event); + return mDelegate.dispatchPopulateAccessibilityEvent(host, event); } @Override public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) { - if (mExploreByTouchHelper != null) { - mExploreByTouchHelper.onPopulateAccessibilityEvent(host, event); - } else { - super.onPopulateAccessibilityEvent(host, event); - } + mDelegate.onPopulateAccessibilityEvent(host, event); } @Override public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { - if (mExploreByTouchHelper != null) { - mExploreByTouchHelper.onInitializeAccessibilityEvent(host, event); - } else { - super.onInitializeAccessibilityEvent(host, event); - } + mDelegate.onInitializeAccessibilityEvent(host, event); } @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { - if (mExploreByTouchHelper != null) { - mExploreByTouchHelper.onInitializeAccessibilityNodeInfo(host, info); - } else { - super.onInitializeAccessibilityNodeInfo(host, info); - } + mDelegate.onInitializeAccessibilityNodeInfo(host, info); } @Override public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, AccessibilityEvent event) { - return (mExploreByTouchHelper != null) - ? mExploreByTouchHelper.onRequestSendAccessibilityEvent(host, child, event) - : super.onRequestSendAccessibilityEvent(host, child, event); + return mDelegate.onRequestSendAccessibilityEvent(host, child, event); } @Override public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) { - return (mExploreByTouchHelper != null) - ? mExploreByTouchHelper.getAccessibilityNodeProvider(host) - : super.getAccessibilityNodeProvider(host); + return mDelegate.getAccessibilityNodeProvider(host); } @Override public boolean performAccessibilityAction(View host, int action, Bundle args) { - return (mExploreByTouchHelper != null) - ? mExploreByTouchHelper.performAccessibilityAction(host, action, args) - : super.performAccessibilityAction(host, action, args); + return mDelegate.performAccessibilityAction(host, action, args); } /** - * Delegated to {@link ExploreByTouchHelper} + * Dispatches hover event to the virtual view hierarchy. This method should be called in + * {@link View#dispatchHoverEvent(MotionEvent)}. + * + * @see ExploreByTouchHelper#dispatchHoverEvent(MotionEvent) */ public final boolean dispatchHoverEvent(MotionEvent event) { - return (mExploreByTouchHelper != null) ? mExploreByTouchHelper.dispatchHoverEvent(event) - : false; + return mDelegate instanceof ExploreByTouchHelper + && ((ExploreByTouchHelper) mDelegate).dispatchHoverEvent(event); } - protected int getVirtualViewAt(float x, float y) { - final CharSequence text = mView.getText(); - if (text instanceof Spanned) { - final Spanned spannedText = (Spanned) text; - final int offset = getOffsetForPosition(mView, x, y); - ClickableSpan[] linkSpans = spannedText.getSpans(offset, offset, ClickableSpan.class); - if (linkSpans.length == 1) { - ClickableSpan linkSpan = linkSpans[0]; - return spannedText.getSpanStart(linkSpan); - } + @VisibleForTesting + static class PreOLinkAccessibilityHelper extends ExploreByTouchHelper { + + private final Rect mTempRect = new Rect(); + private final TextView mView; + + PreOLinkAccessibilityHelper(TextView view) { + super(view); + mView = view; } - return ExploreByTouchHelper.INVALID_ID; - } - protected void getVisibleVirtualViews(List<Integer> virtualViewIds) { - final CharSequence text = mView.getText(); - if (text instanceof Spanned) { - final Spanned spannedText = (Spanned) text; - ClickableSpan[] linkSpans = spannedText.getSpans(0, spannedText.length(), - ClickableSpan.class); - for (ClickableSpan span : linkSpans) { - virtualViewIds.add(spannedText.getSpanStart(span)); + protected int getVirtualViewAt(float x, float y) { + final CharSequence text = mView.getText(); + if (text instanceof Spanned) { + final Spanned spannedText = (Spanned) text; + final int offset = getOffsetForPosition(mView, x, y); + ClickableSpan[] linkSpans = + spannedText.getSpans(offset, offset, ClickableSpan.class); + if (linkSpans.length == 1) { + ClickableSpan linkSpan = linkSpans[0]; + return spannedText.getSpanStart(linkSpan); + } } + return ExploreByTouchHelper.INVALID_ID; } - } - protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) { - final ClickableSpan span = getSpanForOffset(virtualViewId); - if (span != null) { - event.setContentDescription(getTextForSpan(span)); - } else { - Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId); - event.setContentDescription(mView.getText()); + protected void getVisibleVirtualViews(List<Integer> virtualViewIds) { + final CharSequence text = mView.getText(); + if (text instanceof Spanned) { + final Spanned spannedText = (Spanned) text; + ClickableSpan[] linkSpans = spannedText.getSpans(0, spannedText.length(), + ClickableSpan.class); + for (ClickableSpan span : linkSpans) { + virtualViewIds.add(spannedText.getSpanStart(span)); + } + } } - } - protected void onPopulateNodeForVirtualView(int virtualViewId, - AccessibilityNodeInfoCompat info) { - final ClickableSpan span = getSpanForOffset(virtualViewId); - if (span != null) { - info.setContentDescription(getTextForSpan(span)); - } else { - Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId); - info.setContentDescription(mView.getText()); - } - info.setFocusable(true); - info.setClickable(true); - getBoundsForSpan(span, mTempRect); - if (mTempRect.isEmpty()) { - Log.e(TAG, "LinkSpan bounds is empty for: " + virtualViewId); - mTempRect.set(0, 0, 1, 1); + protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) { + final ClickableSpan span = getSpanForOffset(virtualViewId); + if (span != null) { + event.setContentDescription(getTextForSpan(span)); + } else { + Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId); + event.setContentDescription(mView.getText()); + } } - info.setBoundsInParent(mTempRect); - info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK); - } - protected boolean onPerformActionForVirtualView(int virtualViewId, int action, - Bundle arguments) { - if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) { - ClickableSpan span = getSpanForOffset(virtualViewId); + protected void onPopulateNodeForVirtualView( + int virtualViewId, + AccessibilityNodeInfoCompat info) { + final ClickableSpan span = getSpanForOffset(virtualViewId); if (span != null) { - span.onClick(mView); - return true; + info.setContentDescription(getTextForSpan(span)); } else { Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId); + info.setContentDescription(mView.getText()); } + info.setFocusable(true); + info.setClickable(true); + getBoundsForSpan(span, mTempRect); + if (mTempRect.isEmpty()) { + Log.e(TAG, "LinkSpan bounds is empty for: " + virtualViewId); + mTempRect.set(0, 0, 1, 1); + } + info.setBoundsInParent(mTempRect); + info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK); } - return false; - } - private ClickableSpan getSpanForOffset(int offset) { - CharSequence text = mView.getText(); - if (text instanceof Spanned) { - Spanned spannedText = (Spanned) text; - ClickableSpan[] spans = spannedText.getSpans(offset, offset, ClickableSpan.class); - if (spans.length == 1) { - return spans[0]; + protected boolean onPerformActionForVirtualView( + int virtualViewId, + int action, + Bundle arguments) { + if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) { + ClickableSpan span = getSpanForOffset(virtualViewId); + if (span != null) { + span.onClick(mView); + return true; + } else { + Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId); + } } + return false; } - return null; - } - private CharSequence getTextForSpan(ClickableSpan span) { - CharSequence text = mView.getText(); - if (text instanceof Spanned) { - Spanned spannedText = (Spanned) text; - return spannedText.subSequence(spannedText.getSpanStart(span), - spannedText.getSpanEnd(span)); + private ClickableSpan getSpanForOffset(int offset) { + CharSequence text = mView.getText(); + if (text instanceof Spanned) { + Spanned spannedText = (Spanned) text; + ClickableSpan[] spans = spannedText.getSpans(offset, offset, ClickableSpan.class); + if (spans.length == 1) { + return spans[0]; + } + } + return null; } - return text; - } - // Find the bounds of a span. If it spans multiple lines, it will only return the bounds for the - // section on the first line. - private Rect getBoundsForSpan(ClickableSpan span, Rect outRect) { - CharSequence text = mView.getText(); - outRect.setEmpty(); - if (text instanceof Spanned) { - final Layout layout = mView.getLayout(); - if (layout != null) { + private CharSequence getTextForSpan(ClickableSpan span) { + CharSequence text = mView.getText(); + if (text instanceof Spanned) { Spanned spannedText = (Spanned) text; - final int spanStart = spannedText.getSpanStart(span); - final int spanEnd = spannedText.getSpanEnd(span); - final float xStart = layout.getPrimaryHorizontal(spanStart); - final float xEnd = layout.getPrimaryHorizontal(spanEnd); - final int lineStart = layout.getLineForOffset(spanStart); - final int lineEnd = layout.getLineForOffset(spanEnd); - layout.getLineBounds(lineStart, outRect); - if (lineEnd == lineStart) { - // If the span is on a single line, adjust both the left and right bounds - // so outrect is exactly bounding the span. - outRect.left = (int) Math.min(xStart, xEnd); - outRect.right = (int) Math.max(xStart, xEnd); - } else { - // If the span wraps across multiple lines, only use the first line (as returned - // by layout.getLineBounds above), and adjust the "start" of outrect to where - // the span starts, leaving the "end" of outrect at the end of the line. - // ("start" being left for LTR, and right for RTL) - if (layout.getParagraphDirection(lineStart) == Layout.DIR_RIGHT_TO_LEFT) { - outRect.right = (int) xStart; + return spannedText.subSequence( + spannedText.getSpanStart(span), + spannedText.getSpanEnd(span)); + } + return text; + } + + // Find the bounds of a span. If it spans multiple lines, it will only return the bounds for + // the section on the first line. + private Rect getBoundsForSpan(ClickableSpan span, Rect outRect) { + CharSequence text = mView.getText(); + outRect.setEmpty(); + if (text instanceof Spanned) { + final Layout layout = mView.getLayout(); + if (layout != null) { + Spanned spannedText = (Spanned) text; + final int spanStart = spannedText.getSpanStart(span); + final int spanEnd = spannedText.getSpanEnd(span); + final float xStart = layout.getPrimaryHorizontal(spanStart); + final float xEnd = layout.getPrimaryHorizontal(spanEnd); + final int lineStart = layout.getLineForOffset(spanStart); + final int lineEnd = layout.getLineForOffset(spanEnd); + layout.getLineBounds(lineStart, outRect); + if (lineEnd == lineStart) { + // If the span is on a single line, adjust both the left and right bounds + // so outrect is exactly bounding the span. + outRect.left = (int) Math.min(xStart, xEnd); + outRect.right = (int) Math.max(xStart, xEnd); } else { - outRect.left = (int) xStart; + // If the span wraps across multiple lines, only use the first line (as + // returned by layout.getLineBounds above), and adjust the "start" of + // outrect to where the span starts, leaving the "end" of outrect at the end + // of the line. ("start" being left for LTR, and right for RTL) + if (layout.getParagraphDirection(lineStart) == Layout.DIR_RIGHT_TO_LEFT) { + outRect.right = (int) xStart; + } else { + outRect.left = (int) xStart; + } } - } - // Offset for padding - outRect.offset(mView.getTotalPaddingLeft(), mView.getTotalPaddingTop()); + // Offset for padding + outRect.offset(mView.getTotalPaddingLeft(), mView.getTotalPaddingTop()); + } } + return outRect; } - return outRect; - } - // Compat implementation of TextView#getOffsetForPosition(). + // Compat implementation of TextView#getOffsetForPosition(). - private static int getOffsetForPosition(TextView view, float x, float y) { - if (view.getLayout() == null) return -1; - final int line = getLineAtCoordinate(view, y); - return getOffsetAtCoordinate(view, line, x); - } + private static int getOffsetForPosition(TextView view, float x, float y) { + if (view.getLayout() == null) return -1; + final int line = getLineAtCoordinate(view, y); + return getOffsetAtCoordinate(view, line, x); + } - private static float convertToLocalHorizontalCoordinate(TextView view, float x) { - x -= view.getTotalPaddingLeft(); - // Clamp the position to inside of the view. - x = Math.max(0.0f, x); - x = Math.min(view.getWidth() - view.getTotalPaddingRight() - 1, x); - x += view.getScrollX(); - return x; - } + private static float convertToLocalHorizontalCoordinate(TextView view, float x) { + x -= view.getTotalPaddingLeft(); + // Clamp the position to inside of the view. + x = Math.max(0.0f, x); + x = Math.min(view.getWidth() - view.getTotalPaddingRight() - 1, x); + x += view.getScrollX(); + return x; + } - private static int getLineAtCoordinate(TextView view, float y) { - y -= view.getTotalPaddingTop(); - // Clamp the position to inside of the view. - y = Math.max(0.0f, y); - y = Math.min(view.getHeight() - view.getTotalPaddingBottom() - 1, y); - y += view.getScrollY(); - return view.getLayout().getLineForVertical((int) y); - } + private static int getLineAtCoordinate(TextView view, float y) { + y -= view.getTotalPaddingTop(); + // Clamp the position to inside of the view. + y = Math.max(0.0f, y); + y = Math.min(view.getHeight() - view.getTotalPaddingBottom() - 1, y); + y += view.getScrollY(); + return view.getLayout().getLineForVertical((int) y); + } - private static int getOffsetAtCoordinate(TextView view, int line, float x) { - x = convertToLocalHorizontalCoordinate(view, x); - return view.getLayout().getOffsetForHorizontal(line, x); + private static int getOffsetAtCoordinate(TextView view, int line, float x) { + x = convertToLocalHorizontalCoordinate(view, x); + return view.getLayout().getOffsetForHorizontal(line, x); + } } } diff --git a/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java b/com/android/setupwizardlib/util/LinkAccessibilityHelperTest.java index 844e73e9..6228e6fc 100644 --- a/com/android/setupwizardlib/test/LinkAccessibilityHelperTest.java +++ b/com/android/setupwizardlib/util/LinkAccessibilityHelperTest.java @@ -14,29 +14,35 @@ * limitations under the License. */ -package com.android.setupwizardlib.test; +package com.android.setupwizardlib.util; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.same; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import android.graphics.Rect; -import android.os.Build; import android.os.Bundle; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.support.v4.text.BidiFormatter; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat; import android.support.v4.widget.ExploreByTouchHelper; import android.text.SpannableStringBuilder; import android.util.DisplayMetrics; import android.util.TypedValue; +import android.view.MotionEvent; import android.view.View; import android.view.accessibility.AccessibilityEvent; +import android.widget.FrameLayout; import android.widget.TextView; import com.android.setupwizardlib.span.LinkSpan; -import com.android.setupwizardlib.util.LinkAccessibilityHelper; +import com.android.setupwizardlib.util.LinkAccessibilityHelper.PreOLinkAccessibilityHelper; import org.junit.Test; import org.junit.runner.RunWith; @@ -52,13 +58,12 @@ public class LinkAccessibilityHelperTest { private static final LinkSpan LINK_SPAN = new LinkSpan("foobar"); private TextView mTextView; - private TestLinkAccessibilityHelper mHelper; + private TestPreOLinkAccessibilityHelper mHelper; private DisplayMetrics mDisplayMetrics; @Test public void testGetVirtualViewAt() { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); final int virtualViewId = mHelper.getVirtualViewAt(dp2Px(15), dp2Px(10)); assertEquals("Virtual view ID should be 1", 1, virtualViewId); @@ -66,7 +71,6 @@ public class LinkAccessibilityHelperTest { @Test public void testGetVirtualViewAtHost() { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); final int virtualViewId = mHelper.getVirtualViewAt(dp2Px(100), dp2Px(100)); assertEquals("Virtual view ID should be INVALID_ID", @@ -75,7 +79,6 @@ public class LinkAccessibilityHelperTest { @Test public void testGetVisibleVirtualViews() { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); List<Integer> virtualViewIds = new ArrayList<>(); mHelper.getVisibleVirtualViews(virtualViewIds); @@ -86,7 +89,6 @@ public class LinkAccessibilityHelperTest { @Test public void testOnPopulateEventForVirtualView() { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); AccessibilityEvent event = AccessibilityEvent.obtain(); mHelper.onPopulateEventForVirtualView(1, event); @@ -100,7 +102,6 @@ public class LinkAccessibilityHelperTest { @Test public void testOnPopulateEventForVirtualViewHost() { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); AccessibilityEvent event = AccessibilityEvent.obtain(); mHelper.onPopulateEventForVirtualView(ExploreByTouchHelper.INVALID_ID, event); @@ -113,7 +114,6 @@ public class LinkAccessibilityHelperTest { @Test public void testOnPopulateNodeForVirtualView() { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain(); mHelper.onPopulateNodeForVirtualView(1, info); @@ -132,7 +132,6 @@ public class LinkAccessibilityHelperTest { @Test public void testNullLayout() { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; initTextView(); // Setting the padding will cause the layout to be null-ed out. mTextView.setPadding(1, 1, 1, 1); @@ -150,7 +149,6 @@ public class LinkAccessibilityHelperTest { @Test public void testRtlLayout() { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; SpannableStringBuilder ssb = new SpannableStringBuilder("מכונה בתרגום"); ssb.setSpan(LINK_SPAN, 1, 2, 0 /* flags */); initTextView(ssb); @@ -170,7 +168,6 @@ public class LinkAccessibilityHelperTest { @Test public void testMultilineLink() { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; SpannableStringBuilder ssb = new SpannableStringBuilder( "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + "Praesent accumsan efficitur eros eu porttitor."); @@ -192,7 +189,6 @@ public class LinkAccessibilityHelperTest { @Test public void testRtlMultilineLink() { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; String iwLoremIpsum = "אחר על רביעי אקטואליה. לוח דת אחרות המקובל רומנית, מיזמים מועמדים " + "האנציקלופדיה בה צ'ט. מתן מה שנורו לערוך ייִדיש, בקר או החול אנתרופולוגיה, עוד " + "דפים המחשב מיזמים ב."; @@ -216,7 +212,6 @@ public class LinkAccessibilityHelperTest { @Test public void testBidiMultilineLink() { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) return; String iwLoremIpsum = "אחר על רביעי אקטואליה. לוח דת אחרות המקובל רומנית, מיזמים מועמדים " + "האנציקלופדיה בה צ'ט. מתן מה שנורו לערוך ייִדיש, בקר או החול אנתרופולוגיה, עוד " + "דפים המחשב מיזמים ב."; @@ -243,6 +238,70 @@ public class LinkAccessibilityHelperTest { info.recycle(); } + @Test + public void testMethodDelegation() { + initTextView(); + ExploreByTouchHelper delegate = mock(TestPreOLinkAccessibilityHelper.class); + LinkAccessibilityHelper helper = new LinkAccessibilityHelper(delegate); + + AccessibilityEvent accessibilityEvent = + AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_CLICKED); + + helper.sendAccessibilityEvent(mTextView, AccessibilityEvent.TYPE_VIEW_CLICKED); + verify(delegate).sendAccessibilityEvent( + same(mTextView), + eq(AccessibilityEvent.TYPE_VIEW_CLICKED)); + + helper.sendAccessibilityEventUnchecked(mTextView, accessibilityEvent); + verify(delegate).sendAccessibilityEventUnchecked(same(mTextView), same(accessibilityEvent)); + + helper.performAccessibilityAction( + mTextView, + AccessibilityActionCompat.ACTION_CLICK.getId(), + Bundle.EMPTY); + verify(delegate).performAccessibilityAction( + same(mTextView), + eq(AccessibilityActionCompat.ACTION_CLICK.getId()), + eq(Bundle.EMPTY)); + + helper.dispatchPopulateAccessibilityEvent( + mTextView, + accessibilityEvent); + verify(delegate).dispatchPopulateAccessibilityEvent( + same(mTextView), + same(accessibilityEvent)); + + MotionEvent motionEvent = MotionEvent.obtain(0, 0, 0, 0, 0, 0); + helper.dispatchHoverEvent(motionEvent); + verify(delegate).dispatchHoverEvent(eq(motionEvent)); + + helper.getAccessibilityNodeProvider(mTextView); + verify(delegate).getAccessibilityNodeProvider(same(mTextView)); + + helper.onInitializeAccessibilityEvent(mTextView, accessibilityEvent); + verify(delegate).onInitializeAccessibilityEvent( + same(mTextView), + eq(accessibilityEvent)); + + AccessibilityNodeInfoCompat accessibilityNodeInfo = AccessibilityNodeInfoCompat.obtain(); + helper.onInitializeAccessibilityNodeInfo(mTextView, accessibilityNodeInfo); + verify(delegate).onInitializeAccessibilityNodeInfo( + same(mTextView), + same(accessibilityNodeInfo)); + + helper.onPopulateAccessibilityEvent(mTextView, accessibilityEvent); + verify(delegate).onPopulateAccessibilityEvent( + same(mTextView), + same(accessibilityEvent)); + + FrameLayout parent = new FrameLayout(InstrumentationRegistry.getTargetContext()); + helper.onRequestSendAccessibilityEvent(parent, mTextView, accessibilityEvent); + verify(delegate).onRequestSendAccessibilityEvent( + same(parent), + same(mTextView), + same(accessibilityEvent)); + } + private void initTextView() { SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world"); ssb.setSpan(LINK_SPAN, 1, 2, 0 /* flags */); @@ -254,7 +313,7 @@ public class LinkAccessibilityHelperTest { mTextView.setSingleLine(false); mTextView.setText(text); mTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); - mHelper = new TestLinkAccessibilityHelper(mTextView); + mHelper = new TestPreOLinkAccessibilityHelper(mTextView); int measureExactly500dp = View.MeasureSpec.makeMeasureSpec(dp2Px(500), View.MeasureSpec.EXACTLY); @@ -270,9 +329,9 @@ public class LinkAccessibilityHelperTest { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, mDisplayMetrics); } - private static class TestLinkAccessibilityHelper extends LinkAccessibilityHelper { + public static class TestPreOLinkAccessibilityHelper extends PreOLinkAccessibilityHelper { - TestLinkAccessibilityHelper(TextView view) { + TestPreOLinkAccessibilityHelper(TextView view) { super(view); } diff --git a/com/android/setupwizardlib/util/PartnerTest.java b/com/android/setupwizardlib/util/PartnerTest.java index f47eef18..aeb678fa 100644 --- a/com/android/setupwizardlib/util/PartnerTest.java +++ b/com/android/setupwizardlib/util/PartnerTest.java @@ -31,6 +31,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.os.Build.VERSION; @@ -40,20 +41,25 @@ import com.android.setupwizardlib.BuildConfig; import com.android.setupwizardlib.R; import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner; import com.android.setupwizardlib.util.Partner.ResourceEntry; +import com.android.setupwizardlib.util.PartnerTest.ShadowApplicationPackageManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.robolectric.RuntimeEnvironment; +import org.robolectric.Shadows; import org.robolectric.annotation.Config; -import org.robolectric.res.builder.DefaultPackageManager; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; import org.robolectric.shadows.ShadowResources; import java.util.Arrays; import java.util.Collections; @RunWith(SuwLibRobolectricTestRunner.class) -@Config(constants = BuildConfig.class, sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK }) +@Config( + constants = BuildConfig.class, + sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK }, + shadows = ShadowApplicationPackageManager.class) public class PartnerTest { private static final String ACTION_PARTNER_CUSTOMIZATION = @@ -62,7 +68,7 @@ public class PartnerTest { private Context mContext; private Resources mPartnerResources; - private TestPackageManager mPackageManager; + private ShadowApplicationPackageManager mPackageManager; @Before public void setUp() throws Exception { @@ -71,8 +77,9 @@ public class PartnerTest { mContext = spy(application); mPartnerResources = spy(ShadowResources.getSystem()); - mPackageManager = new TestPackageManager(); - RuntimeEnvironment.setRobolectricPackageManager(mPackageManager); + mPackageManager = + (ShadowApplicationPackageManager) Shadows.shadowOf(application.getPackageManager()); + mPackageManager.partnerResources = mPartnerResources; } @Test @@ -173,13 +180,18 @@ public class PartnerTest { return info; } - private class TestPackageManager extends DefaultPackageManager { + @Implements(className = "android.app.ApplicationPackageManager") + public static class ShadowApplicationPackageManager extends + org.robolectric.shadows.ShadowApplicationPackageManager { + public Resources partnerResources; + + @Implementation @Override public Resources getResourcesForApplication(ApplicationInfo app) throws NameNotFoundException { if (app != null && "test.partner.package".equals(app.packageName)) { - return mPartnerResources; + return partnerResources; } else { return super.getResourcesForApplication(app); } diff --git a/com/android/setupwizardlib/view/IllustrationVideoViewTest.java b/com/android/setupwizardlib/view/IllustrationVideoViewTest.java index ffa228d5..ddf59ca4 100644 --- a/com/android/setupwizardlib/view/IllustrationVideoViewTest.java +++ b/com/android/setupwizardlib/view/IllustrationVideoViewTest.java @@ -48,7 +48,7 @@ import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.annotation.RealObject; -import org.robolectric.internal.Shadow; +import org.robolectric.shadow.api.Shadow; import org.robolectric.shadows.ShadowMediaPlayer; import org.robolectric.util.ReflectionHelpers; diff --git a/com/android/setupwizardlib/view/NavigationBarButton.java b/com/android/setupwizardlib/view/NavigationBarButton.java index 5172c476..45d3737c 100644 --- a/com/android/setupwizardlib/view/NavigationBarButton.java +++ b/com/android/setupwizardlib/view/NavigationBarButton.java @@ -17,143 +17,16 @@ package com.android.setupwizardlib.view; import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.LayerDrawable; -import android.os.Build; -import android.support.annotation.NonNull; import android.util.AttributeSet; import android.widget.Button; -/** - * Button for navigation bar, which includes tinting of its compound drawables to be used for dark - * and light themes. - */ public class NavigationBarButton extends Button { public NavigationBarButton(Context context) { super(context); - init(); } public NavigationBarButton(Context context, AttributeSet attrs) { super(context, attrs); - init(); - } - - private void init() { - // Unfortunately, drawableStart and drawableEnd set through XML does not call the setter, - // so manually getting it and wrapping it in the compat drawable. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - Drawable[] drawables = getCompoundDrawablesRelative(); - for (int i = 0; i < drawables.length; i++) { - if (drawables[i] != null) { - drawables[i] = TintedDrawable.wrap(drawables[i]); - } - } - setCompoundDrawablesRelativeWithIntrinsicBounds(drawables[0], drawables[1], - drawables[2], drawables[3]); - } - } - - @Override - public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom) { - if (left != null) left = TintedDrawable.wrap(left); - if (top != null) top = TintedDrawable.wrap(top); - if (right != null) right = TintedDrawable.wrap(right); - if (bottom != null) bottom = TintedDrawable.wrap(bottom); - super.setCompoundDrawables(left, top, right, bottom); - tintDrawables(); - } - - @Override - public void setCompoundDrawablesRelative(Drawable start, Drawable top, Drawable end, - Drawable bottom) { - if (start != null) start = TintedDrawable.wrap(start); - if (top != null) top = TintedDrawable.wrap(top); - if (end != null) end = TintedDrawable.wrap(end); - if (bottom != null) bottom = TintedDrawable.wrap(bottom); - super.setCompoundDrawablesRelative(start, top, end, bottom); - tintDrawables(); - } - - @Override - public void setTextColor(ColorStateList colors) { - super.setTextColor(colors); - tintDrawables(); - } - - private void tintDrawables() { - final ColorStateList textColors = getTextColors(); - if (textColors != null) { - for (Drawable drawable : getAllCompoundDrawables()) { - if (drawable instanceof TintedDrawable) { - ((TintedDrawable) drawable).setTintListCompat(textColors); - } - } - invalidate(); - } - } - - private Drawable[] getAllCompoundDrawables() { - Drawable[] drawables = new Drawable[6]; - Drawable[] compoundDrawables = getCompoundDrawables(); - drawables[0] = compoundDrawables[0]; // left - drawables[1] = compoundDrawables[1]; // top - drawables[2] = compoundDrawables[2]; // right - drawables[3] = compoundDrawables[3]; // bottom - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - Drawable[] compoundDrawablesRelative = getCompoundDrawablesRelative(); - drawables[4] = compoundDrawablesRelative[0]; // start - drawables[5] = compoundDrawablesRelative[2]; // end - } - return drawables; - } - - // TODO: Remove this class and use DrawableCompat.wrap() once we can use support library 22.1.0 - // or above - private static class TintedDrawable extends LayerDrawable { - - public static TintedDrawable wrap(Drawable drawable) { - if (drawable instanceof TintedDrawable) { - return (TintedDrawable) drawable; - } - return new TintedDrawable(drawable.mutate()); - } - - private ColorStateList mTintList = null; - - TintedDrawable(Drawable wrapped) { - super(new Drawable[] { wrapped }); - } - - @Override - public boolean isStateful() { - return true; - } - - @Override - public boolean setState(@NonNull int[] stateSet) { - boolean needsInvalidate = super.setState(stateSet); - boolean needsInvalidateForState = updateState(); - return needsInvalidate || needsInvalidateForState; - } - - public void setTintListCompat(ColorStateList colors) { - mTintList = colors; - if (updateState()) { - invalidateSelf(); - } - } - - private boolean updateState() { - if (mTintList != null) { - final int color = mTintList.getColorForState(getState(), 0); - setColorFilter(color, PorterDuff.Mode.SRC_IN); - return true; // Needs invalidate - } - return false; - } } } diff --git a/com/android/setupwizardlib/view/RichTextView.java b/com/android/setupwizardlib/view/RichTextView.java index e6bc9da0..5a78561f 100644 --- a/com/android/setupwizardlib/view/RichTextView.java +++ b/com/android/setupwizardlib/view/RichTextView.java @@ -17,11 +17,6 @@ package com.android.setupwizardlib.view; import android.content.Context; -import android.graphics.drawable.Drawable; -import android.os.Build.VERSION; -import android.os.Build.VERSION_CODES; -import android.support.v4.view.ViewCompat; -import android.support.v7.widget.AppCompatTextView; import android.text.Annotation; import android.text.SpannableString; import android.text.Spanned; @@ -30,18 +25,22 @@ import android.text.style.ClickableSpan; import android.text.style.TextAppearanceSpan; import android.util.AttributeSet; import android.util.Log; -import android.view.MotionEvent; +import android.widget.TextView; import com.android.setupwizardlib.span.LinkSpan; import com.android.setupwizardlib.span.LinkSpan.OnLinkClickListener; import com.android.setupwizardlib.span.SpanHelper; -import com.android.setupwizardlib.util.LinkAccessibilityHelper; /** * An extension of TextView that automatically replaces the annotation tags as specified in * {@link SpanHelper#replaceSpan(android.text.Spannable, Object, Object)} + * + * <p>Note: The accessibility interaction for ClickableSpans (and therefore LinkSpans) are built + * into platform in O, although the interaction paradigm is different. (See b/17726921). In this + * platform version, the links are exposed in the Local Context Menu of TalkBack instead of + * accessible directly through swiping. */ -public class RichTextView extends AppCompatTextView implements OnLinkClickListener { +public class RichTextView extends TextView implements OnLinkClickListener { /* static section */ @@ -89,22 +88,14 @@ public class RichTextView extends AppCompatTextView implements OnLinkClickListen /* non-static section */ - private LinkAccessibilityHelper mAccessibilityHelper; private OnLinkClickListener mOnLinkClickListener; public RichTextView(Context context) { super(context); - init(); } public RichTextView(Context context, AttributeSet attrs) { super(context, attrs); - init(); - } - - private void init() { - mAccessibilityHelper = new LinkAccessibilityHelper(this); - ViewCompat.setAccessibilityDelegate(this, mAccessibilityHelper); } @Override @@ -141,32 +132,6 @@ public class RichTextView extends AppCompatTextView implements OnLinkClickListen return false; } - @Override - protected boolean dispatchHoverEvent(MotionEvent event) { - if (mAccessibilityHelper != null && mAccessibilityHelper.dispatchHoverEvent(event)) { - return true; - } - return super.dispatchHoverEvent(event); - } - - @Override - protected void drawableStateChanged() { - super.drawableStateChanged(); - - if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) { - // b/26765507 causes drawableStart and drawableEnd to not get the right state on M. As a - // workaround, set the state on those drawables directly. - final int[] state = getDrawableState(); - for (Drawable drawable : getCompoundDrawablesRelative()) { - if (drawable != null) { - if (drawable.setState(state)) { - invalidateDrawable(drawable); - } - } - } - } - } - public void setOnLinkClickListener(OnLinkClickListener listener) { mOnLinkClickListener = listener; } diff --git a/com/android/systemui/BatteryMeterView.java b/com/android/systemui/BatteryMeterView.java index 2b31967c..2fe66a14 100644 --- a/com/android/systemui/BatteryMeterView.java +++ b/com/android/systemui/BatteryMeterView.java @@ -15,6 +15,8 @@ */ package com.android.systemui; +import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS; +import static android.app.StatusBarManager.DISABLE_NONE; import static android.provider.Settings.System.SHOW_BATTERY_PERCENT; import android.animation.ArgbEvaluator; @@ -52,6 +54,7 @@ import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.policy.IconLogger; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; +import com.android.systemui.util.Utils.DisableStateTracker; import java.text.NumberFormat; @@ -101,6 +104,9 @@ public class BatteryMeterView extends LinearLayout implements mSettingObserver = new SettingObserver(new Handler(context.getMainLooper())); + addOnAttachStateChangeListener( + new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS)); + mSlotBattery = context.getString( com.android.internal.R.string.status_bar_battery); mBatteryIconView = new ImageView(context); diff --git a/com/android/systemui/assist/AssistManager.java b/com/android/systemui/assist/AssistManager.java index c5eebccb..8a8bafaf 100644 --- a/com/android/systemui/assist/AssistManager.java +++ b/com/android/systemui/assist/AssistManager.java @@ -61,6 +61,7 @@ public class AssistManager implements ConfigurationChangedReceiver { private AssistOrbContainer mView; private final DeviceProvisionedController mDeviceProvisionedController; protected final AssistUtils mAssistUtils; + private final boolean mShouldEnableOrb; private IVoiceInteractionSessionShowCallback mShowCallback = new IVoiceInteractionSessionShowCallback.Stub() { @@ -96,6 +97,7 @@ public class AssistManager implements ConfigurationChangedReceiver { | ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_UI_MODE | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS); onConfigurationChanged(context.getResources().getConfiguration()); + mShouldEnableOrb = !ActivityManager.isLowRamDeviceStatic(); } protected void registerVoiceInteractionSessionListener() { @@ -179,7 +181,9 @@ public class AssistManager implements ConfigurationChangedReceiver { private void showOrb(@NonNull ComponentName assistComponent, boolean isService) { maybeSwapSearchIcon(assistComponent, isService); - mView.show(true /* show */, true /* animate */); + if (mShouldEnableOrb) { + mView.show(true /* show */, true /* animate */); + } } private void startAssistInternal(Bundle args, @NonNull ComponentName assistComponent, diff --git a/com/android/systemui/doze/AlwaysOnDisplayPolicy.java b/com/android/systemui/doze/AlwaysOnDisplayPolicy.java index 5c99961e..debda210 100644 --- a/com/android/systemui/doze/AlwaysOnDisplayPolicy.java +++ b/com/android/systemui/doze/AlwaysOnDisplayPolicy.java @@ -16,8 +16,13 @@ package com.android.systemui.doze; +import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.os.UserHandle; import android.provider.Settings; import android.text.format.DateUtils; import android.util.KeyValueListParser; @@ -34,6 +39,10 @@ import java.util.Arrays; public class AlwaysOnDisplayPolicy { public static final String TAG = "AlwaysOnDisplayPolicy"; + private static final long DEFAULT_PROX_SCREEN_OFF_DELAY_MS = 10 * DateUtils.SECOND_IN_MILLIS; + private static final long DEFAULT_PROX_COOLDOWN_TRIGGER_MS = 2 * DateUtils.SECOND_IN_MILLIS; + private static final long DEFAULT_PROX_COOLDOWN_PERIOD_MS = 5 * DateUtils.SECOND_IN_MILLIS; + static final String KEY_SCREEN_BRIGHTNESS_ARRAY = "screen_brightness_array"; static final String KEY_DIMMING_SCRIM_ARRAY = "dimming_scrim_array"; static final String KEY_PROX_SCREEN_OFF_DELAY_MS = "prox_screen_off_delay"; @@ -46,7 +55,7 @@ public class AlwaysOnDisplayPolicy { * @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS * @see #KEY_SCREEN_BRIGHTNESS_ARRAY */ - public final int[] screenBrightnessArray; + public int[] screenBrightnessArray; /** * Integer array to map ambient brightness type to dimming scrim. @@ -54,7 +63,7 @@ public class AlwaysOnDisplayPolicy { * @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS * @see #KEY_DIMMING_SCRIM_ARRAY */ - public final int[] dimmingScrimArray; + public int[] dimmingScrimArray; /** * Delay time(ms) from covering the prox to turning off the screen. @@ -62,7 +71,7 @@ public class AlwaysOnDisplayPolicy { * @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS * @see #KEY_PROX_SCREEN_OFF_DELAY_MS */ - public final long proxScreenOffDelayMs; + public long proxScreenOffDelayMs; /** * The threshold time(ms) to trigger the cooldown timer, which will @@ -71,7 +80,7 @@ public class AlwaysOnDisplayPolicy { * @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS * @see #KEY_PROX_COOLDOWN_TRIGGER_MS */ - public final long proxCooldownTriggerMs; + public long proxCooldownTriggerMs; /** * The period(ms) to turning off the prox sensor if @@ -80,43 +89,78 @@ public class AlwaysOnDisplayPolicy { * @see Settings.Global#ALWAYS_ON_DISPLAY_CONSTANTS * @see #KEY_PROX_COOLDOWN_PERIOD_MS */ - public final long proxCooldownPeriodMs; + public long proxCooldownPeriodMs; private final KeyValueListParser mParser; + private final Context mContext; + private SettingsObserver mSettingsObserver; public AlwaysOnDisplayPolicy(Context context) { - final Resources resources = context.getResources(); + mContext = context; mParser = new KeyValueListParser(','); - - final String value = Settings.Global.getString(context.getContentResolver(), - Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS); - - try { - mParser.setString(value); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Bad AOD constants"); - } - - proxScreenOffDelayMs = mParser.getLong(KEY_PROX_SCREEN_OFF_DELAY_MS, - 10 * DateUtils.SECOND_IN_MILLIS); - proxCooldownTriggerMs = mParser.getLong(KEY_PROX_COOLDOWN_TRIGGER_MS, - 2 * DateUtils.SECOND_IN_MILLIS); - proxCooldownPeriodMs = mParser.getLong(KEY_PROX_COOLDOWN_PERIOD_MS, - 5 * DateUtils.SECOND_IN_MILLIS); - screenBrightnessArray = parseIntArray(KEY_SCREEN_BRIGHTNESS_ARRAY, - resources.getIntArray(R.array.config_doze_brightness_sensor_to_brightness)); - dimmingScrimArray = parseIntArray(KEY_DIMMING_SCRIM_ARRAY, - resources.getIntArray(R.array.config_doze_brightness_sensor_to_scrim_opacity)); + mSettingsObserver = new SettingsObserver(context.getMainThreadHandler()); + mSettingsObserver.observe(); } private int[] parseIntArray(final String key, final int[] defaultArray) { final String value = mParser.getString(key, null); if (value != null) { - return Arrays.stream(value.split(":")).map(String::trim).mapToInt( - Integer::parseInt).toArray(); + try { + return Arrays.stream(value.split(":")).map(String::trim).mapToInt( + Integer::parseInt).toArray(); + } catch (NumberFormatException e) { + return defaultArray; + } } else { return defaultArray; } } + private final class SettingsObserver extends ContentObserver { + private final Uri ALWAYS_ON_DISPLAY_CONSTANTS_URI + = Settings.Global.getUriFor(Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS); + + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(ALWAYS_ON_DISPLAY_CONSTANTS_URI, + false, this, UserHandle.USER_ALL); + update(null); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + update(uri); + } + + public void update(Uri uri) { + if (uri == null || ALWAYS_ON_DISPLAY_CONSTANTS_URI.equals(uri)) { + final Resources resources = mContext.getResources(); + final String value = Settings.Global.getString(mContext.getContentResolver(), + Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS); + + try { + mParser.setString(value); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Bad AOD constants"); + } + + proxScreenOffDelayMs = mParser.getLong(KEY_PROX_SCREEN_OFF_DELAY_MS, + DEFAULT_PROX_SCREEN_OFF_DELAY_MS); + proxCooldownTriggerMs = mParser.getLong(KEY_PROX_COOLDOWN_TRIGGER_MS, + DEFAULT_PROX_COOLDOWN_TRIGGER_MS); + proxCooldownPeriodMs = mParser.getLong(KEY_PROX_COOLDOWN_PERIOD_MS, + DEFAULT_PROX_COOLDOWN_PERIOD_MS); + screenBrightnessArray = parseIntArray(KEY_SCREEN_BRIGHTNESS_ARRAY, + resources.getIntArray( + R.array.config_doze_brightness_sensor_to_brightness)); + dimmingScrimArray = parseIntArray(KEY_DIMMING_SCRIM_ARRAY, + resources.getIntArray( + R.array.config_doze_brightness_sensor_to_scrim_opacity)); + } + } + } } diff --git a/com/android/systemui/doze/DozePauser.java b/com/android/systemui/doze/DozePauser.java index 76a19021..58f14483 100644 --- a/com/android/systemui/doze/DozePauser.java +++ b/com/android/systemui/doze/DozePauser.java @@ -28,20 +28,21 @@ public class DozePauser implements DozeMachine.Part { public static final String TAG = DozePauser.class.getSimpleName(); private final AlarmTimeout mPauseTimeout; private final DozeMachine mMachine; - private final long mTimeoutMs; + private final AlwaysOnDisplayPolicy mPolicy; public DozePauser(Handler handler, DozeMachine machine, AlarmManager alarmManager, AlwaysOnDisplayPolicy policy) { mMachine = machine; mPauseTimeout = new AlarmTimeout(alarmManager, this::onTimeout, TAG, handler); - mTimeoutMs = policy.proxScreenOffDelayMs; + mPolicy = policy; } @Override public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { switch (newState) { case DOZE_AOD_PAUSING: - mPauseTimeout.schedule(mTimeoutMs, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED); + mPauseTimeout.schedule(mPolicy.proxScreenOffDelayMs, + AlarmTimeout.MODE_IGNORE_IF_SCHEDULED); break; default: mPauseTimeout.cancel(); diff --git a/com/android/systemui/doze/DozeScreenBrightness.java b/com/android/systemui/doze/DozeScreenBrightness.java index 03407e2b..4bb4e79c 100644 --- a/com/android/systemui/doze/DozeScreenBrightness.java +++ b/com/android/systemui/doze/DozeScreenBrightness.java @@ -22,6 +22,7 @@ import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Handler; +import android.os.Trace; import com.android.internal.annotations.VisibleForTesting; @@ -94,9 +95,14 @@ public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListen @Override public void onSensorChanged(SensorEvent event) { - if (mRegistered) { - mLastSensorValue = (int) event.values[0]; - updateBrightnessAndReady(); + Trace.beginSection("DozeScreenBrightness.onSensorChanged" + event.values[0]); + try { + if (mRegistered) { + mLastSensorValue = (int) event.values[0]; + updateBrightnessAndReady(); + } + } finally { + Trace.endSection(); } } diff --git a/com/android/systemui/globalactions/GlobalActionsDialog.java b/com/android/systemui/globalactions/GlobalActionsDialog.java index 4cbbbd6c..189badfc 100644 --- a/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -1280,7 +1280,23 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, DialogIn mGradientDrawable.setScreenSize(displaySize.x, displaySize.y); GradientColors colors = mColorExtractor.getColors(mKeyguardShowing ? WallpaperManager.FLAG_LOCK : WallpaperManager.FLAG_SYSTEM); - mGradientDrawable.setColors(colors, false); + updateColors(colors, false /* animate */); + } + + /** + * Updates background and system bars according to current GradientColors. + * @param colors Colors and hints to use. + * @param animate Interpolates gradient if true, just sets otherwise. + */ + private void updateColors(GradientColors colors, boolean animate) { + mGradientDrawable.setColors(colors, animate); + View decorView = getWindow().getDecorView(); + if (colors.supportsDarkText()) { + decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR | + View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); + } else { + decorView.setSystemUiVisibility(0); + } } @Override @@ -1350,11 +1366,13 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, DialogIn public void onColorsChanged(ColorExtractor extractor, int which) { if (mKeyguardShowing) { if ((WallpaperManager.FLAG_LOCK & which) != 0) { - mGradientDrawable.setColors(extractor.getColors(WallpaperManager.FLAG_LOCK)); + updateColors(extractor.getColors(WallpaperManager.FLAG_LOCK), + true /* animate */); } } else { if ((WallpaperManager.FLAG_SYSTEM & which) != 0) { - mGradientDrawable.setColors(extractor.getColors(WallpaperManager.FLAG_SYSTEM)); + updateColors(extractor.getColors(WallpaperManager.FLAG_SYSTEM), + true /* animate */); } } } diff --git a/com/android/systemui/keyguard/KeyguardViewMediator.java b/com/android/systemui/keyguard/KeyguardViewMediator.java index 3eb68f52..28adca97 100644 --- a/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -17,6 +17,7 @@ package com.android.systemui.keyguard; import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; +import static android.view.Display.INVALID_DISPLAY; import static com.android.internal.telephony.IccCardConstants.State.ABSENT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST; @@ -239,6 +240,9 @@ public class KeyguardViewMediator extends SystemUI { // answer whether the input should be restricted) private boolean mShowing; + // display id of the secondary display on which we have put a keyguard window + private int mSecondaryDisplayShowing = INVALID_DISPLAY; + /** Cached value of #isInputRestricted */ private boolean mInputRestricted; @@ -646,6 +650,13 @@ public class KeyguardViewMediator extends SystemUI { } return KeyguardSecurityView.PROMPT_REASON_NONE; } + + @Override + public void onSecondaryDisplayShowingChanged(int displayId) { + synchronized (KeyguardViewMediator.this) { + setShowingLocked(mShowing, displayId, false); + } + } }; public void userActivity() { @@ -670,7 +681,7 @@ public class KeyguardViewMediator extends SystemUI { filter.addAction(Intent.ACTION_SHUTDOWN); mContext.registerReceiver(mBroadcastReceiver, filter); - mKeyguardDisplayManager = new KeyguardDisplayManager(mContext); + mKeyguardDisplayManager = new KeyguardDisplayManager(mContext, mViewMediatorCallback); mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); @@ -685,7 +696,8 @@ public class KeyguardViewMediator extends SystemUI { com.android.keyguard.R.bool.config_enableKeyguardService)) { setShowingLocked(!shouldWaitForProvisioning() && !mLockPatternUtils.isLockScreenDisabled( - KeyguardUpdateMonitor.getCurrentUser()), true /* forceCallbacks */); + KeyguardUpdateMonitor.getCurrentUser()), + mSecondaryDisplayShowing, true /* forceCallbacks */); } mStatusBarKeyguardViewManager = @@ -1694,10 +1706,10 @@ public class KeyguardViewMediator extends SystemUI { playSound(mTrustedSoundId); } - private void updateActivityLockScreenState(boolean showing) { + private void updateActivityLockScreenState(boolean showing, int secondaryDisplayShowing) { mUiOffloadThread.submit(() -> { try { - ActivityManager.getService().setLockScreenShown(showing); + ActivityManager.getService().setLockScreenShown(showing, secondaryDisplayShowing); } catch (RemoteException e) { } }); @@ -2060,30 +2072,39 @@ public class KeyguardViewMediator extends SystemUI { } private void setShowingLocked(boolean showing) { - setShowingLocked(showing, false /* forceCallbacks */); + setShowingLocked(showing, mSecondaryDisplayShowing, false /* forceCallbacks */); } - private void setShowingLocked(boolean showing, boolean forceCallbacks) { - if (showing != mShowing || forceCallbacks) { + private void setShowingLocked( + boolean showing, int secondaryDisplayShowing, boolean forceCallbacks) { + final boolean notifyDefaultDisplayCallbacks = showing != mShowing || forceCallbacks; + if (notifyDefaultDisplayCallbacks || secondaryDisplayShowing != mSecondaryDisplayShowing) { mShowing = showing; - int size = mKeyguardStateCallbacks.size(); - for (int i = size - 1; i >= 0; i--) { - IKeyguardStateCallback callback = mKeyguardStateCallbacks.get(i); - try { - callback.onShowingStateChanged(showing); - } catch (RemoteException e) { - Slog.w(TAG, "Failed to call onShowingStateChanged", e); - if (e instanceof DeadObjectException) { - mKeyguardStateCallbacks.remove(callback); - } + mSecondaryDisplayShowing = secondaryDisplayShowing; + if (notifyDefaultDisplayCallbacks) { + notifyDefaultDisplayCallbacks(showing); + } + updateActivityLockScreenState(showing, secondaryDisplayShowing); + } + } + + private void notifyDefaultDisplayCallbacks(boolean showing) { + int size = mKeyguardStateCallbacks.size(); + for (int i = size - 1; i >= 0; i--) { + IKeyguardStateCallback callback = mKeyguardStateCallbacks.get(i); + try { + callback.onShowingStateChanged(showing); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to call onShowingStateChanged", e); + if (e instanceof DeadObjectException) { + mKeyguardStateCallbacks.remove(callback); } } - updateInputRestrictedLocked(); - mUiOffloadThread.submit(() -> { - mTrustManager.reportKeyguardShowingChanged(); - }); - updateActivityLockScreenState(showing); } + updateInputRestrictedLocked(); + mUiOffloadThread.submit(() -> { + mTrustManager.reportKeyguardShowingChanged(); + }); } private void notifyTrustedChangedLocked(boolean trusted) { diff --git a/com/android/systemui/media/NotificationPlayer.java b/com/android/systemui/media/NotificationPlayer.java index 50720e9f..b5c0d538 100644 --- a/com/android/systemui/media/NotificationPlayer.java +++ b/com/android/systemui/media/NotificationPlayer.java @@ -29,6 +29,8 @@ import android.os.PowerManager; import android.os.SystemClock; import android.util.Log; +import com.android.internal.annotations.GuardedBy; + import java.util.LinkedList; /** @@ -57,8 +59,12 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener } } - private LinkedList<Command> mCmdQueue = new LinkedList(); + private final LinkedList<Command> mCmdQueue = new LinkedList<Command>(); + private final Object mCompletionHandlingLock = new Object(); + @GuardedBy("mCompletionHandlingLock") + private CreationAndCompletionThread mCompletionThread; + @GuardedBy("mCompletionHandlingLock") private Looper mLooper; /* @@ -76,7 +82,10 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener public void run() { Looper.prepare(); + // ok to modify mLooper as here we are + // synchronized on mCompletionHandlingLock due to the Object.wait() in startSound(cmd) mLooper = Looper.myLooper(); + if (DEBUG) Log.d(mTag, "in run: new looper " + mLooper); synchronized(this) { AudioManager audioManager = (AudioManager) mCmd.context.getSystemService(Context.AUDIO_SERVICE); @@ -97,7 +106,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener if ((mCmd.uri != null) && (mCmd.uri.getEncodedPath() != null) && (mCmd.uri.getEncodedPath().length() > 0)) { if (!audioManager.isMusicActiveRemotely()) { - synchronized(mQueueAudioFocusLock) { + synchronized (mQueueAudioFocusLock) { if (mAudioManagerWithAudioFocus == null) { if (DEBUG) Log.d(mTag, "requesting AudioFocus"); int focusGain = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK; @@ -129,7 +138,9 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener Log.e(mTag, "Exception while sleeping to sync notification playback" + " with ducking", e); } + if (DEBUG) { Log.d(mTag, "player.start"); } if (mPlayer != null) { + if (DEBUG) { Log.d(mTag, "mPlayer.release"); } mPlayer.release(); } mPlayer = player; @@ -148,7 +159,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener // is playing, let it continue until we're done, so there // is less of a glitch. try { - if (DEBUG) Log.d(mTag, "Starting playback"); + if (DEBUG) { Log.d(mTag, "startSound()"); } //----------------------------------- // This is were we deviate from the AsyncPlayer implementation and create the // MediaPlayer in a new thread with which we're synchronized @@ -158,10 +169,11 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener // matters if((mLooper != null) && (mLooper.getThread().getState() != Thread.State.TERMINATED)) { + if (DEBUG) { Log.d(mTag, "in startSound quitting looper " + mLooper); } mLooper.quit(); } mCompletionThread = new CreationAndCompletionThread(cmd); - synchronized(mCompletionThread) { + synchronized (mCompletionThread) { mCompletionThread.start(); mCompletionThread.wait(); } @@ -209,13 +221,18 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener mPlayer = null; synchronized(mQueueAudioFocusLock) { if (mAudioManagerWithAudioFocus != null) { + if (DEBUG) { Log.d(mTag, "in STOP: abandonning AudioFocus"); } mAudioManagerWithAudioFocus.abandonAudioFocus(null); mAudioManagerWithAudioFocus = null; } } - if((mLooper != null) - && (mLooper.getThread().getState() != Thread.State.TERMINATED)) { - mLooper.quit(); + synchronized (mCompletionHandlingLock) { + if ((mLooper != null) && + (mLooper.getThread().getState() != Thread.State.TERMINATED)) + { + if (DEBUG) { Log.d(mTag, "in STOP: quitting looper "+ mLooper); } + mLooper.quit(); + } } } else { Log.w(mTag, "STOP command without a player"); @@ -250,9 +267,11 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener } // if there are no more sounds to play, end the Looper to listen for media completion synchronized (mCmdQueue) { - if (mCmdQueue.size() == 0) { - synchronized(mCompletionHandlingLock) { - if(mLooper != null) { + synchronized(mCompletionHandlingLock) { + if (DEBUG) { Log.d(mTag, "onCompletion queue size=" + mCmdQueue.size()); } + if ((mCmdQueue.size() == 0)) { + if (mLooper != null) { + if (DEBUG) { Log.d(mTag, "in onCompletion quitting looper " + mLooper); } mLooper.quit(); } mCompletionThread = null; @@ -269,13 +288,20 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener } private String mTag; + + @GuardedBy("mCmdQueue") private CmdThread mThread; - private CreationAndCompletionThread mCompletionThread; - private final Object mCompletionHandlingLock = new Object(); + private MediaPlayer mPlayer; + + + @GuardedBy("mCmdQueue") private PowerManager.WakeLock mWakeLock; + private final Object mQueueAudioFocusLock = new Object(); - private AudioManager mAudioManagerWithAudioFocus; // synchronized on mQueueAudioFocusLock + @GuardedBy("mQueueAudioFocusLock") + private AudioManager mAudioManagerWithAudioFocus; + private int mNotificationRampTimeMs = 0; // The current state according to the caller. Reality lags behind @@ -311,6 +337,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener */ @Deprecated public void play(Context context, Uri uri, boolean looping, int stream) { + if (DEBUG) { Log.d(mTag, "play uri=" + uri.toString()); } PlayerBase.deprecateStreamTypeForPlayback(stream, "NotificationPlayer", "play"); Command cmd = new Command(); cmd.requestTime = SystemClock.uptimeMillis(); @@ -339,6 +366,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener * (see {@link MediaPlayer#setAudioAttributes(AudioAttributes)}) */ public void play(Context context, Uri uri, boolean looping, AudioAttributes attributes) { + if (DEBUG) { Log.d(mTag, "play uri=" + uri.toString()); } Command cmd = new Command(); cmd.requestTime = SystemClock.uptimeMillis(); cmd.code = PLAY; @@ -357,6 +385,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener * at this point. Calling this multiple times has no ill effects. */ public void stop() { + if (DEBUG) { Log.d(mTag, "stop"); } synchronized (mCmdQueue) { // This check allows stop to be called multiple times without starting // a thread that ends up doing nothing. @@ -370,6 +399,7 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener } } + @GuardedBy("mCmdQueue") private void enqueueLocked(Command cmd) { mCmdQueue.add(cmd); if (mThread == null) { @@ -393,22 +423,26 @@ public class NotificationPlayer implements OnCompletionListener, OnErrorListener * @hide */ public void setUsesWakeLock(Context context) { - if (mWakeLock != null || mThread != null) { - // if either of these has happened, we've already played something. - // and our releases will be out of sync. - throw new RuntimeException("assertion failed mWakeLock=" + mWakeLock - + " mThread=" + mThread); + synchronized (mCmdQueue) { + if (mWakeLock != null || mThread != null) { + // if either of these has happened, we've already played something. + // and our releases will be out of sync. + throw new RuntimeException("assertion failed mWakeLock=" + mWakeLock + + " mThread=" + mThread); + } + PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag); } - PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag); } + @GuardedBy("mCmdQueue") private void acquireWakeLock() { if (mWakeLock != null) { mWakeLock.acquire(); } } + @GuardedBy("mCmdQueue") private void releaseWakeLock() { if (mWakeLock != null) { mWakeLock.release(); diff --git a/com/android/systemui/pip/phone/PipManager.java b/com/android/systemui/pip/phone/PipManager.java index b3f992db..f8996aae 100644 --- a/com/android/systemui/pip/phone/PipManager.java +++ b/com/android/systemui/pip/phone/PipManager.java @@ -16,7 +16,8 @@ package com.android.systemui.pip.phone; -import static android.app.ActivityManager.StackId.PINNED_STACK_ID; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.view.Display.DEFAULT_DISPLAY; import android.app.ActivityManager; @@ -30,6 +31,7 @@ import android.graphics.Rect; import android.os.Handler; import android.os.RemoteException; import android.util.Log; +import android.util.Pair; import android.view.IPinnedStackController; import android.view.IPinnedStackListener; import android.view.IWindowManager; @@ -70,11 +72,11 @@ public class PipManager implements BasePipManager { */ TaskStackListener mTaskStackListener = new TaskStackListener() { @Override - public void onActivityPinned(String packageName, int taskId) { + public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { mTouchHandler.onActivityPinned(); mMediaController.onActivityPinned(); mMenuController.onActivityPinned(); - mNotificationController.onActivityPinned(packageName, + mNotificationController.onActivityPinned(packageName, userId, true /* deferUntilAnimationEnds */); SystemServicesProxy.getInstance(mContext).setPipVisibility(true); @@ -82,13 +84,15 @@ public class PipManager implements BasePipManager { @Override public void onActivityUnpinned() { - ComponentName topPipActivity = PipUtils.getTopPinnedActivity(mContext, - mActivityManager); - mMenuController.onActivityUnpinned(topPipActivity); - mTouchHandler.onActivityUnpinned(topPipActivity); - mNotificationController.onActivityUnpinned(topPipActivity); - - SystemServicesProxy.getInstance(mContext).setPipVisibility(topPipActivity != null); + final Pair<ComponentName, Integer> topPipActivityInfo = PipUtils.getTopPinnedActivity( + mContext, mActivityManager); + final ComponentName topActivity = topPipActivityInfo.first; + final int userId = topActivity != null ? topPipActivityInfo.second : 0; + mMenuController.onActivityUnpinned(); + mTouchHandler.onActivityUnpinned(topActivity); + mNotificationController.onActivityUnpinned(topActivity, userId); + + SystemServicesProxy.getInstance(mContext).setPipVisibility(topActivity != null); } @Override @@ -196,7 +200,8 @@ public class PipManager implements BasePipManager { public final void onBusEvent(ExpandPipEvent event) { if (event.clearThumbnailWindows) { try { - StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID); + StackInfo stackInfo = mActivityManager.getStackInfo( + WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); if (stackInfo != null && stackInfo.taskIds != null) { SystemServicesProxy ssp = SystemServicesProxy.getInstance(mContext); for (int taskId : stackInfo.taskIds) { diff --git a/com/android/systemui/pip/phone/PipMediaController.java b/com/android/systemui/pip/phone/PipMediaController.java index b3a0794f..174a7ef1 100644 --- a/com/android/systemui/pip/phone/PipMediaController.java +++ b/com/android/systemui/pip/phone/PipMediaController.java @@ -230,7 +230,7 @@ public class PipMediaController { private void resolveActiveMediaController(List<MediaController> controllers) { if (controllers != null) { final ComponentName topActivity = PipUtils.getTopPinnedActivity(mContext, - mActivityManager); + mActivityManager).first; if (topActivity != null) { for (int i = 0; i < controllers.size(); i++) { final MediaController controller = controllers.get(i); diff --git a/com/android/systemui/pip/phone/PipMenuActivityController.java b/com/android/systemui/pip/phone/PipMenuActivityController.java index 34666fb3..9fb201b8 100644 --- a/com/android/systemui/pip/phone/PipMenuActivityController.java +++ b/com/android/systemui/pip/phone/PipMenuActivityController.java @@ -16,7 +16,8 @@ package com.android.systemui.pip.phone; -import static android.app.ActivityManager.StackId.PINNED_STACK_ID; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import android.app.ActivityManager.StackInfo; import android.app.ActivityOptions; @@ -223,7 +224,7 @@ public class PipMenuActivityController { } } - public void onActivityUnpinned(ComponentName topPipActivity) { + public void onActivityUnpinned() { hideMenu(); setStartActivityRequested(false); } @@ -383,7 +384,8 @@ public class PipMenuActivityController { private void startMenuActivity(int menuState, Rect stackBounds, Rect movementBounds, boolean allowMenuTimeout, boolean willResizeMenu) { try { - StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID); + StackInfo pinnedStackInfo = mActivityManager.getStackInfo( + WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null && pinnedStackInfo.taskIds.length > 0) { Intent intent = new Intent(mContext, PipMenuActivity.class); @@ -421,7 +423,8 @@ public class PipMenuActivityController { // Fetch the pinned stack bounds Rect stackBounds = null; try { - StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID); + StackInfo pinnedStackInfo = mActivityManager.getStackInfo( + WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); if (pinnedStackInfo != null) { stackBounds = pinnedStackInfo.bounds; } diff --git a/com/android/systemui/pip/phone/PipMotionHelper.java b/com/android/systemui/pip/phone/PipMotionHelper.java index cebb22f0..21a836c0 100644 --- a/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/com/android/systemui/pip/phone/PipMotionHelper.java @@ -16,8 +16,10 @@ package com.android.systemui.pip.phone; -import static android.app.ActivityManager.StackId.PINNED_STACK_ID; +import static android.app.ActivityManager.StackId.INVALID_STACK_ID; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static com.android.systemui.Interpolators.FAST_OUT_LINEAR_IN; import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN; import static com.android.systemui.Interpolators.LINEAR_OUT_SLOW_IN; @@ -121,7 +123,8 @@ public class PipMotionHelper implements Handler.Callback { void synchronizePinnedStackBounds() { cancelAnimations(); try { - StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID); + StackInfo stackInfo = + mActivityManager.getStackInfo(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); if (stackInfo != null) { mBounds.set(stackInfo.bounds); } @@ -158,13 +161,7 @@ public class PipMotionHelper implements Handler.Callback { mMenuController.hideMenuWithoutResize(); mHandler.post(() -> { try { - if (skipAnimation) { - mActivityManager.moveTasksToFullscreenStack(PINNED_STACK_ID, true /* onTop */); - } else { - mActivityManager.resizeStack(PINNED_STACK_ID, null /* bounds */, - true /* allowResizeInDockedMode */, true /* preserveWindows */, - true /* animate */, EXPAND_STACK_TO_FULLSCREEN_DURATION); - } + mActivityManager.dismissPip(!skipAnimation, EXPAND_STACK_TO_FULLSCREEN_DURATION); } catch (RemoteException e) { Log.e(TAG, "Error expanding PiP activity", e); } @@ -182,7 +179,7 @@ public class PipMotionHelper implements Handler.Callback { mMenuController.hideMenuWithoutResize(); mHandler.post(() -> { try { - mActivityManager.removeStack(PINNED_STACK_ID); + mActivityManager.removeStacksInWindowingModes(new int[]{ WINDOWING_MODE_PINNED }); } catch (RemoteException e) { Log.e(TAG, "Failed to remove PiP", e); } @@ -529,14 +526,15 @@ public class PipMotionHelper implements Handler.Callback { Rect toBounds = (Rect) args.arg1; int duration = args.argi1; try { - StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID); + StackInfo stackInfo = mActivityManager.getStackInfo( + WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); if (stackInfo == null) { // In the case where we've already re-expanded or dismissed the PiP, then // just skip the resize return true; } - mActivityManager.resizeStack(PINNED_STACK_ID, toBounds, + mActivityManager.resizeStack(stackInfo.stackId, toBounds, false /* allowResizeInDockedMode */, true /* preserveWindows */, true /* animate */, duration); mBounds.set(toBounds); diff --git a/com/android/systemui/pip/phone/PipNotificationController.java b/com/android/systemui/pip/phone/PipNotificationController.java index 696fdbc8..6d083e9d 100644 --- a/com/android/systemui/pip/phone/PipNotificationController.java +++ b/com/android/systemui/pip/phone/PipNotificationController.java @@ -35,10 +35,15 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.Resources; -import android.graphics.drawable.Icon; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.UserHandle; +import android.util.IconDrawableFactory; import android.util.Log; +import android.util.Pair; import com.android.systemui.R; import com.android.systemui.SystemUI; @@ -57,22 +62,29 @@ public class PipNotificationController { private IActivityManager mActivityManager; private AppOpsManager mAppOpsManager; private NotificationManager mNotificationManager; + private IconDrawableFactory mIconDrawableFactory; private PipMotionHelper mMotionHelper; // Used when building a deferred notification private String mDeferredNotificationPackageName; + private int mDeferredNotificationUserId; private AppOpsManager.OnOpChangedListener mAppOpsChangedListener = new OnOpChangedListener() { @Override public void onOpChanged(String op, String packageName) { try { // Dismiss the PiP once the user disables the app ops setting for that package - final ApplicationInfo appInfo = mContext.getPackageManager().getApplicationInfo( - packageName, 0); - if (mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid, packageName) - != MODE_ALLOWED) { - mMotionHelper.dismissPip(); + final Pair<ComponentName, Integer> topPipActivityInfo = + PipUtils.getTopPinnedActivity(mContext, mActivityManager); + if (topPipActivityInfo.first != null) { + final ApplicationInfo appInfo = mContext.getPackageManager() + .getApplicationInfoAsUser(packageName, 0, topPipActivityInfo.second); + if (appInfo.packageName.equals(topPipActivityInfo.first.getPackageName()) && + mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid, + packageName) != MODE_ALLOWED) { + mMotionHelper.dismissPip(); + } } } catch (NameNotFoundException e) { // Unregister the listener if the package can't be found @@ -88,16 +100,18 @@ public class PipNotificationController { mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); mNotificationManager = NotificationManager.from(context); mMotionHelper = motionHelper; + mIconDrawableFactory = IconDrawableFactory.newInstance(context); } - public void onActivityPinned(String packageName, boolean deferUntilAnimationEnds) { + public void onActivityPinned(String packageName, int userId, boolean deferUntilAnimationEnds) { // Clear any existing notification mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID); if (deferUntilAnimationEnds) { mDeferredNotificationPackageName = packageName; + mDeferredNotificationUserId = userId; } else { - showNotificationForApp(mDeferredNotificationPackageName); + showNotificationForApp(packageName, userId); } // Register for changes to the app ops setting for this package while it is in PiP @@ -106,22 +120,25 @@ public class PipNotificationController { public void onPinnedStackAnimationEnded() { if (mDeferredNotificationPackageName != null) { - showNotificationForApp(mDeferredNotificationPackageName); + showNotificationForApp(mDeferredNotificationPackageName, mDeferredNotificationUserId); mDeferredNotificationPackageName = null; + mDeferredNotificationUserId = 0; } } - public void onActivityUnpinned(ComponentName topPipActivity) { + public void onActivityUnpinned(ComponentName topPipActivity, int userId) { // Unregister for changes to the previously PiP'ed package unregisterAppOpsListener(); // Reset the deferred notification package mDeferredNotificationPackageName = null; + mDeferredNotificationUserId = 0; if (topPipActivity != null) { // onActivityUnpinned() is only called after the transition is complete, so we don't // need to defer until the animation ends to update the notification - onActivityPinned(topPipActivity.getPackageName(), false /* deferUntilAnimationEnds */); + onActivityPinned(topPipActivity.getPackageName(), userId, + false /* deferUntilAnimationEnds */); } else { mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID); } @@ -130,20 +147,27 @@ public class PipNotificationController { /** * Builds and shows the notification for the given app. */ - private void showNotificationForApp(String packageName) { + private void showNotificationForApp(String packageName, int userId) { // Build a new notification - final Notification.Builder builder = - new Notification.Builder(mContext, NotificationChannels.GENERAL) - .setLocalOnly(true) - .setOngoing(true) - .setSmallIcon(R.drawable.pip_notification_icon) - .setColor(mContext.getColor( - com.android.internal.R.color.system_notification_accent_color)); - if (updateNotificationForApp(builder, packageName)) { - SystemUI.overrideNotificationAppName(mContext, builder); - - // Show the new notification - mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build()); + try { + final UserHandle user = UserHandle.of(userId); + final Context userContext = mContext.createPackageContextAsUser( + mContext.getPackageName(), 0, user); + final Notification.Builder builder = + new Notification.Builder(userContext, NotificationChannels.GENERAL) + .setLocalOnly(true) + .setOngoing(true) + .setSmallIcon(R.drawable.pip_notification_icon) + .setColor(mContext.getColor( + com.android.internal.R.color.system_notification_accent_color)); + if (updateNotificationForApp(builder, packageName, user)) { + SystemUI.overrideNotificationAppName(mContext, builder); + + // Show the new notification + mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, builder.build()); + } + } catch (NameNotFoundException e) { + Log.e(TAG, "Could not show notification for application", e); } } @@ -151,33 +175,33 @@ public class PipNotificationController { * Updates the notification builder with app-specific information, returning whether it was * successful. */ - private boolean updateNotificationForApp(Notification.Builder builder, String packageName) { + private boolean updateNotificationForApp(Notification.Builder builder, String packageName, + UserHandle user) throws NameNotFoundException { final PackageManager pm = mContext.getPackageManager(); final ApplicationInfo appInfo; try { - appInfo = pm.getApplicationInfo(packageName, 0); + appInfo = pm.getApplicationInfoAsUser(packageName, 0, user.getIdentifier()); } catch (NameNotFoundException e) { Log.e(TAG, "Could not update notification for application", e); return false; } if (appInfo != null) { - final String appName = pm.getApplicationLabel(appInfo).toString(); + final String appName = pm.getUserBadgedLabel(pm.getApplicationLabel(appInfo), user) + .toString(); final String message = mContext.getString(R.string.pip_notification_message, appName); final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS, Uri.fromParts("package", packageName, null)); + settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user); settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK); - final Icon appIcon = appInfo.icon != 0 - ? Icon.createWithResource(packageName, appInfo.icon) - : Icon.createWithResource(Resources.getSystem(), - com.android.internal.R.drawable.sym_def_app_icon); + final Drawable iconDrawable = mIconDrawableFactory.getBadgedIcon(appInfo); builder.setContentTitle(mContext.getString(R.string.pip_notification_title, appName)) .setContentText(message) - .setContentIntent(PendingIntent.getActivity(mContext, packageName.hashCode(), - settingsIntent, FLAG_CANCEL_CURRENT)) + .setContentIntent(PendingIntent.getActivityAsUser(mContext, packageName.hashCode(), + settingsIntent, FLAG_CANCEL_CURRENT, null, user)) .setStyle(new Notification.BigTextStyle().bigText(message)) - .setLargeIcon(appIcon); + .setLargeIcon(createBitmap(iconDrawable).createAshmemBitmap()); return true; } return false; @@ -191,4 +215,17 @@ public class PipNotificationController { private void unregisterAppOpsListener() { mAppOpsManager.stopWatchingMode(mAppOpsChangedListener); } + + /** + * Bakes a drawable into a bitmap. + */ + private Bitmap createBitmap(Drawable d) { + Bitmap bitmap = Bitmap.createBitmap(d.getIntrinsicWidth(), d.getIntrinsicHeight(), + Config.ARGB_8888); + Canvas c = new Canvas(bitmap); + d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); + d.draw(c); + c.setBitmap(null); + return bitmap; + } } diff --git a/com/android/systemui/pip/phone/PipUtils.java b/com/android/systemui/pip/phone/PipUtils.java index a8cdd1bd..2f53de96 100644 --- a/com/android/systemui/pip/phone/PipUtils.java +++ b/com/android/systemui/pip/phone/PipUtils.java @@ -16,7 +16,8 @@ package com.android.systemui.pip.phone; -import static android.app.ActivityManager.StackId.PINNED_STACK_ID; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import android.app.ActivityManager.StackInfo; import android.app.IActivityManager; @@ -24,33 +25,35 @@ import android.content.ComponentName; import android.content.Context; import android.os.RemoteException; import android.util.Log; +import android.util.Pair; public class PipUtils { private static final String TAG = "PipUtils"; /** - * @return the ComponentName of the top non-SystemUI activity in the pinned stack, or null if - * none exists. + * @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack. + * The component name may be null if no such activity exists. */ - public static ComponentName getTopPinnedActivity(Context context, + public static Pair<ComponentName, Integer> getTopPinnedActivity(Context context, IActivityManager activityManager) { try { final String sysUiPackageName = context.getPackageName(); - final StackInfo pinnedStackInfo = activityManager.getStackInfo(PINNED_STACK_ID); + final StackInfo pinnedStackInfo = + activityManager.getStackInfo(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null && pinnedStackInfo.taskIds.length > 0) { for (int i = pinnedStackInfo.taskNames.length - 1; i >= 0; i--) { ComponentName cn = ComponentName.unflattenFromString( pinnedStackInfo.taskNames[i]); if (cn != null && !cn.getPackageName().equals(sysUiPackageName)) { - return cn; + return new Pair<>(cn, pinnedStackInfo.taskUserIds[i]); } } } } catch (RemoteException e) { Log.w(TAG, "Unable to get pinned stack."); } - return null; + return new Pair<>(null, 0); } } diff --git a/com/android/systemui/pip/tv/PipManager.java b/com/android/systemui/pip/tv/PipManager.java index e8c12952..e0445c16 100644 --- a/com/android/systemui/pip/tv/PipManager.java +++ b/com/android/systemui/pip/tv/PipManager.java @@ -53,7 +53,9 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; -import static android.app.ActivityManager.StackId.PINNED_STACK_ID; +import static android.app.ActivityManager.StackId.INVALID_STACK_ID; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.view.Display.DEFAULT_DISPLAY; /** @@ -121,6 +123,7 @@ public class PipManager implements BasePipManager { private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED; private boolean mInitialized; private int mPipTaskId = TASK_ID_NO_PIP; + private int mPinnedStackId = INVALID_STACK_ID; private ComponentName mPipComponentName; private MediaController mPipMediaController; private String[] mLastPackagesResourceGranted; @@ -336,9 +339,11 @@ public class PipManager implements BasePipManager { mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveMediaSessionListener); if (removePipStack) { try { - mActivityManager.removeStack(PINNED_STACK_ID); + mActivityManager.removeStack(mPinnedStackId); } catch (RemoteException e) { Log.e(TAG, "removeStack failed", e); + } finally { + mPinnedStackId = INVALID_STACK_ID; } } for (int i = mListeners.size() - 1; i >= 0; --i) { @@ -424,7 +429,7 @@ public class PipManager implements BasePipManager { } try { int animationDurationMs = -1; - mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds, + mActivityManager.resizeStack(mPinnedStackId, mCurrentPipBounds, true, true, true, animationDurationMs); } catch (RemoteException e) { Log.e(TAG, "resizeStack failed", e); @@ -502,7 +507,8 @@ public class PipManager implements BasePipManager { private StackInfo getPinnedStackInfo() { StackInfo stackInfo = null; try { - stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID); + stackInfo = mActivityManager.getStackInfo( + WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); } catch (RemoteException e) { Log.e(TAG, "getStackInfo failed", e); } @@ -654,7 +660,7 @@ public class PipManager implements BasePipManager { } @Override - public void onActivityPinned(String packageName, int taskId) { + public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { if (DEBUG) Log.d(TAG, "onActivityPinned()"); if (!checkCurrentUserId(mContext, DEBUG)) { return; @@ -665,6 +671,7 @@ public class PipManager implements BasePipManager { return; } if (DEBUG) Log.d(TAG, "PINNED_STACK:" + stackInfo); + mPinnedStackId = stackInfo.stackId; mPipTaskId = stackInfo.taskIds[stackInfo.taskIds.length - 1]; mPipComponentName = ComponentName.unflattenFromString( stackInfo.taskNames[stackInfo.taskNames.length - 1]); diff --git a/com/android/systemui/qs/AlphaControlledSignalTileView.java b/com/android/systemui/qs/AlphaControlledSignalTileView.java new file mode 100644 index 00000000..2c7ec70a --- /dev/null +++ b/com/android/systemui/qs/AlphaControlledSignalTileView.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2017 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.systemui.qs; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.drawable.Drawable; +import com.android.systemui.qs.tileimpl.SlashImageView; + + +/** + * Creates AlphaControlledSlashImageView instead of SlashImageView + */ +public class AlphaControlledSignalTileView extends SignalTileView { + public AlphaControlledSignalTileView(Context context) { + super(context); + } + + @Override + protected SlashImageView createSlashImageView(Context context) { + return new AlphaControlledSlashImageView(context); + } + + /** + * Creates AlphaControlledSlashDrawable instead of regular SlashDrawables + */ + public static class AlphaControlledSlashImageView extends SlashImageView { + public AlphaControlledSlashImageView(Context context) { + super(context); + } + + public void setFinalImageTintList(ColorStateList tint) { + super.setImageTintList(tint); + final SlashDrawable slash = getSlash(); + if (slash != null) { + ((AlphaControlledSlashDrawable)slash).setFinalTintList(tint); + } + } + + @Override + protected void ensureSlashDrawable() { + if (getSlash() == null) { + final SlashDrawable slash = new AlphaControlledSlashDrawable(getDrawable()); + setSlash(slash); + slash.setAnimationEnabled(getAnimationEnabled()); + setImageViewDrawable(slash); + } + } + } + + /** + * SlashDrawable that disobeys orders to change its drawable's tint except when you tell + * it not to disobey. The slash still will animate its alpha. + */ + public static class AlphaControlledSlashDrawable extends SlashDrawable { + AlphaControlledSlashDrawable(Drawable d) { + super(d); + } + + @Override + protected void setDrawableTintList(ColorStateList tint) { + } + + /** + * Set a target tint list instead of + */ + public void setFinalTintList(ColorStateList tint) { + super.setDrawableTintList(tint); + } + } +} + diff --git a/com/android/systemui/qs/SignalTileView.java b/com/android/systemui/qs/SignalTileView.java index b300e4a3..9ee40ccf 100644 --- a/com/android/systemui/qs/SignalTileView.java +++ b/com/android/systemui/qs/SignalTileView.java @@ -63,13 +63,17 @@ public class SignalTileView extends QSIconViewImpl { @Override protected View createIcon() { mIconFrame = new FrameLayout(mContext); - mSignal = new SlashImageView(mContext); + mSignal = createSlashImageView(mContext); mIconFrame.addView(mSignal); mOverlay = new ImageView(mContext); mIconFrame.addView(mOverlay, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); return mIconFrame; } + protected SlashImageView createSlashImageView(Context context) { + return new SlashImageView(context); + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); diff --git a/com/android/systemui/qs/SlashDrawable.java b/com/android/systemui/qs/SlashDrawable.java index c3561489..a9b2376e 100644 --- a/com/android/systemui/qs/SlashDrawable.java +++ b/com/android/systemui/qs/SlashDrawable.java @@ -197,11 +197,15 @@ public class SlashDrawable extends Drawable { public void setTintList(@Nullable ColorStateList tint) { mTintList = tint; super.setTintList(tint); - mDrawable.setTintList(tint); + setDrawableTintList(tint); mPaint.setColor(tint.getDefaultColor()); invalidateSelf(); } + protected void setDrawableTintList(@Nullable ColorStateList tint) { + mDrawable.setTintList(tint); + } + @Override public void setTintMode(@NonNull Mode tintMode) { mTintMode = tintMode; diff --git a/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/com/android/systemui/qs/tileimpl/QSIconViewImpl.java index 8074cb9b..e8c8b907 100644 --- a/com/android/systemui/qs/tileimpl/QSIconViewImpl.java +++ b/com/android/systemui/qs/tileimpl/QSIconViewImpl.java @@ -33,6 +33,7 @@ import com.android.systemui.plugins.qs.QSIconView; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTile.State; +import com.android.systemui.qs.AlphaControlledSignalTileView.AlphaControlledSlashImageView; import java.util.Objects; public class QSIconViewImpl extends QSIconView { @@ -138,7 +139,12 @@ public class QSIconViewImpl extends QSIconView { animateGrayScale(mTint, color, iv); mTint = color; } else { - setTint(iv, color); + if (iv instanceof AlphaControlledSlashImageView) { + ((AlphaControlledSlashImageView)iv) + .setFinalImageTintList(ColorStateList.valueOf(color)); + } else { + setTint(iv, color); + } mTint = color; } } @@ -149,6 +155,10 @@ public class QSIconViewImpl extends QSIconView { } public static void animateGrayScale(int fromColor, int toColor, ImageView iv) { + if (iv instanceof AlphaControlledSlashImageView) { + ((AlphaControlledSlashImageView)iv) + .setFinalImageTintList(ColorStateList.valueOf(toColor)); + } if (ValueAnimator.areAnimatorsEnabled()) { final float fromAlpha = Color.alpha(fromColor); final float toAlpha = Color.alpha(toColor); diff --git a/com/android/systemui/qs/tileimpl/SlashImageView.java b/com/android/systemui/qs/tileimpl/SlashImageView.java index 97e9c3df..63d6f82c 100644 --- a/com/android/systemui/qs/tileimpl/SlashImageView.java +++ b/com/android/systemui/qs/tileimpl/SlashImageView.java @@ -34,7 +34,15 @@ public class SlashImageView extends ImageView { super(context); } - private void ensureSlashDrawable() { + protected SlashDrawable getSlash() { + return mSlash; + } + + protected void setSlash(SlashDrawable slash) { + mSlash = slash; + } + + protected void ensureSlashDrawable() { if (mSlash == null) { mSlash = new SlashDrawable(getDrawable()); mSlash.setAnimationEnabled(mAnimationEnabled); @@ -56,10 +64,18 @@ public class SlashImageView extends ImageView { } } + protected void setImageViewDrawable(SlashDrawable slash) { + super.setImageDrawable(slash); + } + public void setAnimationEnabled(boolean enabled) { mAnimationEnabled = enabled; } + public boolean getAnimationEnabled() { + return mAnimationEnabled; + } + private void setSlashState(@NonNull SlashState slashState) { ensureSlashDrawable(); mSlash.setRotation(slashState.rotation); diff --git a/com/android/systemui/qs/tiles/BluetoothTile.java b/com/android/systemui/qs/tiles/BluetoothTile.java index 8d62f2aa..81b8622c 100644 --- a/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/com/android/systemui/qs/tiles/BluetoothTile.java @@ -23,6 +23,7 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Intent; +import android.graphics.drawable.Drawable; import android.provider.Settings; import android.service.quicksettings.Tile; import android.text.TextUtils; @@ -34,10 +35,8 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.settingslib.Utils; import com.android.settingslib.bluetooth.CachedBluetoothDevice; -import com.android.settingslib.graph.BluetoothDeviceLayerDrawable; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.R.drawable; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.plugins.qs.QSTile.BooleanState; @@ -135,11 +134,7 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { if (lastDevice != null) { int batteryLevel = lastDevice.getBatteryLevel(); if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) { - BluetoothDeviceLayerDrawable drawable = createLayerDrawable(mContext, - R.drawable.ic_qs_bluetooth_connected, batteryLevel, - mContext.getResources().getFraction( - R.fraction.bt_battery_scale_fraction, 1, 1)); - state.icon = new DrawableIcon(drawable); + state.icon = new BluetoothBatteryDrawable(batteryLevel); } } state.contentDescription = mContext.getString( @@ -215,6 +210,22 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { return new BluetoothDetailAdapter(); } + private class BluetoothBatteryDrawable extends Icon { + private int mLevel; + + BluetoothBatteryDrawable(int level) { + mLevel = level; + } + + @Override + public Drawable getDrawable(Context context) { + return createLayerDrawable(context, + R.drawable.ic_qs_bluetooth_connected, mLevel, + context.getResources().getFraction( + R.fraction.bt_battery_scale_fraction, 1, 1)); + } + } + protected class BluetoothDetailAdapter implements DetailAdapter, QSDetailItems.Callback { // We probably won't ever have space in the UI for more than 20 devices, so don't // get info for them. diff --git a/com/android/systemui/qs/tiles/CellularTile.java b/com/android/systemui/qs/tiles/CellularTile.java index 2e389ba1..0ce3e6aa 100644 --- a/com/android/systemui/qs/tiles/CellularTile.java +++ b/com/android/systemui/qs/tiles/CellularTile.java @@ -266,7 +266,7 @@ public class CellularTile extends QSTileImpl<SignalState> { } @Override - public void setNoSims(boolean show) { + public void setNoSims(boolean show, boolean simDetected) { mInfo.noSim = show; if (mInfo.noSim) { // Make sure signal gets cleared out when no sims. diff --git a/com/android/systemui/qs/tiles/WifiTile.java b/com/android/systemui/qs/tiles/WifiTile.java index 33b15121..23702736 100644 --- a/com/android/systemui/qs/tiles/WifiTile.java +++ b/com/android/systemui/qs/tiles/WifiTile.java @@ -37,10 +37,10 @@ import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.plugins.qs.QSIconView; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTile.SignalState; +import com.android.systemui.qs.AlphaControlledSignalTileView; import com.android.systemui.qs.QSDetailItems; import com.android.systemui.qs.QSDetailItems.Item; import com.android.systemui.qs.QSHost; -import com.android.systemui.qs.SignalTileView; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.NetworkController.AccessPointController; @@ -104,7 +104,7 @@ public class WifiTile extends QSTileImpl<SignalState> { @Override public QSIconView createTileView(Context context) { - return new SignalTileView(context); + return new AlphaControlledSignalTileView(context); } @Override diff --git a/com/android/systemui/recents/Recents.java b/com/android/systemui/recents/Recents.java index 406bcac0..283ac0c4 100644 --- a/com/android/systemui/recents/Recents.java +++ b/com/android/systemui/recents/Recents.java @@ -16,6 +16,9 @@ package com.android.systemui.recents; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS; import android.app.ActivityManager; @@ -437,9 +440,12 @@ public class Recents extends SystemUI int currentUser = sSystemServicesProxy.getCurrentUser(); SystemServicesProxy ssp = Recents.getSystemServices(); ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask(); + final int activityType = runningTask != null + ? runningTask.configuration.windowConfiguration.getActivityType() + : ACTIVITY_TYPE_UNDEFINED; boolean screenPinningActive = ssp.isScreenPinningActive(); - boolean isRunningTaskInHomeOrRecentsStack = runningTask != null && - ActivityManager.StackId.isHomeOrRecentsStack(runningTask.stackId); + boolean isRunningTaskInHomeOrRecentsStack = + activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS; if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) { logDockAttempt(mContext, runningTask.topActivity, runningTask.resizeMode); if (runningTask.supportsSplitScreenMultiWindow) { diff --git a/com/android/systemui/recents/RecentsActivity.java b/com/android/systemui/recents/RecentsActivity.java index f5455568..86b77900 100644 --- a/com/android/systemui/recents/RecentsActivity.java +++ b/com/android/systemui/recents/RecentsActivity.java @@ -358,6 +358,9 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD mScrimViews = new SystemBarScrimViews(this); getWindow().getAttributes().privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY; + if (Recents.getConfiguration().isLowRamDevice) { + getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + } mLastConfig = new Configuration(Utilities.getAppConfiguration(this)); mFocusTimerDuration = getResources().getInteger(R.integer.recents_auto_advance_duration); diff --git a/com/android/systemui/recents/RecentsImpl.java b/com/android/systemui/recents/RecentsImpl.java index aecf95fc..3e2a5f3f 100644 --- a/com/android/systemui/recents/RecentsImpl.java +++ b/com/android/systemui/recents/RecentsImpl.java @@ -16,9 +16,9 @@ package com.android.systemui.recents; -import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.INVALID_STACK_ID; -import static android.app.ActivityManager.StackId.isHomeOrRecentsStack; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.view.View.MeasureSpec; import android.app.ActivityManager; @@ -173,7 +173,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener } @Override - public void onActivityPinned(String packageName, int taskId) { + public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { // Check this is for the right user if (!checkCurrentUserId(mContext, false /* debug */)) { return; @@ -533,7 +533,9 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener if (runningTask == null) return; // Find the task in the recents list - boolean isRunningTaskInHomeStack = SystemServicesProxy.isHomeStack(runningTask.stackId); + boolean isRunningTaskInHomeStack = + runningTask.configuration.windowConfiguration.getActivityType() + == ACTIVITY_TYPE_HOME; ArrayList<Task> tasks = focusedStack.getStackTasks(); Task toTask = null; ActivityOptions launchOpts = null; @@ -565,8 +567,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // Launch the task ssp.startActivityFromRecents( - mContext, toTask.key, toTask.title, launchOpts, INVALID_STACK_ID, - null /* resultListener */); + mContext, toTask.key, toTask.title, launchOpts, null /* resultListener */); } /** @@ -584,9 +585,10 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // Return early if there is no running task (can't determine affiliated tasks in this case) ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask(); + final int activityType = runningTask.configuration.windowConfiguration.getActivityType(); if (runningTask == null) return; // Return early if the running task is in the home/recents stack (optimization) - if (isHomeOrRecentsStack(runningTask.stackId)) return; + if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) return; // Find the task in the recents list ArrayList<Task> tasks = focusedStack.getStackTasks(); @@ -639,8 +641,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // Launch the task ssp.startActivityFromRecents( - mContext, toTask.key, toTask.title, launchOpts, INVALID_STACK_ID, - null /* resultListener */); + mContext, toTask.key, toTask.title, launchOpts, null /* resultListener */); } public void showNextAffiliatedTask() { @@ -872,7 +873,9 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo runningTask, Rect windowOverrideRect) { final boolean isLowRamDevice = Recents.getConfiguration().isLowRamDevice; - if (runningTask != null && runningTask.stackId == FREEFORM_WORKSPACE_STACK_ID) { + if (runningTask != null + && runningTask.configuration.windowConfiguration.getWindowingMode() + == WINDOWING_MODE_FREEFORM) { ArrayList<AppTransitionAnimationSpec> specs = new ArrayList<>(); ArrayList<Task> tasks = mDummyStackView.getStack().getStackTasks(); TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm(); diff --git a/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java b/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java index e02fb147..e4a4f592 100644 --- a/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java +++ b/com/android/systemui/recents/events/activity/HideStackActionButtonEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2017 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. @@ -22,5 +22,15 @@ import com.android.systemui.recents.events.EventBus; * This is sent when the stack action button should be hidden. */ public class HideStackActionButtonEvent extends EventBus.Event { - // Simple event + + // Whether or not to translate the stack action button when hiding it + public final boolean translate; + + public HideStackActionButtonEvent() { + this(true); + } + + public HideStackActionButtonEvent(boolean translate) { + this.translate = translate; + } } diff --git a/com/android/systemui/recents/events/activity/LaunchTaskEvent.java b/com/android/systemui/recents/events/activity/LaunchTaskEvent.java index 3db106e7..862a1eee 100644 --- a/com/android/systemui/recents/events/activity/LaunchTaskEvent.java +++ b/com/android/systemui/recents/events/activity/LaunchTaskEvent.java @@ -16,6 +16,9 @@ package com.android.systemui.recents.events.activity; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; + import android.graphics.Rect; import com.android.systemui.recents.events.EventBus; @@ -30,15 +33,23 @@ public class LaunchTaskEvent extends EventBus.Event { public final TaskView taskView; public final Task task; public final Rect targetTaskBounds; - public final int targetTaskStack; + public final int targetWindowingMode; + public final int targetActivityType; public final boolean screenPinningRequested; - public LaunchTaskEvent(TaskView taskView, Task task, Rect targetTaskBounds, int targetTaskStack, + public LaunchTaskEvent(TaskView taskView, Task task, Rect targetTaskBounds, boolean screenPinningRequested) { + this(taskView, task, targetTaskBounds, screenPinningRequested, + WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED); + } + + public LaunchTaskEvent(TaskView taskView, Task task, Rect targetTaskBounds, + boolean screenPinningRequested, int windowingMode, int activityType) { this.taskView = taskView; this.task = task; this.targetTaskBounds = targetTaskBounds; - this.targetTaskStack = targetTaskStack; + this.targetWindowingMode = windowingMode; + this.targetActivityType = activityType; this.screenPinningRequested = screenPinningRequested; } diff --git a/com/android/systemui/recents/misc/SystemServicesProxy.java b/com/android/systemui/recents/misc/SystemServicesProxy.java index 71777822..bddf9a59 100644 --- a/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -16,13 +16,15 @@ package com.android.systemui.recents.misc; -import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; -import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.HOME_STACK_ID; -import static android.app.ActivityManager.StackId.INVALID_STACK_ID; -import static android.app.ActivityManager.StackId.PINNED_STACK_ID; -import static android.app.ActivityManager.StackId.RECENTS_STACK_ID; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; import android.annotation.NonNull; @@ -34,6 +36,7 @@ import android.app.ActivityOptions; import android.app.AppGlobals; import android.app.IActivityManager; import android.app.KeyguardManager; +import android.app.WindowConfiguration; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -172,7 +175,7 @@ public class SystemServicesProxy { public void onTaskStackChangedBackground() { } public void onTaskStackChanged() { } public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { } - public void onActivityPinned(String packageName, int taskId) { } + public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { } public void onActivityUnpinned() { } public void onPinnedActivityRestartAttempt(boolean clearedTask) { } public void onPinnedStackAnimationStarted() { } @@ -227,9 +230,11 @@ public class SystemServicesProxy { } @Override - public void onActivityPinned(String packageName, int taskId) throws RemoteException { + public void onActivityPinned(String packageName, int userId, int taskId, int stackId) + throws RemoteException { mHandler.removeMessages(H.ON_ACTIVITY_PINNED); - mHandler.obtainMessage(H.ON_ACTIVITY_PINNED, taskId, 0, packageName).sendToTarget(); + mHandler.obtainMessage(H.ON_ACTIVITY_PINNED, + new PinnedActivityInfo(packageName, userId, taskId, stackId)).sendToTarget(); } @Override @@ -481,16 +486,22 @@ public class SystemServicesProxy { public ActivityManager.RunningTaskInfo getRunningTask() { // Note: The set of running tasks from the system is ordered by recency List<ActivityManager.RunningTaskInfo> tasks = mAm.getRunningTasks(10); - if (tasks != null && !tasks.isEmpty()) { - // Find the first task in a valid stack, we ignore everything from the Recents and PiP - // stacks - for (int i = 0; i < tasks.size(); i++) { - ActivityManager.RunningTaskInfo task = tasks.get(i); - int stackId = task.stackId; - if (stackId != RECENTS_STACK_ID && stackId != PINNED_STACK_ID) { - return task; - } + if (tasks == null || tasks.isEmpty()) { + return null; + } + + // Find the first task in a valid stack, we ignore everything from the Recents and PiP + // stacks + for (int i = 0; i < tasks.size(); i++) { + final ActivityManager.RunningTaskInfo task = tasks.get(i); + final WindowConfiguration winConfig = task.configuration.windowConfiguration; + if (winConfig.getActivityType() == ACTIVITY_TYPE_RECENTS) { + continue; + } + if (winConfig.getWindowingMode() == WINDOWING_MODE_PINNED) { + continue; } + return task; } return null; } @@ -517,12 +528,17 @@ public class SystemServicesProxy { ActivityManager.StackInfo fullscreenStackInfo = null; ActivityManager.StackInfo recentsStackInfo = null; for (int i = 0; i < stackInfos.size(); i++) { - StackInfo stackInfo = stackInfos.get(i); - if (stackInfo.stackId == HOME_STACK_ID) { + final StackInfo stackInfo = stackInfos.get(i); + final WindowConfiguration winConfig = stackInfo.configuration.windowConfiguration; + final int activityType = winConfig.getActivityType(); + final int windowingMode = winConfig.getWindowingMode(); + if (activityType == ACTIVITY_TYPE_HOME) { homeStackInfo = stackInfo; - } else if (stackInfo.stackId == FULLSCREEN_WORKSPACE_STACK_ID) { + } else if (activityType == ACTIVITY_TYPE_STANDARD + && (windowingMode == WINDOWING_MODE_FULLSCREEN + || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)) { fullscreenStackInfo = stackInfo; - } else if (stackInfo.stackId == RECENTS_STACK_ID) { + } else if (activityType == ACTIVITY_TYPE_RECENTS) { recentsStackInfo = stackInfo; } } @@ -576,7 +592,7 @@ public class SystemServicesProxy { try { final ActivityOptions options = ActivityOptions.makeBasic(); options.setDockCreateMode(createMode); - options.setLaunchStackId(DOCKED_STACK_ID); + options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); mIam.startActivityFromRecents(taskId, options.toBundle()); return true; } catch (Exception e) { @@ -601,34 +617,6 @@ public class SystemServicesProxy { } /** - * Returns whether the given stack id is the home stack id. - */ - public static boolean isHomeStack(int stackId) { - return stackId == HOME_STACK_ID; - } - - /** - * Returns whether the given stack id is the pinned stack id. - */ - public static boolean isPinnedStack(int stackId){ - return stackId == PINNED_STACK_ID; - } - - /** - * Returns whether the given stack id is the docked stack id. - */ - public static boolean isDockedStack(int stackId) { - return stackId == DOCKED_STACK_ID; - } - - /** - * Returns whether the given stack id is the freeform workspace stack id. - */ - public static boolean isFreeformStack(int stackId) { - return stackId == FREEFORM_WORKSPACE_STACK_ID; - } - - /** * @return whether there are any docked tasks for the current user. */ public boolean hasDockedTask() { @@ -636,7 +624,8 @@ public class SystemServicesProxy { ActivityManager.StackInfo stackInfo = null; try { - stackInfo = mIam.getStackInfo(DOCKED_STACK_ID); + stackInfo = + mIam.getStackInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED); } catch (RemoteException e) { e.printStackTrace(); } @@ -737,14 +726,12 @@ public class SystemServicesProxy { } } - /** - * Moves a task into another stack. - */ - public void moveTaskToStack(int taskId, int stackId) { + /** Set the task's windowing mode. */ + public void setTaskWindowingMode(int taskId, int windowingMode) { if (mIam == null) return; try { - mIam.positionTaskInStack(taskId, stackId, 0); + mIam.setTaskWindowingMode(taskId, windowingMode, false /* onTop */); } catch (RemoteException | IllegalArgumentException e) { e.printStackTrace(); } @@ -1101,9 +1088,10 @@ public class SystemServicesProxy { try { // Use the recents stack bounds, fallback to fullscreen stack if it is null - ActivityManager.StackInfo stackInfo = mIam.getStackInfo(RECENTS_STACK_ID); + ActivityManager.StackInfo stackInfo = + mIam.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS); if (stackInfo == null) { - stackInfo = mIam.getStackInfo(FULLSCREEN_WORKSPACE_STACK_ID); + stackInfo = mIam.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); } if (stackInfo != null) { windowRect.set(stackInfo.bounds); @@ -1120,25 +1108,34 @@ public class SystemServicesProxy { opts != null ? opts.toBundle() : null, UserHandle.CURRENT)); } + public void startActivityFromRecents(Context context, Task.TaskKey taskKey, String taskName, + ActivityOptions options, + @Nullable final StartActivityFromRecentsResultListener resultListener) { + startActivityFromRecents(context, taskKey, taskName, options, + WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED, resultListener); + } + /** Starts an activity from recents. */ public void startActivityFromRecents(Context context, Task.TaskKey taskKey, String taskName, - ActivityOptions options, int stackId, + ActivityOptions options, int windowingMode, int activityType, @Nullable final StartActivityFromRecentsResultListener resultListener) { if (mIam == null) { return; } - if (taskKey.stackId == DOCKED_STACK_ID) { + if (taskKey.windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { // We show non-visible docked tasks in Recents, but we always want to launch // them in the fullscreen stack. if (options == null) { options = ActivityOptions.makeBasic(); } - options.setLaunchStackId(FULLSCREEN_WORKSPACE_STACK_ID); - } else if (stackId != INVALID_STACK_ID) { + options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + } else if (windowingMode != WINDOWING_MODE_UNDEFINED + || activityType != ACTIVITY_TYPE_UNDEFINED) { if (options == null) { options = ActivityOptions.makeBasic(); } - options.setLaunchStackId(stackId); + options.setLaunchWindowingMode(windowingMode); + options.setLaunchActivityType(activityType); } final ActivityOptions finalOptions = options; @@ -1307,6 +1304,20 @@ public class SystemServicesProxy { void onStartActivityResult(boolean succeeded); } + private class PinnedActivityInfo { + final String mPackageName; + final int mUserId; + final int mTaskId; + final int mStackId; + + PinnedActivityInfo(String packageName, int userId, int taskId, int stackId) { + mPackageName = packageName; + mUserId = userId; + mTaskId = taskId; + mStackId = stackId; + } + } + private final class H extends Handler { private static final int ON_TASK_STACK_CHANGED = 1; private static final int ON_TASK_SNAPSHOT_CHANGED = 2; @@ -1342,8 +1353,10 @@ public class SystemServicesProxy { break; } case ON_ACTIVITY_PINNED: { + final PinnedActivityInfo info = (PinnedActivityInfo) msg.obj; for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { - mTaskStackListeners.get(i).onActivityPinned((String) msg.obj, msg.arg1); + mTaskStackListeners.get(i).onActivityPinned( + info.mPackageName, info.mUserId, info.mTaskId, info.mStackId); } break; } diff --git a/com/android/systemui/recents/model/HighResThumbnailLoader.java b/com/android/systemui/recents/model/HighResThumbnailLoader.java index 48fa6c3c..6414ea1e 100644 --- a/com/android/systemui/recents/model/HighResThumbnailLoader.java +++ b/com/android/systemui/recents/model/HighResThumbnailLoader.java @@ -187,7 +187,7 @@ public class HighResThumbnailLoader implements TaskCallbacks { } @Override - public void onTaskStackIdChanged() { + public void onTaskWindowingModeChanged() { } private final Runnable mLoader = new Runnable() { diff --git a/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/com/android/systemui/recents/model/RecentsTaskLoadPlan.java index 8d31730d..d5e03135 100644 --- a/com/android/systemui/recents/model/RecentsTaskLoadPlan.java +++ b/com/android/systemui/recents/model/RecentsTaskLoadPlan.java @@ -16,6 +16,8 @@ package com.android.systemui.recents.model; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; + import android.app.ActivityManager; import android.content.Context; import android.content.pm.ActivityInfo; @@ -155,12 +157,13 @@ public class RecentsTaskLoadPlan { ActivityManager.RecentTaskInfo t = mRawTasks.get(i); // Compose the task key - Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.stackId, t.baseIntent, + final int windowingMode = t.configuration.windowConfiguration.getWindowingMode(); + Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, windowingMode, t.baseIntent, t.userId, t.firstActiveTime, t.lastActiveTime); // This task is only shown in the stack if it satisfies the historical time or min // number of tasks constraints. Freeform tasks are also always shown. - boolean isFreeformTask = SystemServicesProxy.isFreeformStack(t.stackId); + boolean isFreeformTask = windowingMode == WINDOWING_MODE_FREEFORM; boolean isStackTask; if (Recents.getConfiguration().isGridEnabled) { // When grid layout is enabled, we only show the first diff --git a/com/android/systemui/recents/model/Task.java b/com/android/systemui/recents/model/Task.java index 9e6bf854..abdb5cb8 100644 --- a/com/android/systemui/recents/model/Task.java +++ b/com/android/systemui/recents/model/Task.java @@ -16,6 +16,8 @@ package com.android.systemui.recents.model; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; + import android.app.ActivityManager; import android.content.ComponentName; import android.content.Intent; @@ -46,8 +48,8 @@ public class Task { public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData); /* Notifies when a task has been unbound */ public void onTaskDataUnloaded(); - /* Notifies when a task's stack id has changed. */ - public void onTaskStackIdChanged(); + /* Notifies when a task's windowing mode has changed. */ + public void onTaskWindowingModeChanged(); } /* The Task Key represents the unique primary key for the task */ @@ -55,7 +57,7 @@ public class Task { @ViewDebug.ExportedProperty(category="recents") public final int id; @ViewDebug.ExportedProperty(category="recents") - public int stackId; + public int windowingMode; @ViewDebug.ExportedProperty(category="recents") public final Intent baseIntent; @ViewDebug.ExportedProperty(category="recents") @@ -67,10 +69,10 @@ public class Task { private int mHashCode; - public TaskKey(int id, int stackId, Intent intent, int userId, long firstActiveTime, + public TaskKey(int id, int windowingMode, Intent intent, int userId, long firstActiveTime, long lastActiveTime) { this.id = id; - this.stackId = stackId; + this.windowingMode = windowingMode; this.baseIntent = intent; this.userId = userId; this.firstActiveTime = firstActiveTime; @@ -78,8 +80,8 @@ public class Task { updateHashCode(); } - public void setStackId(int stackId) { - this.stackId = stackId; + public void setWindowingMode(int windowingMode) { + this.windowingMode = windowingMode; updateHashCode(); } @@ -93,7 +95,9 @@ public class Task { return false; } TaskKey otherKey = (TaskKey) o; - return id == otherKey.id && stackId == otherKey.stackId && userId == otherKey.userId; + return id == otherKey.id + && windowingMode == otherKey.windowingMode + && userId == otherKey.userId; } @Override @@ -103,12 +107,12 @@ public class Task { @Override public String toString() { - return "id=" + id + " stackId=" + stackId + " user=" + userId + " lastActiveTime=" + - lastActiveTime; + return "id=" + id + " windowingMode=" + windowingMode + " user=" + userId + + " lastActiveTime=" + lastActiveTime; } private void updateHashCode() { - mHashCode = Objects.hash(id, stackId, userId); + mHashCode = Objects.hash(id, windowingMode, userId); } } @@ -277,14 +281,12 @@ public class Task { this.group = group; } - /** - * Updates the stack id of this task. - */ - public void setStackId(int stackId) { - key.setStackId(stackId); + /** Updates the task's windowing mode. */ + public void setWindowingMode(int windowingMode) { + key.setWindowingMode(windowingMode); int callbackCount = mCallbacks.size(); for (int i = 0; i < callbackCount; i++) { - mCallbacks.get(i).onTaskStackIdChanged(); + mCallbacks.get(i).onTaskWindowingModeChanged(); } } @@ -293,7 +295,7 @@ public class Task { */ public boolean isFreeformTask() { SystemServicesProxy ssp = Recents.getSystemServices(); - return ssp.hasFreeformWorkspaceSupport() && ssp.isFreeformStack(key.stackId); + return ssp.hasFreeformWorkspaceSupport() && key.windowingMode == WINDOWING_MODE_FREEFORM; } /** Notifies the callback listeners that this task has been loaded */ diff --git a/com/android/systemui/recents/model/TaskKeyCache.java b/com/android/systemui/recents/model/TaskKeyCache.java index be99f930..247a6542 100644 --- a/com/android/systemui/recents/model/TaskKeyCache.java +++ b/com/android/systemui/recents/model/TaskKeyCache.java @@ -45,7 +45,7 @@ public abstract class TaskKeyCache<V> { final V getAndInvalidateIfModified(Task.TaskKey key) { Task.TaskKey lastKey = mKeys.get(key.id); if (lastKey != null) { - if ((lastKey.stackId != key.stackId) || + if ((lastKey.windowingMode != key.windowingMode) || (lastKey.lastActiveTime != key.lastActiveTime)) { // The task has updated (been made active since the last time it was put into the // LRU cache) or the stack id for the task has changed, invalidate that cache item diff --git a/com/android/systemui/recents/model/TaskStack.java b/com/android/systemui/recents/model/TaskStack.java index 6e3be09b..fdae917c 100644 --- a/com/android/systemui/recents/model/TaskStack.java +++ b/com/android/systemui/recents/model/TaskStack.java @@ -18,8 +18,8 @@ package com.android.systemui.recents.model; import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT; import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; -import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.WindowManager.DOCKED_BOTTOM; import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.DOCKED_LEFT; @@ -115,7 +115,7 @@ class FilteredTaskList { /** * Moves the given task. */ - public void moveTaskToStack(Task task, int insertIndex, int newStackId) { + public void setTaskWindowingMode(Task task, int insertIndex, int windowingMode) { int taskIndex = indexOf(task); if (taskIndex != insertIndex) { mTasks.remove(taskIndex); @@ -127,7 +127,7 @@ class FilteredTaskList { // Update the stack id now, after we've moved the task, and before we update the // filtered tasks - task.setStackId(newStackId); + task.setWindowingMode(windowingMode); updateFilteredTasks(); } @@ -590,17 +590,15 @@ public class TaskStack { mCb = cb; } - /** - * Moves the given task to either the front of the freeform workspace or the stack. - */ - public void moveTaskToStack(Task task, int newStackId) { + /** Sets the windowing mode for a given task. */ + public void setTaskWindowingMode(Task task, int windowingMode) { // Find the index to insert into ArrayList<Task> taskList = mStackTaskList.getTasks(); int taskCount = taskList.size(); - if (!task.isFreeformTask() && (newStackId == FREEFORM_WORKSPACE_STACK_ID)) { + if (!task.isFreeformTask() && (windowingMode == WINDOWING_MODE_FREEFORM)) { // Insert freeform tasks at the front - mStackTaskList.moveTaskToStack(task, taskCount, newStackId); - } else if (task.isFreeformTask() && (newStackId == FULLSCREEN_WORKSPACE_STACK_ID)) { + mStackTaskList.setTaskWindowingMode(task, taskCount, windowingMode); + } else if (task.isFreeformTask() && (windowingMode == WINDOWING_MODE_FULLSCREEN)) { // Insert after the first stacked task int insertIndex = 0; for (int i = taskCount - 1; i >= 0; i--) { @@ -609,7 +607,7 @@ public class TaskStack { break; } } - mStackTaskList.moveTaskToStack(task, insertIndex, newStackId); + mStackTaskList.setTaskWindowingMode(task, insertIndex, windowingMode); } } diff --git a/com/android/systemui/recents/views/RecentsTransitionHelper.java b/com/android/systemui/recents/views/RecentsTransitionHelper.java index b2675d7a..ee05d81c 100644 --- a/com/android/systemui/recents/views/RecentsTransitionHelper.java +++ b/com/android/systemui/recents/views/RecentsTransitionHelper.java @@ -16,14 +16,17 @@ package com.android.systemui.recents.views; -import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID; -import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; -import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.INVALID_STACK_ID; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import android.annotation.Nullable; -import android.app.ActivityManager.StackId; import android.app.ActivityOptions; import android.app.ActivityOptions.OnAnimationStartedListener; import android.content.Context; @@ -107,7 +110,7 @@ public class RecentsTransitionHelper { */ public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task, final TaskStackView stackView, final TaskView taskView, - final boolean screenPinningRequested, final int destinationStack) { + final boolean screenPinningRequested, final int windowingMode, final int activityType) { final ActivityOptions.OnAnimationStartedListener animStartedListener; final AppTransitionAnimationSpecsFuture transitionFuture; @@ -116,8 +119,8 @@ public class RecentsTransitionHelper { // Fetch window rect here already in order not to be blocked on lock contention in WM // when the future calls it. final Rect windowRect = Recents.getSystemServices().getWindowRect(); - transitionFuture = getAppTransitionFuture( - () -> composeAnimationSpecs(task, stackView, destinationStack, windowRect)); + transitionFuture = getAppTransitionFuture(() -> composeAnimationSpecs( + task, stackView, windowingMode, activityType, windowRect)); animStartedListener = new OnAnimationStartedListener() { private boolean mHandled; @@ -180,7 +183,8 @@ public class RecentsTransitionHelper { if (taskView == null) { // If there is no task view, then we do not need to worry about animating out occluding // task views, and we can launch immediately - startTaskActivity(stack, task, taskView, opts, transitionFuture, destinationStack); + startTaskActivity(stack, task, taskView, opts, transitionFuture, + windowingMode, activityType); } else { LaunchTaskStartedEvent launchStartedEvent = new LaunchTaskStartedEvent(taskView, screenPinningRequested); @@ -189,13 +193,14 @@ public class RecentsTransitionHelper { @Override public void run() { startTaskActivity(stack, task, taskView, opts, transitionFuture, - destinationStack); + windowingMode, activityType); } }); EventBus.getDefault().send(launchStartedEvent); } else { EventBus.getDefault().send(launchStartedEvent); - startTaskActivity(stack, task, taskView, opts, transitionFuture, destinationStack); + startTaskActivity(stack, task, taskView, opts, transitionFuture, + windowingMode, activityType); } } Recents.getSystemServices().sendCloseSystemWindows( @@ -224,13 +229,13 @@ public class RecentsTransitionHelper { * * @param taskView this is the {@link TaskView} that we are launching from. This can be null if * we are toggling recents and the launch-to task is now offscreen. - * @param destinationStack id of the stack to put the task into. */ private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskView taskView, ActivityOptions opts, AppTransitionAnimationSpecsFuture transitionFuture, - int destinationStack) { + int windowingMode, int activityType) { SystemServicesProxy ssp = Recents.getSystemServices(); - ssp.startActivityFromRecents(mContext, task.key, task.title, opts, destinationStack, + ssp.startActivityFromRecents(mContext, task.key, task.title, opts, windowingMode, + activityType, succeeded -> { if (succeeded) { // Keep track of the index of the task launch @@ -310,11 +315,9 @@ public class RecentsTransitionHelper { * Composes the animation specs for all the tasks in the target stack. */ private List<AppTransitionAnimationSpec> composeAnimationSpecs(final Task task, - final TaskStackView stackView, final int destinationStack, Rect windowRect) { - // Ensure we have a valid target stack id - final int targetStackId = destinationStack != INVALID_STACK_ID ? - destinationStack : task.key.stackId; - if (!StackId.useAnimationSpecForAppTransition(targetStackId)) { + final TaskStackView stackView, int windowingMode, int activityType, Rect windowRect) { + if (activityType == ACTIVITY_TYPE_RECENTS || activityType == ACTIVITY_TYPE_HOME + || windowingMode == WINDOWING_MODE_PINNED) { return null; } @@ -329,9 +332,12 @@ public class RecentsTransitionHelper { List<AppTransitionAnimationSpec> specs = new ArrayList<>(); // TODO: Sometimes targetStackId is not initialized after reboot, so we also have to - // check for INVALID_STACK_ID - if (targetStackId == FULLSCREEN_WORKSPACE_STACK_ID || targetStackId == DOCKED_STACK_ID - || targetStackId == ASSISTANT_STACK_ID || targetStackId == INVALID_STACK_ID) { + // check for INVALID_STACK_ID (now WINDOWING_MODE_UNDEFINED) + if (windowingMode == WINDOWING_MODE_FULLSCREEN + || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY + || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY + || activityType == ACTIVITY_TYPE_ASSISTANT + || windowingMode == WINDOWING_MODE_UNDEFINED) { if (taskView == null) { specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect)); } else { @@ -353,7 +359,7 @@ public class RecentsTransitionHelper { int taskCount = tasks.size(); for (int i = taskCount - 1; i >= 0; i--) { Task t = tasks.get(i); - if (t.isFreeformTask() || targetStackId == FREEFORM_WORKSPACE_STACK_ID) { + if (t.isFreeformTask() || windowingMode == WINDOWING_MODE_FREEFORM) { TaskView tv = stackView.getChildViewForTask(t); if (tv == null) { // TODO: Create a different animation task rect for this case (though it should diff --git a/com/android/systemui/recents/views/RecentsView.java b/com/android/systemui/recents/views/RecentsView.java index c44cd728..c7edb9ae 100644 --- a/com/android/systemui/recents/views/RecentsView.java +++ b/com/android/systemui/recents/views/RecentsView.java @@ -174,8 +174,6 @@ public class RecentsView extends FrameLayout { ? R.layout.recents_low_ram_stack_action_button : R.layout.recents_stack_action_button, this, false); - mStackActionButton.setOnClickListener( - v -> EventBus.getDefault().send(new DismissAllTaskViewsEvent())); mStackButtonShadowRadius = mStackActionButton.getShadowRadius(); mStackButtonShadowDistance = new PointF(mStackActionButton.getShadowDx(), @@ -205,10 +203,6 @@ public class RecentsView extends FrameLayout { mStackButtonShadowDistance.x, mStackButtonShadowDistance.y, mStackButtonShadowColor); } - if (Recents.getConfiguration().isLowRamDevice) { - int bgColor = Utils.getColorAttr(mContext, R.attr.clearAllBackgroundColor); - mStackActionButton.setBackgroundColor(bgColor); - } } // Let's also require dark status and nav bars if the text is dark @@ -338,8 +332,7 @@ public class RecentsView extends FrameLayout { Task task = mTaskStackView.getFocusedTask(); if (task != null) { TaskView taskView = mTaskStackView.getChildViewForTask(task); - EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null, - INVALID_STACK_ID, false)); + EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null, false)); if (logEvent != 0) { MetricsLogger.action(getContext(), logEvent, @@ -363,32 +356,13 @@ public class RecentsView extends FrameLayout { Task task = getStack().getLaunchTarget(); if (task != null) { TaskView taskView = mTaskStackView.getChildViewForTask(task); - EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null, - INVALID_STACK_ID, false)); + EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null, false)); return true; } } return false; } - /** Launches a given task. */ - public boolean launchTask(Task task, Rect taskBounds, int destinationStack) { - if (mTaskStackView != null) { - // Iterate the stack views and try and find the given task. - List<TaskView> taskViews = mTaskStackView.getTaskViews(); - int taskViewCount = taskViews.size(); - for (int j = 0; j < taskViewCount; j++) { - TaskView tv = taskViews.get(j); - if (tv.getTask() == task) { - EventBus.getDefault().send(new LaunchTaskEvent(tv, task, taskBounds, - destinationStack, false)); - return true; - } - } - } - return false; - } - /** * Hides the task stack and shows the empty view. */ @@ -570,9 +544,10 @@ public class RecentsView extends FrameLayout { public final void onBusEvent(LaunchTaskEvent event) { mLastTaskLaunchedWasFreeform = event.task.isFreeformTask(); mTransitionHelper.launchTaskFromRecents(getStack(), event.task, mTaskStackView, - event.taskView, event.screenPinningRequested, event.targetTaskStack); + event.taskView, event.screenPinningRequested, event.targetWindowingMode, + event.targetActivityType); if (Recents.getConfiguration().isLowRamDevice) { - hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, false /* translate */); + EventBus.getDefault().send(new HideStackActionButtonEvent(false /* translate */)); } } @@ -580,7 +555,7 @@ public class RecentsView extends FrameLayout { int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION; if (RecentsDebugFlags.Static.EnableStackActionButton) { // Hide the stack action button - hideStackActionButton(taskViewExitToHomeDuration, false /* translate */); + EventBus.getDefault().send(new HideStackActionButtonEvent()); } animateBackgroundScrim(0f, taskViewExitToHomeDuration); @@ -741,13 +716,10 @@ public class RecentsView extends FrameLayout { animateBackgroundScrim(getOpaqueScrimAlpha(), TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION); } - if (Recents.getConfiguration().isLowRamDevice && mEmptyView.getVisibility() != View.VISIBLE) { - showStackActionButton(SHOW_STACK_ACTION_BUTTON_DURATION, false /* translate */); - } } public final void onBusEvent(AllTaskViewsDismissedEvent event) { - hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */); + EventBus.getDefault().send(new HideStackActionButtonEvent()); } public final void onBusEvent(DismissAllTaskViewsEvent event) { @@ -795,7 +767,8 @@ public class RecentsView extends FrameLayout { mStackActionButton.setVisibility(View.VISIBLE); mStackActionButton.setAlpha(0f); if (translate) { - mStackActionButton.setTranslationY(-mStackActionButton.getMeasuredHeight() * 0.25f); + mStackActionButton.setTranslationY(mStackActionButton.getMeasuredHeight() * + (Recents.getConfiguration().isLowRamDevice ? 1 : -0.25f)); } else { mStackActionButton.setTranslationY(0f); } @@ -841,8 +814,8 @@ public class RecentsView extends FrameLayout { if (mStackActionButton.getVisibility() == View.VISIBLE) { if (translate) { - mStackActionButton.animate() - .translationY(-mStackActionButton.getMeasuredHeight() * 0.25f); + mStackActionButton.animate().translationY(mStackActionButton.getMeasuredHeight() + * (Recents.getConfiguration().isLowRamDevice ? 1 : -0.25f)); } mStackActionButton.animate() .alpha(0f) @@ -954,7 +927,7 @@ public class RecentsView extends FrameLayout { /** * @return the bounds of the stack action button. */ - private Rect getStackActionButtonBoundsFromStackLayout() { + Rect getStackActionButtonBoundsFromStackLayout() { Rect actionButtonRect = new Rect(mTaskStackView.mLayoutAlgorithm.getStackActionButtonRect()); int left, top; if (Recents.getConfiguration().isLowRamDevice) { @@ -976,6 +949,16 @@ public class RecentsView extends FrameLayout { return actionButtonRect; } + View getStackActionButton() { + return mStackActionButton; + } + + @Override + public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { + super.requestDisallowInterceptTouchEvent(disallowIntercept); + mTouchHandler.cancelStackActionButtonClick(); + } + public void dump(String prefix, PrintWriter writer) { String innerPrefix = prefix + " "; String id = Integer.toHexString(System.identityHashCode(this)); diff --git a/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/com/android/systemui/recents/views/RecentsViewTouchHandler.java index 46619c21..b6b24bcd 100644 --- a/com/android/systemui/recents/views/RecentsViewTouchHandler.java +++ b/com/android/systemui/recents/views/RecentsViewTouchHandler.java @@ -23,6 +23,7 @@ import android.graphics.Rect; import android.view.InputDevice; import android.view.MotionEvent; import android.view.PointerIcon; +import android.view.View; import android.view.ViewConfiguration; import android.view.ViewDebug; @@ -31,6 +32,8 @@ import com.android.systemui.recents.Recents; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.ConfigurationChangedEvent; import com.android.systemui.recents.events.activity.HideRecentsEvent; +import com.android.systemui.recents.events.activity.HideStackActionButtonEvent; +import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent; import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent; import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent; import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent; @@ -99,8 +102,7 @@ public class RecentsViewTouchHandler { /** Touch preprocessing for handling below */ public boolean onInterceptTouchEvent(MotionEvent ev) { - handleTouchEvent(ev); - return mDragRequested; + return handleTouchEvent(ev) || mDragRequested; } /** Handles touch events once we have intercepted them */ @@ -183,22 +185,47 @@ public class RecentsViewTouchHandler { } } + void cancelStackActionButtonClick() { + mRv.getStackActionButton().setPressed(false); + } + + private boolean isWithinStackActionButton(float x, float y) { + Rect rect = mRv.getStackActionButtonBoundsFromStackLayout(); + return mRv.getStackActionButton().getVisibility() == View.VISIBLE && + mRv.getStackActionButton().pointInView(x - rect.left, y - rect.top, 0 /* slop */); + } + + private void changeStackActionButtonDrawableHotspot(float x, float y) { + Rect rect = mRv.getStackActionButtonBoundsFromStackLayout(); + mRv.getStackActionButton().drawableHotspotChanged(x - rect.left, y - rect.top); + } + /** * Handles dragging touch events */ - private void handleTouchEvent(MotionEvent ev) { + private boolean handleTouchEvent(MotionEvent ev) { int action = ev.getActionMasked(); + boolean consumed = false; + float evX = ev.getX(); + float evY = ev.getY(); switch (action) { case MotionEvent.ACTION_DOWN: - mDownPos.set((int) ev.getX(), (int) ev.getY()); + mDownPos.set((int) evX, (int) evY); mDeviceId = ev.getDeviceId(); + + if (isWithinStackActionButton(evX, evY)) { + changeStackActionButtonDrawableHotspot(evX, evY); + mRv.getStackActionButton().setPressed(true); + } break; case MotionEvent.ACTION_MOVE: { - float evX = ev.getX(); - float evY = ev.getY(); float x = evX - mTaskViewOffset.x; float y = evY - mTaskViewOffset.y; + if (mRv.getStackActionButton().isPressed() && isWithinStackActionButton(evX, evY)) { + changeStackActionButtonDrawableHotspot(evX, evY); + } + if (mDragRequested) { if (!mIsDragging) { mIsDragging = Math.hypot(evX - mDownPos.x, evY - mDownPos.y) > mDragSlop; @@ -232,9 +259,7 @@ public class RecentsViewTouchHandler { EventBus.getDefault().send(new DragDropTargetChangedEvent(mDragTask, currentDropTarget)); } - } - mTaskView.setTranslationX(x); mTaskView.setTranslationY(y); } @@ -242,6 +267,11 @@ public class RecentsViewTouchHandler { } case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { + if (mRv.getStackActionButton().isPressed() && isWithinStackActionButton(evX, evY)) { + EventBus.getDefault().send(new DismissAllTaskViewsEvent()); + consumed = true; + } + cancelStackActionButtonClick(); if (mDragRequested) { boolean cancelled = action == MotionEvent.ACTION_CANCEL; if (cancelled) { @@ -254,5 +284,6 @@ public class RecentsViewTouchHandler { mDeviceId = -1; } } + return consumed; } } diff --git a/com/android/systemui/recents/views/TaskStackView.java b/com/android/systemui/recents/views/TaskStackView.java index 8899e307..3160ee0e 100644 --- a/com/android/systemui/recents/views/TaskStackView.java +++ b/com/android/systemui/recents/views/TaskStackView.java @@ -16,9 +16,8 @@ package com.android.systemui.recents.views; -import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.INVALID_STACK_ID; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -1487,7 +1486,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal Task frontTask = tasks.get(tasks.size() - 1); if (frontTask != null && frontTask.isFreeformTask()) { EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(frontTask), - frontTask, null, INVALID_STACK_ID, false)); + frontTask, null, false)); return true; } } @@ -1768,8 +1767,17 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } // In grid layout, the stack action button always remains visible. - if (mEnterAnimationComplete && !useGridLayout() && - !Recents.getConfiguration().isLowRamDevice) { + if (mEnterAnimationComplete && !useGridLayout()) { + if (Recents.getConfiguration().isLowRamDevice) { + // Show stack button when user drags down to show older tasks on low ram devices + if (mStack.getTaskCount() > 0 && !mStackActionButtonVisible + && mTouchHandler.mIsScrolling && curScroll - prevScroll < 0) { + // Going up + EventBus.getDefault().send( + new ShowStackActionButtonEvent(true /* translate */)); + } + return; + } if (prevScroll > SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD && curScroll <= SHOW_STACK_ACTION_BUTTON_SCROLL_THRESHOLD && mStack.getTaskCount() > 0) { @@ -1956,6 +1964,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Remove the task from the stack mStack.removeTask(event.task, event.animation, false /* fromDockGesture */); EventBus.getDefault().send(new DeleteTaskDataEvent(event.task)); + if (mStack.getTaskCount() > 0 && Recents.getConfiguration().isLowRamDevice) { + EventBus.getDefault().send(new ShowStackActionButtonEvent(false /* translate */)); + } MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_DISMISS, event.task.key.getComponent().toString()); @@ -2082,18 +2093,18 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } boolean isFreeformTask = event.task.isFreeformTask(); - boolean hasChangedStacks = + boolean hasChangedWindowingMode = (!isFreeformTask && event.dropTarget == mFreeformWorkspaceDropTarget) || (isFreeformTask && event.dropTarget == mStackDropTarget); - if (hasChangedStacks) { + if (hasChangedWindowingMode) { // Move the task to the right position in the stack (ie. the front of the stack if // freeform or the front of the stack if fullscreen). Note, we MUST move the tasks // before we update their stack ids, otherwise, the keys will have changed. if (event.dropTarget == mFreeformWorkspaceDropTarget) { - mStack.moveTaskToStack(event.task, FREEFORM_WORKSPACE_STACK_ID); + mStack.setTaskWindowingMode(event.task, WINDOWING_MODE_FREEFORM); } else if (event.dropTarget == mStackDropTarget) { - mStack.moveTaskToStack(event.task, FULLSCREEN_WORKSPACE_STACK_ID); + mStack.setTaskWindowingMode(event.task, WINDOWING_MODE_FULLSCREEN); } updateLayoutAlgorithm(true /* boundScroll */); @@ -2102,7 +2113,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal @Override public void run() { SystemServicesProxy ssp = Recents.getSystemServices(); - ssp.moveTaskToStack(event.task.key.id, event.task.key.stackId); + ssp.setTaskWindowingMode(event.task.key.id, event.task.key.windowingMode); } }); } @@ -2369,12 +2380,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal public void run() { EventBus.getDefault().send(new LaunchTaskEvent( getChildViewForTask(task), task, null, - INVALID_STACK_ID, false /* screenPinningRequested */)); + false /* screenPinningRequested */)); } }); } else { - EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(task), - task, null, INVALID_STACK_ID, false /* screenPinningRequested */)); + EventBus.getDefault().send(new LaunchTaskEvent(getChildViewForTask(task), task, null, + false /* screenPinningRequested */)); } } diff --git a/com/android/systemui/recents/views/TaskView.java b/com/android/systemui/recents/views/TaskView.java index ceeebd96..9d639647 100644 --- a/com/android/systemui/recents/views/TaskView.java +++ b/com/android/systemui/recents/views/TaskView.java @@ -647,7 +647,7 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks } @Override - public void onTaskStackIdChanged() { + public void onTaskWindowingModeChanged() { // Force rebind the header, the thumbnail does not change due to stack changes mHeaderView.bindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode); mHeaderView.onTaskDataLoaded(); @@ -674,8 +674,7 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks mActionButtonView.setTranslationZ(0f); screenPinningRequested = true; } - EventBus.getDefault().send(new LaunchTaskEvent(this, mTask, null, INVALID_STACK_ID, - screenPinningRequested)); + EventBus.getDefault().send(new LaunchTaskEvent(this, mTask, null, screenPinningRequested)); MetricsLogger.action(v.getContext(), MetricsEvent.ACTION_OVERVIEW_SELECT, mTask.key.getComponent().toString()); diff --git a/com/android/systemui/recents/views/TaskViewHeader.java b/com/android/systemui/recents/views/TaskViewHeader.java index ae922fcc..198ecae2 100644 --- a/com/android/systemui/recents/views/TaskViewHeader.java +++ b/com/android/systemui/recents/views/TaskViewHeader.java @@ -16,6 +16,11 @@ package com.android.systemui.recents.views; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.Nullable; @@ -57,10 +62,6 @@ import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.recents.misc.Utilities; import com.android.systemui.recents.model.Task; -import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; -import static android.app.ActivityManager.StackId.INVALID_STACK_ID; - /* The task bar view */ public class TaskViewHeader extends FrameLayout implements View.OnClickListener, View.OnLongClickListener { @@ -172,7 +173,7 @@ public class TaskViewHeader extends FrameLayout int mTaskBarViewLightTextColor; int mTaskBarViewDarkTextColor; int mDisabledTaskBarBackgroundColor; - int mMoveTaskTargetStackId = INVALID_STACK_ID; + int mTaskWindowingMode = WINDOWING_MODE_UNDEFINED; // Header background private HighlightColorDrawable mBackground; @@ -485,12 +486,12 @@ public class TaskViewHeader extends FrameLayout // current task if (mMoveTaskButton != null) { if (t.isFreeformTask()) { - mMoveTaskTargetStackId = FULLSCREEN_WORKSPACE_STACK_ID; + mTaskWindowingMode = WINDOWING_MODE_FULLSCREEN; mMoveTaskButton.setImageDrawable(t.useLightOnPrimaryColor ? mLightFullscreenIcon : mDarkFullscreenIcon); } else { - mMoveTaskTargetStackId = FREEFORM_WORKSPACE_STACK_ID; + mTaskWindowingMode = WINDOWING_MODE_FREEFORM; mMoveTaskButton.setImageDrawable(t.useLightOnPrimaryColor ? mLightFreeformIcon : mDarkFreeformIcon); @@ -621,8 +622,8 @@ public class TaskViewHeader extends FrameLayout Constants.Metrics.DismissSourceHeaderButton); } else if (v == mMoveTaskButton) { TaskView tv = Utilities.findParent(this, TaskView.class); - EventBus.getDefault().send(new LaunchTaskEvent(tv, mTask, null, - mMoveTaskTargetStackId, false)); + EventBus.getDefault().send(new LaunchTaskEvent(tv, mTask, null, false, + mTaskWindowingMode, ACTIVITY_TYPE_UNDEFINED)); } else if (v == mAppInfoView) { EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask)); } else if (v == mAppIconView) { diff --git a/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java b/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java index bcf4f17d..2d7cfb1a 100644 --- a/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java +++ b/com/android/systemui/recents/views/grid/GridTaskViewThumbnail.java @@ -26,8 +26,8 @@ import com.android.systemui.recents.views.TaskViewThumbnail; public class GridTaskViewThumbnail extends TaskViewThumbnail { - private Path mThumbnailOutline; - private Path mRestBackgroundOutline; + private final Path mThumbnailOutline = new Path(); + private final Path mRestBackgroundOutline = new Path(); // True if either this view's size or thumbnail scale has changed and mThumbnailOutline should // be updated. private boolean mUpdateThumbnailOutline = true; @@ -77,47 +77,38 @@ public class GridTaskViewThumbnail extends TaskViewThumbnail { (int) (mThumbnailRect.width() * mThumbnailScale)); final int thumbnailHeight = Math.min(viewHeight, (int) (mThumbnailRect.height() * mThumbnailScale)); - // Draw the thumbnail, we only round the bottom corners: - // - // outerLeft outerRight - // <-----------------------> mRestBackgroundOutline - // _________________________ (thumbnailWidth < viewWidth) - // |_______________________| outerTop A ____ B - // | | ↑ | | - // | | | | | - // | | | | | - // | | | | | C - // \_______________________/ ↓ |__/ - // mCornerRadius outerBottom E D - // - // mRestBackgroundOutline (thumbnailHeight < viewHeight) - // A _________________________ B - // | | C - // F \_______________________/ - // E D - final int outerLeft = 0; - final int outerTop = 0; - final int outerRight = outerLeft + thumbnailWidth; - final int outerBottom = outerTop + thumbnailHeight; - mThumbnailOutline = new Path(); - mThumbnailOutline.moveTo(outerLeft, outerTop); - mThumbnailOutline.lineTo(outerRight, outerTop); - mThumbnailOutline.lineTo(outerRight, outerBottom - mCornerRadius); - mThumbnailOutline.arcTo(outerRight - 2 * mCornerRadius, outerBottom - 2 * mCornerRadius, - outerRight, outerBottom, 0, 90, false); - mThumbnailOutline.lineTo(outerLeft + mCornerRadius, outerBottom); - mThumbnailOutline.arcTo(outerLeft, outerBottom - 2 * mCornerRadius, - outerLeft + 2 * mCornerRadius, outerBottom, 90, 90, false); - mThumbnailOutline.lineTo(outerLeft, outerTop); - mThumbnailOutline.close(); if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) { + // Draw the thumbnail, we only round the bottom corners: + // + // outerLeft outerRight + // <-----------------------> mRestBackgroundOutline + // _________________________ (thumbnailWidth < viewWidth) + // |_______________________| outerTop A ____ B + // | | ↑ | | + // | | | | | + // | | | | | + // | | | | | C + // \_______________________/ ↓ |__/ + // mCornerRadius outerBottom E D + // + // mRestBackgroundOutline (thumbnailHeight < viewHeight) + // A _________________________ B + // | | C + // F \_______________________/ + // E D + final int outerLeft = 0; + final int outerTop = 0; + final int outerRight = outerLeft + thumbnailWidth; + final int outerBottom = outerTop + thumbnailHeight; + createThumbnailPath(outerLeft, outerTop, outerRight, outerBottom, mThumbnailOutline); + if (thumbnailWidth < viewWidth) { final int l = Math.max(0, outerRight - mCornerRadius); final int r = outerRight; final int t = outerTop; final int b = outerBottom; - mRestBackgroundOutline = new Path(); + mRestBackgroundOutline.reset(); mRestBackgroundOutline.moveTo(l, t); // A mRestBackgroundOutline.lineTo(r, t); // B mRestBackgroundOutline.lineTo(r, b - mCornerRadius); // C @@ -133,7 +124,7 @@ public class GridTaskViewThumbnail extends TaskViewThumbnail { final int r = outerRight; final int t = Math.max(0, thumbnailHeight - mCornerRadius); final int b = outerBottom; - mRestBackgroundOutline = new Path(); + mRestBackgroundOutline.reset(); mRestBackgroundOutline.moveTo(l, t); // A mRestBackgroundOutline.lineTo(r, t); // B mRestBackgroundOutline.lineTo(r, b - mCornerRadius); // C @@ -145,9 +136,26 @@ public class GridTaskViewThumbnail extends TaskViewThumbnail { mRestBackgroundOutline.lineTo(l, t); // A mRestBackgroundOutline.close(); } + } else { + createThumbnailPath(0, 0, viewWidth, viewHeight, mThumbnailOutline); } } + private void createThumbnailPath(int outerLeft, int outerTop, int outerRight, int outerBottom, + Path outPath) { + outPath.reset(); + outPath.moveTo(outerLeft, outerTop); + outPath.lineTo(outerRight, outerTop); + outPath.lineTo(outerRight, outerBottom - mCornerRadius); + outPath.arcTo(outerRight - 2 * mCornerRadius, outerBottom - 2 * mCornerRadius, outerRight, + outerBottom, 0, 90, false); + outPath.lineTo(outerLeft + mCornerRadius, outerBottom); + outPath.arcTo(outerLeft, outerBottom - 2 * mCornerRadius, outerLeft + 2 * mCornerRadius, + outerBottom, 90, 90, false); + outPath.lineTo(outerLeft, outerTop); + outPath.close(); + } + @Override protected void onDraw(Canvas canvas) { final int titleHeight = getResources().getDimensionPixelSize( diff --git a/com/android/systemui/stackdivider/DividerView.java b/com/android/systemui/stackdivider/DividerView.java index 6bfef20c..7bcef574 100644 --- a/com/android/systemui/stackdivider/DividerView.java +++ b/com/android/systemui/stackdivider/DividerView.java @@ -16,6 +16,9 @@ package com.android.systemui.stackdivider; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW; import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW; @@ -23,7 +26,6 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.Nullable; -import android.app.ActivityManager.StackId; import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; @@ -36,8 +38,6 @@ import android.util.AttributeSet; import android.view.Choreographer; import android.view.Display; import android.view.DisplayInfo; -import android.view.GestureDetector; -import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; import android.view.PointerIcon; import android.view.VelocityTracker; @@ -62,8 +62,8 @@ import com.android.internal.policy.DockedDividerUtils; import com.android.internal.view.SurfaceFlingerVsyncChoreographer; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.recents.Recents; import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent; import com.android.systemui.recents.events.activity.DockedTopTaskEvent; import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent; import com.android.systemui.recents.events.activity.UndockingTaskEvent; @@ -93,7 +93,6 @@ public class DividerView extends FrameLayout implements OnTouchListener, private static final int LOG_VALUE_UNDOCK_MAX_OTHER = 1; private static final int TASK_POSITION_SAME = Integer.MAX_VALUE; - private static final boolean SWAPPING_ENABLED = false; /** * How much the background gets scaled when we are in the minimized dock state. @@ -153,7 +152,6 @@ public class DividerView extends FrameLayout implements OnTouchListener, private boolean mEntranceAnimationRunning; private boolean mExitAnimationRunning; private int mExitStartPosition; - private GestureDetector mGestureDetector; private boolean mDockedStackMinimized; private boolean mHomeStackResizable; private boolean mAdjustedForIme; @@ -295,21 +293,6 @@ public class DividerView extends FrameLayout implements OnTouchListener, landscape ? TYPE_HORIZONTAL_DOUBLE_ARROW : TYPE_VERTICAL_DOUBLE_ARROW)); getViewTreeObserver().addOnComputeInternalInsetsListener(this); mHandle.setAccessibilityDelegate(mHandleDelegate); - mGestureDetector = new GestureDetector(mContext, new SimpleOnGestureListener() { - @Override - public boolean onSingleTapUp(MotionEvent e) { - if (SWAPPING_ENABLED) { - updateDockSide(); - SystemServicesProxy ssp = Recents.getSystemServices(); - if (mDockSide != WindowManager.DOCKED_INVALID - && !ssp.isRecentsActivityVisible()) { - mWindowManagerProxy.swapTasks(); - return true; - } - } - return false; - } - }); } @Override @@ -478,7 +461,6 @@ public class DividerView extends FrameLayout implements OnTouchListener, @Override public boolean onTouch(View v, MotionEvent event) { convertToScreenCoordinates(event); - mGestureDetector.onTouchEvent(event); final int action = event.getAction() & MotionEvent.ACTION_MASK; switch (action) { case MotionEvent.ACTION_DOWN: @@ -679,7 +661,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, } else { mWindowManagerProxy.maximizeDockedStack(); } - mWindowManagerProxy.setResizeDimLayer(false, -1, 0f); + mWindowManagerProxy.setResizeDimLayer(false, WINDOWING_MODE_UNDEFINED, 0f); } private void liftBackground() { @@ -1015,8 +997,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, SnapTarget closestDismissTarget = getSnapAlgorithm().getClosestDismissTarget(position); float dimFraction = getDimFraction(position, closestDismissTarget); mWindowManagerProxy.setResizeDimLayer(dimFraction != 0f, - getStackIdForDismissTarget(closestDismissTarget), - dimFraction); + getWindowingModeForDismissTarget(closestDismissTarget), dimFraction); } private void applyExitAnimationParallax(Rect taskRect, int position) { @@ -1150,13 +1131,13 @@ public class DividerView extends FrameLayout implements OnTouchListener, } } - private int getStackIdForDismissTarget(SnapTarget dismissTarget) { + private int getWindowingModeForDismissTarget(SnapTarget dismissTarget) { if ((dismissTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(mDockSide)) || (dismissTarget.flag == SnapTarget.FLAG_DISMISS_END && dockSideBottomRight(mDockSide))) { - return StackId.DOCKED_STACK_ID; + return WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; } else { - return StackId.RECENTS_STACK_ID; + return WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; } } @@ -1210,6 +1191,10 @@ public class DividerView extends FrameLayout implements OnTouchListener, } } + public final void onBusEvent(DockedFirstAnimationFrameEvent event) { + saveSnapTargetBeforeMinimized(mSnapAlgorithm.getMiddleTarget()); + } + public final void onBusEvent(DockedTopTaskEvent event) { if (event.dragMode == NavigationBarGestureHelper.DRAG_MODE_NONE) { mState.growAfterRecentsDrawn = false; diff --git a/com/android/systemui/stackdivider/WindowManagerProxy.java b/com/android/systemui/stackdivider/WindowManagerProxy.java index c2451264..85a60624 100644 --- a/com/android/systemui/stackdivider/WindowManagerProxy.java +++ b/com/android/systemui/stackdivider/WindowManagerProxy.java @@ -16,7 +16,6 @@ package com.android.systemui.stackdivider; -import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.view.WindowManager.DOCKED_INVALID; import android.app.ActivityManager; @@ -56,7 +55,7 @@ public class WindowManagerProxy { private final Rect mTouchableRegion = new Rect(); private boolean mDimLayerVisible; - private int mDimLayerTargetStack; + private int mDimLayerTargetWindowingMode; private float mDimLayerAlpha; private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); @@ -88,8 +87,7 @@ public class WindowManagerProxy { @Override public void run() { try { - ActivityManager.getService().moveTasksToFullscreenStack( - DOCKED_STACK_ID, false /* onTop */); + ActivityManager.getService().dismissSplitScreenMode(false /* onTop */); } catch (RemoteException e) { Log.w(TAG, "Failed to remove stack: " + e); } @@ -100,8 +98,7 @@ public class WindowManagerProxy { @Override public void run() { try { - ActivityManager.getService().resizeStack( - DOCKED_STACK_ID, null, true, true, false, -1); + ActivityManager.getService().dismissSplitScreenMode(true /* onTop */); } catch (RemoteException e) { Log.w(TAG, "Failed to resize stack: " + e); } @@ -113,18 +110,7 @@ public class WindowManagerProxy { public void run() { try { WindowManagerGlobal.getWindowManagerService().setResizeDimLayer(mDimLayerVisible, - mDimLayerTargetStack, mDimLayerAlpha); - } catch (RemoteException e) { - Log.w(TAG, "Failed to resize stack: " + e); - } - } - }; - - private final Runnable mSwapRunnable = new Runnable() { - @Override - public void run() { - try { - ActivityManager.getService().swapDockedAndFullscreenStack(); + mDimLayerTargetWindowingMode, mDimLayerAlpha); } catch (RemoteException e) { Log.w(TAG, "Failed to resize stack: " + e); } @@ -211,17 +197,13 @@ public class WindowManagerProxy { return DOCKED_INVALID; } - public void setResizeDimLayer(boolean visible, int targetStackId, float alpha) { + public void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) { mDimLayerVisible = visible; - mDimLayerTargetStack = targetStackId; + mDimLayerTargetWindowingMode = targetWindowingMode; mDimLayerAlpha = alpha; mExecutor.execute(mDimLayerRunnable); } - public void swapTasks() { - mExecutor.execute(mSwapRunnable); - } - public void setTouchRegion(Rect region) { synchronized (mDockedRect) { mTouchableRegion.set(region); diff --git a/com/android/systemui/statusbar/ExpandableNotificationRow.java b/com/android/systemui/statusbar/ExpandableNotificationRow.java index 7fe7f399..966e7899 100644 --- a/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -64,10 +64,10 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; import com.android.systemui.statusbar.NotificationGuts.GutsContent; import com.android.systemui.statusbar.notification.AboveShelfChangedListener; +import com.android.systemui.statusbar.notification.AboveShelfObserver; import com.android.systemui.statusbar.notification.HybridNotificationView; import com.android.systemui.statusbar.notification.NotificationInflater; import com.android.systemui.statusbar.notification.NotificationUtils; -import com.android.systemui.statusbar.notification.NotificationViewWrapper; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.StatusBar; @@ -436,9 +436,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } else { minHeight = mNotificationMinHeight; } - NotificationViewWrapper collapsedWrapper = layout.getVisibleWrapper( - NotificationContentView.VISIBLE_TYPE_CONTRACTED); - minHeight += collapsedWrapper.getMinHeightIncrease(mUseIncreasedCollapsedHeight); boolean headsUpCustom = layout.getHeadsUpChild() != null && layout.getHeadsUpChild().getId() != com.android.internal.R.id.status_bar_latest_event_content; @@ -450,11 +447,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } else { headsUpheight = mMaxHeadsUpHeight; } - NotificationViewWrapper headsUpWrapper = layout.getVisibleWrapper( - NotificationContentView.VISIBLE_TYPE_HEADSUP); - if (headsUpWrapper != null) { - headsUpheight += headsUpWrapper.getMinHeightIncrease(mUseIncreasedCollapsedHeight); - } layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight, mNotificationAmbientHeight); } @@ -2024,14 +2016,15 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } @Override - public int getMinHeight() { - if (mGuts != null && mGuts.isExposed()) { + public int getMinHeight(boolean ignoreTemporaryStates) { + if (!ignoreTemporaryStates && mGuts != null && mGuts.isExposed()) { return mGuts.getIntrinsicHeight(); - } else if (isHeadsUpAllowed() && mIsHeadsUp && mHeadsUpManager.isTrackingHeadsUp()) { + } else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp + && mHeadsUpManager.isTrackingHeadsUp()) { return getPinnedHeadsUpHeight(false /* atLeastMinHeight */); } else if (mIsSummaryWithChildren && !isGroupExpanded() && !mShowingPublic) { return mChildrenContainer.getMinHeight(); - } else if (isHeadsUpAllowed() && mIsHeadsUp) { + } else if (!ignoreTemporaryStates && isHeadsUpAllowed() && mIsHeadsUp) { return mHeadsUpHeight; } NotificationContentView showingLayout = getShowingLayout(); diff --git a/com/android/systemui/statusbar/ExpandableView.java b/com/android/systemui/statusbar/ExpandableView.java index efe5e0c2..aac9af8a 100644 --- a/com/android/systemui/statusbar/ExpandableView.java +++ b/com/android/systemui/statusbar/ExpandableView.java @@ -151,9 +151,21 @@ public abstract class ExpandableView extends FrameLayout { } /** - * @return The minimum content height of this notification. + * @return The minimum content height of this notification. This also respects the temporary + * states of the view. */ public int getMinHeight() { + return getMinHeight(false /* ignoreTemporaryStates */); + } + + /** + * Get the minimum height of this view. + * + * @param ignoreTemporaryStates should temporary states be ignored like the guts or heads-up. + * + * @return The minimum height that this view needs. + */ + public int getMinHeight(boolean ignoreTemporaryStates) { return getHeight(); } diff --git a/com/android/systemui/statusbar/KeyboardShortcuts.java b/com/android/systemui/statusbar/KeyboardShortcuts.java index d370a633..2d16d220 100644 --- a/com/android/systemui/statusbar/KeyboardShortcuts.java +++ b/com/android/systemui/statusbar/KeyboardShortcuts.java @@ -433,24 +433,27 @@ public final class KeyboardShortcuts { // Assist. final AssistUtils assistUtils = new AssistUtils(mContext); final ComponentName assistComponent = assistUtils.getAssistComponentForUser(userId); - PackageInfo assistPackageInfo = null; - try { - assistPackageInfo = mPackageManager.getPackageInfo( - assistComponent.getPackageName(), 0, userId); - } catch (RemoteException e) { - Log.e(TAG, "PackageManagerService is dead"); - } + // Not all devices have an assist component. + if (assistComponent != null) { + PackageInfo assistPackageInfo = null; + try { + assistPackageInfo = mPackageManager.getPackageInfo( + assistComponent.getPackageName(), 0, userId); + } catch (RemoteException e) { + Log.e(TAG, "PackageManagerService is dead"); + } - if (assistPackageInfo != null) { - final Icon assistIcon = Icon.createWithResource( - assistPackageInfo.applicationInfo.packageName, - assistPackageInfo.applicationInfo.icon); + if (assistPackageInfo != null) { + final Icon assistIcon = Icon.createWithResource( + assistPackageInfo.applicationInfo.packageName, + assistPackageInfo.applicationInfo.icon); - keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo( - mContext.getString(R.string.keyboard_shortcut_group_applications_assist), - assistIcon, - KeyEvent.KEYCODE_UNKNOWN, - KeyEvent.META_META_ON)); + keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo( + mContext.getString(R.string.keyboard_shortcut_group_applications_assist), + assistIcon, + KeyEvent.KEYCODE_UNKNOWN, + KeyEvent.META_META_ON)); + } } // Browser. diff --git a/com/android/systemui/statusbar/NotificationData.java b/com/android/systemui/statusbar/NotificationData.java index ddc7dd06..d0417b59 100644 --- a/com/android/systemui/statusbar/NotificationData.java +++ b/com/android/systemui/statusbar/NotificationData.java @@ -292,8 +292,8 @@ public class NotificationData { if (mRankingMap != null) { // RankingMap as received from NoMan - mRankingMap.getRanking(a.key, mRankingA); - mRankingMap.getRanking(b.key, mRankingB); + getRanking(a.key, mRankingA); + getRanking(b.key, mRankingB); aImportance = mRankingA.getImportance(); bImportance = mRankingB.getImportance(); aRank = mRankingA.getRank(); @@ -381,7 +381,7 @@ public class NotificationData { public boolean isAmbient(String key) { if (mRankingMap != null) { - mRankingMap.getRanking(key, mTmpRanking); + getRanking(key, mTmpRanking); return mTmpRanking.isAmbient(); } return false; @@ -389,7 +389,7 @@ public class NotificationData { public int getVisibilityOverride(String key) { if (mRankingMap != null) { - mRankingMap.getRanking(key, mTmpRanking); + getRanking(key, mTmpRanking); return mTmpRanking.getVisibilityOverride(); } return Ranking.VISIBILITY_NO_OVERRIDE; @@ -397,7 +397,7 @@ public class NotificationData { public boolean shouldSuppressScreenOff(String key) { if (mRankingMap != null) { - mRankingMap.getRanking(key, mTmpRanking); + getRanking(key, mTmpRanking); return (mTmpRanking.getSuppressedVisualEffects() & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) != 0; } @@ -406,7 +406,7 @@ public class NotificationData { public boolean shouldSuppressScreenOn(String key) { if (mRankingMap != null) { - mRankingMap.getRanking(key, mTmpRanking); + getRanking(key, mTmpRanking); return (mTmpRanking.getSuppressedVisualEffects() & NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON) != 0; } @@ -415,7 +415,7 @@ public class NotificationData { public int getImportance(String key) { if (mRankingMap != null) { - mRankingMap.getRanking(key, mTmpRanking); + getRanking(key, mTmpRanking); return mTmpRanking.getImportance(); } return NotificationManager.IMPORTANCE_UNSPECIFIED; @@ -423,7 +423,7 @@ public class NotificationData { public String getOverrideGroupKey(String key) { if (mRankingMap != null) { - mRankingMap.getRanking(key, mTmpRanking); + getRanking(key, mTmpRanking); return mTmpRanking.getOverrideGroupKey(); } return null; @@ -431,7 +431,7 @@ public class NotificationData { public List<SnoozeCriterion> getSnoozeCriteria(String key) { if (mRankingMap != null) { - mRankingMap.getRanking(key, mTmpRanking); + getRanking(key, mTmpRanking); return mTmpRanking.getSnoozeCriteria(); } return null; @@ -439,7 +439,7 @@ public class NotificationData { public NotificationChannel getChannel(String key) { if (mRankingMap != null) { - mRankingMap.getRanking(key, mTmpRanking); + getRanking(key, mTmpRanking); return mTmpRanking.getChannel(); } return null; @@ -452,6 +452,9 @@ public class NotificationData { final int N = mEntries.size(); for (int i = 0; i < N; i++) { Entry entry = mEntries.valueAt(i); + if (!getRanking(entry.key, mTmpRanking)) { + continue; + } final StatusBarNotification oldSbn = entry.notification.cloneLight(); final String overrideGroupKey = getOverrideGroupKey(entry.key); if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) { @@ -466,6 +469,19 @@ public class NotificationData { filterAndSort(); } + /** + * Get the ranking from the current ranking map. + * + * @param key the key to look up + * @param outRanking the ranking to populate + * + * @return {@code true} if the ranking was properly obtained. + */ + @VisibleForTesting + protected boolean getRanking(String key, Ranking outRanking) { + return mRankingMap.getRanking(key, outRanking); + } + // TODO: This should not be public. Instead the Environment should notify this class when // anything changed, and this class should call back the UI so it updates itself. public void filterAndSort() { @@ -573,7 +589,7 @@ public class NotificationData { } private void dumpEntry(PrintWriter pw, String indent, int i, Entry e) { - mRankingMap.getRanking(e.key, mTmpRanking); + getRanking(e.key, mTmpRanking); pw.print(indent); pw.println(" [" + i + "] key=" + e.key + " icon=" + e.icon); StatusBarNotification n = e.notification; diff --git a/com/android/systemui/statusbar/NotificationSnooze.java b/com/android/systemui/statusbar/NotificationSnooze.java index c45ca540..492ab44d 100644 --- a/com/android/systemui/statusbar/NotificationSnooze.java +++ b/com/android/systemui/statusbar/NotificationSnooze.java @@ -16,8 +16,13 @@ package com.android.systemui.statusbar; */ import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.concurrent.TimeUnit; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; @@ -28,12 +33,15 @@ import android.animation.ObjectAnimator; import android.content.Context; import android.content.res.Resources; import android.graphics.Typeface; +import android.metrics.LogMaker; import android.os.Bundle; +import android.provider.Settings; import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; import android.text.SpannableString; import android.text.style.StyleSpan; import android.util.AttributeSet; +import android.util.KeyValueListParser; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -51,11 +59,23 @@ import com.android.systemui.R; public class NotificationSnooze extends LinearLayout implements NotificationGuts.GutsContent, View.OnClickListener { + private static final String TAG = "NotificationSnooze"; /** * If this changes more number increases, more assistant action resId's should be defined for * accessibility purposes, see {@link #setSnoozeOptions(List)} */ private static final int MAX_ASSISTANT_SUGGESTIONS = 1; + private static final String KEY_DEFAULT_SNOOZE = "default"; + private static final String KEY_OPTIONS = "options_array"; + private static final LogMaker OPTIONS_OPEN_LOG = + new LogMaker(MetricsEvent.NOTIFICATION_SNOOZE_OPTIONS) + .setType(MetricsEvent.TYPE_OPEN); + private static final LogMaker OPTIONS_CLOSE_LOG = + new LogMaker(MetricsEvent.NOTIFICATION_SNOOZE_OPTIONS) + .setType(MetricsEvent.TYPE_CLOSE); + private static final LogMaker UNDO_LOG = + new LogMaker(MetricsEvent.NOTIFICATION_UNDO_SNOOZE) + .setType(MetricsEvent.TYPE_ACTION); private NotificationGuts mGutsContainer; private NotificationSwipeActionHelper mSnoozeListener; private StatusBarNotification mSbn; @@ -72,9 +92,31 @@ public class NotificationSnooze extends LinearLayout private boolean mSnoozing; private boolean mExpanded; private AnimatorSet mExpandAnimation; + private KeyValueListParser mParser; + + private final static int[] sAccessibilityActions = { + R.id.action_snooze_shorter, + R.id.action_snooze_short, + R.id.action_snooze_long, + R.id.action_snooze_longer, + }; + + private MetricsLogger mMetricsLogger = new MetricsLogger(); public NotificationSnooze(Context context, AttributeSet attrs) { super(context, attrs); + mParser = new KeyValueListParser(','); + } + + @VisibleForTesting + SnoozeOption getDefaultOption() + { + return mDefaultOption; + } + + @VisibleForTesting + void setKeyValueListParser(KeyValueListParser parser) { + mParser = parser; } @Override @@ -96,7 +138,13 @@ public class NotificationSnooze extends LinearLayout mSnoozeOptions = getDefaultSnoozeOptions(); createOptionViews(); - setSelected(mDefaultOption); + setSelected(mDefaultOption, false); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + logOptionSelection(MetricsEvent.NOTIFICATION_SNOOZE_CLICKED, mDefaultOption); } @Override @@ -136,7 +184,7 @@ public class NotificationSnooze extends LinearLayout SnoozeOption so = mSnoozeOptions.get(i); if (so.getAccessibilityAction() != null && so.getAccessibilityAction().getId() == action) { - setSelected(so); + setSelected(so, true); return true; } } @@ -172,17 +220,49 @@ public class NotificationSnooze extends LinearLayout mSbn = sbn; } - private ArrayList<SnoozeOption> getDefaultSnoozeOptions() { + @VisibleForTesting + ArrayList<SnoozeOption> getDefaultSnoozeOptions() { + final Resources resources = getContext().getResources(); ArrayList<SnoozeOption> options = new ArrayList<>(); + try { + final String config = Settings.Global.getString(getContext().getContentResolver(), + Settings.Global.NOTIFICATION_SNOOZE_OPTIONS); + mParser.setString(config); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Bad snooze constants"); + } + + final int defaultSnooze = mParser.getInt(KEY_DEFAULT_SNOOZE, + resources.getInteger(R.integer.config_notification_snooze_time_default)); + final int[] snoozeTimes = parseIntArray(KEY_OPTIONS, + resources.getIntArray(R.array.config_notification_snooze_times)); - options.add(createOption(15 /* minutes */, R.id.action_snooze_15_min)); - options.add(createOption(30 /* minutes */, R.id.action_snooze_30_min)); - mDefaultOption = createOption(60 /* minutes */, R.id.action_snooze_1_hour); - options.add(mDefaultOption); - options.add(createOption(60 * 2 /* minutes */, R.id.action_snooze_2_hours)); + for (int i = 0; i < snoozeTimes.length && i < sAccessibilityActions.length; i++) { + int snoozeTime = snoozeTimes[i]; + SnoozeOption option = createOption(snoozeTime, sAccessibilityActions[i]); + if (i == 0 || snoozeTime == defaultSnooze) { + mDefaultOption = option; + } + options.add(option); + } return options; } + @VisibleForTesting + int[] parseIntArray(final String key, final int[] defaultArray) { + final String value = mParser.getString(key, null); + if (value != null) { + try { + return Arrays.stream(value.split(":")).map(String::trim).mapToInt( + Integer::parseInt).toArray(); + } catch (NumberFormatException e) { + return defaultArray; + } + } else { + return defaultArray; + } + } + private SnoozeOption createOption(int minutes, int accessibilityActionId) { Resources res = getResources(); boolean showInHours = minutes >= 60; @@ -268,12 +348,24 @@ public class NotificationSnooze extends LinearLayout mExpandAnimation.start(); } - private void setSelected(SnoozeOption option) { + private void setSelected(SnoozeOption option, boolean userAction) { mSelectedOption = option; mSelectedOptionText.setText(option.getConfirmation()); showSnoozeOptions(false); hideSelectedOption(); sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + if (userAction) { + logOptionSelection(MetricsEvent.NOTIFICATION_SELECT_SNOOZE, option); + } + } + + private void logOptionSelection(int category, SnoozeOption option) { + int index = mSnoozeOptions.indexOf(option); + long duration = TimeUnit.MINUTES.toMillis(option.getMinutesToSnoozeFor()); + mMetricsLogger.write(new LogMaker(category) + .setType(MetricsEvent.TYPE_ACTION) + .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_INDEX, index) + .addTaggedData(MetricsEvent.FIELD_NOTIFICATION_SNOOZE_DURATION_MS, duration)); } @Override @@ -284,13 +376,15 @@ public class NotificationSnooze extends LinearLayout final int id = v.getId(); final SnoozeOption tag = (SnoozeOption) v.getTag(); if (tag != null) { - setSelected(tag); + setSelected(tag, true); } else if (id == R.id.notification_snooze) { // Toggle snooze options showSnoozeOptions(!mExpanded); + mMetricsLogger.write(!mExpanded ? OPTIONS_OPEN_LOG : OPTIONS_CLOSE_LOG); } else { // Undo snooze was selected undoSnooze(v); + mMetricsLogger.write(UNDO_LOG); } } @@ -321,7 +415,7 @@ public class NotificationSnooze extends LinearLayout @Override public View getContentView() { // Reset the view before use - setSelected(mDefaultOption); + setSelected(mDefaultOption, false); return this; } @@ -343,7 +437,7 @@ public class NotificationSnooze extends LinearLayout return true; } else { // The view should actually be closed - setSelected(mSnoozeOptions.get(0)); + setSelected(mSnoozeOptions.get(0), false); return false; // Return false here so that guts handles closing the view } } diff --git a/com/android/systemui/statusbar/SignalClusterView.java b/com/android/systemui/statusbar/SignalClusterView.java index 759d2cf2..a7fb61a1 100644 --- a/com/android/systemui/statusbar/SignalClusterView.java +++ b/com/android/systemui/statusbar/SignalClusterView.java @@ -16,14 +16,15 @@ package com.android.systemui.statusbar; +import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS; +import static android.app.StatusBarManager.DISABLE_NONE; + import android.annotation.DrawableRes; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Resources; import android.graphics.Color; import android.graphics.Rect; -import android.graphics.drawable.Animatable; -import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.Drawable; import android.telephony.SubscriptionInfo; import android.util.ArraySet; @@ -50,14 +51,15 @@ import com.android.systemui.statusbar.policy.NetworkControllerImpl; import com.android.systemui.statusbar.policy.SecurityController; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; +import com.android.systemui.util.Utils.DisableStateTracker; import java.util.ArrayList; import java.util.List; +import java.util.Objects; // Intimately tied to the design of res/layout/signal_cluster_view.xml public class SignalClusterView extends LinearLayout implements NetworkControllerImpl.SignalCallback, - SecurityController.SecurityControllerCallback, Tunable, - DarkReceiver { + SecurityController.SecurityControllerCallback, Tunable, DarkReceiver { static final String TAG = "SignalClusterView"; static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -73,6 +75,7 @@ public class SignalClusterView extends LinearLayout implements NetworkController private boolean mNoSimsVisible = false; private boolean mVpnVisible = false; + private boolean mSimDetected; private int mVpnIconId = 0; private int mLastVpnIconId = -1; private boolean mEthernetVisible = false; @@ -148,6 +151,8 @@ public class SignalClusterView extends LinearLayout implements NetworkController mIconScaleFactor = typedValue.getFloat(); mNetworkController = Dependency.get(NetworkController.class); mSecurityController = Dependency.get(SecurityController.class); + addOnAttachStateChangeListener( + new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS)); updateActivityEnabled(); } @@ -327,8 +332,9 @@ public class SignalClusterView extends LinearLayout implements NetworkController } @Override - public void setNoSims(boolean show) { + public void setNoSims(boolean show, boolean simDetected) { mNoSimsVisible = show && !mBlockMobile; + mSimDetected = simDetected; apply(); } @@ -548,6 +554,23 @@ public class SignalClusterView extends LinearLayout implements NetworkController if (mNoSimsVisible) { mIconLogger.onIconShown(SLOT_MOBILE); mNoSimsCombo.setVisibility(View.VISIBLE); + if (!Objects.equals(mSimDetected, mNoSimsCombo.getTag())) { + mNoSimsCombo.setTag(mSimDetected); + if (mSimDetected) { + SignalDrawable d = new SignalDrawable(mNoSims.getContext()); + d.setDarkIntensity(0); + mNoSims.setImageDrawable(d); + mNoSims.setImageLevel(SignalDrawable.getEmptyState(4)); + + SignalDrawable dark = new SignalDrawable(mNoSims.getContext()); + dark.setDarkIntensity(1); + mNoSimsDark.setImageDrawable(dark); + mNoSimsDark.setImageLevel(SignalDrawable.getEmptyState(4)); + } else { + mNoSims.setImageResource(R.drawable.stat_sys_no_sims); + mNoSimsDark.setImageResource(R.drawable.stat_sys_no_sims); + } + } } else { mIconLogger.onIconHidden(SLOT_MOBILE); mNoSimsCombo.setVisibility(View.GONE); diff --git a/com/android/systemui/statusbar/StatusBarIconView.java b/com/android/systemui/statusbar/StatusBarIconView.java index 2cff79df..6cfd42fa 100644 --- a/com/android/systemui/statusbar/StatusBarIconView.java +++ b/com/android/systemui/statusbar/StatusBarIconView.java @@ -43,6 +43,7 @@ import android.util.FloatProperty; import android.util.Log; import android.util.Property; import android.util.TypedValue; +import android.view.View; import android.view.ViewDebug; import android.view.accessibility.AccessibilityEvent; import android.view.animation.Interpolator; @@ -142,6 +143,7 @@ public class StatusBarIconView extends AnimatedImageView { private float[] mMatrix; private ColorMatrixColorFilter mMatrixColorFilter; private boolean mIsInShelf; + private Runnable mLayoutRunnable; public StatusBarIconView(Context context, String slot, StatusBarNotification sbn) { this(context, slot, sbn, false); @@ -796,6 +798,24 @@ public class StatusBarIconView extends AnimatedImageView { } } + /** + * This method returns the drawing rect for the view which is different from the regular + * drawing rect, since we layout all children at position 0 and usually the translation is + * neglected. The standard implementation doesn't account for translation. + * + * @param outRect The (scrolled) drawing bounds of the view. + */ + @Override + public void getDrawingRect(Rect outRect) { + super.getDrawingRect(outRect); + float translationX = getTranslationX(); + float translationY = getTranslationY(); + outRect.left += translationX; + outRect.right += translationX; + outRect.top += translationY; + outRect.bottom += translationY; + } + public void setIsInShelf(boolean isInShelf) { mIsInShelf = isInShelf; } @@ -804,6 +824,19 @@ public class StatusBarIconView extends AnimatedImageView { return mIsInShelf; } + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + if (mLayoutRunnable != null) { + mLayoutRunnable.run(); + mLayoutRunnable = null; + } + } + + public void executeOnLayout(Runnable runnable) { + mLayoutRunnable = runnable; + } + public interface OnVisibilityChangedListener { void onVisibilityChanged(int newVisibility); } diff --git a/com/android/systemui/statusbar/car/CarNavigationBarController.java b/com/android/systemui/statusbar/car/CarNavigationBarController.java index 7e08d560..f5c77f26 100644 --- a/com/android/systemui/statusbar/car/CarNavigationBarController.java +++ b/com/android/systemui/statusbar/car/CarNavigationBarController.java @@ -15,7 +15,13 @@ */ package com.android.systemui.statusbar.car; -import android.app.ActivityManager.StackId; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; + +import android.app.ActivityManager; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -101,7 +107,7 @@ class CarNavigationBarController { } } - public void taskChanged(String packageName, int stackId) { + public void taskChanged(String packageName, ActivityManager.RunningTaskInfo taskInfo) { // If the package name belongs to a filter, then highlight appropriate button in // the navigation bar. if (mFacetPackageMap.containsKey(packageName)) { @@ -115,9 +121,11 @@ class CarNavigationBarController { } // Set up the persistent docked task if needed. - if (mPersistentTaskIntent != null && !mStatusBar.hasDockedTask() - && stackId != StackId.HOME_STACK_ID) { - mStatusBar.startActivityOnStack(mPersistentTaskIntent, StackId.DOCKED_STACK_ID); + boolean isHomeTask = + taskInfo.configuration.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME; + if (mPersistentTaskIntent != null && !mStatusBar.hasDockedTask() && !isHomeTask) { + mStatusBar.startActivityOnStack(mPersistentTaskIntent, + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED); } } @@ -375,13 +383,15 @@ class CarNavigationBarController { // rather than the "preferred/last run" app. intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, index == mCurrentFacetIndex); - int stackId = StackId.FULLSCREEN_WORKSPACE_STACK_ID; + int windowingMode = WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; + int activityType = ACTIVITY_TYPE_UNDEFINED; if (intent.getCategories().contains(Intent.CATEGORY_HOME)) { - stackId = StackId.HOME_STACK_ID; + windowingMode = WINDOWING_MODE_UNDEFINED; + activityType = ACTIVITY_TYPE_HOME; } setCurrentFacet(index); - mStatusBar.startActivityOnStack(intent, stackId); + mStatusBar.startActivityOnStack(intent, windowingMode, activityType); } /** @@ -391,6 +401,7 @@ class CarNavigationBarController { */ private void onFacetLongClicked(Intent intent, int index) { setCurrentFacet(index); - mStatusBar.startActivityOnStack(intent, StackId.FULLSCREEN_WORKSPACE_STACK_ID); + mStatusBar.startActivityOnStack(intent, + WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED); } } diff --git a/com/android/systemui/statusbar/car/CarStatusBar.java b/com/android/systemui/statusbar/car/CarStatusBar.java index 680f693a..59d3e0a3 100644 --- a/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/com/android/systemui/statusbar/car/CarStatusBar.java @@ -314,7 +314,7 @@ public class CarStatusBar extends StatusBar implements ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getRunningTask(); if (runningTaskInfo != null && runningTaskInfo.baseActivity != null) { mController.taskChanged(runningTaskInfo.baseActivity.getPackageName(), - runningTaskInfo.stackId); + runningTaskInfo); } } } @@ -378,9 +378,10 @@ public class CarStatusBar extends StatusBar implements return result; } - public int startActivityOnStack(Intent intent, int stackId) { - ActivityOptions options = ActivityOptions.makeBasic(); - options.setLaunchStackId(stackId); + public int startActivityOnStack(Intent intent, int windowingMode, int activityType) { + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(windowingMode); + options.setLaunchActivityType(activityType); return startActivityWithOptions(intent, options.toBundle()); } diff --git a/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java index 9bfa7a97..bb979ebd 100644 --- a/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java +++ b/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java @@ -25,7 +25,6 @@ import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; -import com.android.systemui.R; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.TransformableView; @@ -48,7 +47,6 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp private int mContentHeight; private int mMinHeightHint; - private boolean mColorized; protected NotificationTemplateViewWrapper(Context ctx, View view, ExpandableNotificationRow row) { @@ -164,9 +162,7 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp public void onContentUpdated(ExpandableNotificationRow row) { // Reinspect the notification. Before the super call, because the super call also updates // the transformation types and we need to have our values set by then. - StatusBarNotification sbn = row.getStatusBarNotification(); - resolveTemplateViews(sbn); - mColorized = sbn.getNotification().isColorized(); + resolveTemplateViews(row.getStatusBarNotification()); super.onContentUpdated(row); } @@ -269,17 +265,6 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp updateActionOffset(); } - @Override - public int getMinHeightIncrease(boolean useIncreasedCollapsedHeight) { - if (mColorized) { - int dimen = useIncreasedCollapsedHeight - ? R.dimen.notification_height_increase_colorized_increased - : R.dimen.notification_height_increase_colorized; - return mRow.getResources().getDimensionPixelSize(dimen); - } - return super.getMinHeightIncrease(useIncreasedCollapsedHeight); - } - private void updateActionOffset() { if (mActionsContainer != null) { // We should never push the actions higher than they are in the headsup view. diff --git a/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/com/android/systemui/statusbar/notification/NotificationViewWrapper.java index 085bce97..5200d696 100644 --- a/com/android/systemui/statusbar/notification/NotificationViewWrapper.java +++ b/com/android/systemui/statusbar/notification/NotificationViewWrapper.java @@ -190,14 +190,4 @@ public abstract class NotificationViewWrapper implements TransformableView { public boolean disallowSingleClick(float x, float y) { return false; } - - /** - * Get the amount that the minheight is allowed to be increased based on this layout. - * - * @param increasedHeight is the view allowed to show even bigger, i.e for messaging layouts - * @return - */ - public int getMinHeightIncrease(boolean increasedHeight) { - return 0; - } } diff --git a/com/android/systemui/statusbar/phone/BarTransitions.java b/com/android/systemui/statusbar/phone/BarTransitions.java index 1f44abea..4bca7971 100644 --- a/com/android/systemui/statusbar/phone/BarTransitions.java +++ b/com/android/systemui/statusbar/phone/BarTransitions.java @@ -50,7 +50,7 @@ public class BarTransitions { public static final int MODE_LIGHTS_OUT_TRANSPARENT = 6; public static final int LIGHTS_IN_DURATION = 250; - public static final int LIGHTS_OUT_DURATION = 750; + public static final int LIGHTS_OUT_DURATION = 1500; public static final int BACKGROUND_DURATION = 200; private final String mTag; diff --git a/com/android/systemui/statusbar/phone/DozeScrimController.java b/com/android/systemui/statusbar/phone/DozeScrimController.java index 021b4518..8afb8490 100644 --- a/com/android/systemui/statusbar/phone/DozeScrimController.java +++ b/com/android/systemui/statusbar/phone/DozeScrimController.java @@ -384,7 +384,7 @@ public class DozeScrimController { if (mDozeParameters.getAlwaysOn()) { // Setting power states can happen after we push out the frame. Make sure we // stay fully opaque until the power state request reaches the lower levels. - setDozeInFrontAlphaDelayed(mAodFrontScrimOpacity, 30); + setDozeInFrontAlphaDelayed(mAodFrontScrimOpacity, 100); } } }; diff --git a/com/android/systemui/statusbar/phone/LightBarController.java b/com/android/systemui/statusbar/phone/LightBarController.java index 533771a3..d226fede 100644 --- a/com/android/systemui/statusbar/phone/LightBarController.java +++ b/com/android/systemui/statusbar/phone/LightBarController.java @@ -264,8 +264,10 @@ public class LightBarController implements BatteryController.BatteryStateChangeC pw.println(" StatusBarTransitionsController:"); mStatusBarIconController.getTransitionsController().dump(fd, pw, args); pw.println(); - pw.println(" NavigationBarTransitionsController:"); - mNavigationBarController.dump(fd, pw, args); - pw.println(); + if (mNavigationBarController != null) { + pw.println(" NavigationBarTransitionsController:"); + mNavigationBarController.dump(fd, pw, args); + pw.println(); + } } } diff --git a/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/com/android/systemui/statusbar/phone/NavigationBarFragment.java index cfe0a4a6..6d3bc1df 100644 --- a/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -437,7 +437,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { } private boolean onNavigationTouch(View v, MotionEvent event) { - mStatusBar.checkUserAutohide(v, event); + mStatusBar.checkUserAutohide(event); return false; } diff --git a/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index 41a69b4b..40fe50fb 100644 --- a/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -4,10 +4,8 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Color; import android.graphics.Rect; -import android.graphics.drawable.Icon; import android.support.annotation.NonNull; import android.support.v4.util.ArrayMap; -import android.support.v4.util.ArraySet; import android.view.LayoutInflater; import android.view.View; import android.widget.FrameLayout; @@ -269,18 +267,26 @@ public class NotificationIconAreaController implements DarkReceiver { */ private void applyNotificationIconsTint() { for (int i = 0; i < mNotificationIcons.getChildCount(); i++) { - StatusBarIconView v = (StatusBarIconView) mNotificationIcons.getChildAt(i); - boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L)); - int color = StatusBarIconView.NO_COLOR; - boolean colorize = !isPreL || NotificationUtils.isGrayscale(v, mNotificationColorUtil); - if (colorize) { - color = DarkIconDispatcher.getTint(mTintArea, v, mIconTint); + final StatusBarIconView iv = (StatusBarIconView) mNotificationIcons.getChildAt(i); + if (iv.getWidth() != 0) { + updateTintForIcon(iv); + } else { + iv.executeOnLayout(() -> updateTintForIcon(iv)); } - v.setStaticDrawableColor(color); - v.setDecorColor(mIconTint); } } + private void updateTintForIcon(StatusBarIconView v) { + boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L)); + int color = StatusBarIconView.NO_COLOR; + boolean colorize = !isPreL || NotificationUtils.isGrayscale(v, mNotificationColorUtil); + if (colorize) { + color = DarkIconDispatcher.getTint(mTintArea, v, mIconTint); + } + v.setStaticDrawableColor(color); + v.setDecorColor(mIconTint); + } + public void setDark(boolean dark) { mNotificationIcons.setDark(dark, false, 0); mShelfIcons.setDark(dark, false, 0); diff --git a/com/android/systemui/statusbar/phone/NotificationPanelView.java b/com/android/systemui/statusbar/phone/NotificationPanelView.java index 078e8189..7b11ace8 100644 --- a/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -509,7 +509,8 @@ public class NotificationPanelView extends PanelView implements if (row.isRemoved()) { continue; } - availableSpace -= child.getMinHeight() + notificationPadding; + availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */) + + notificationPadding; if (availableSpace >= 0 && count < maximum) { count++; } else if (availableSpace > -shelfSize) { @@ -666,7 +667,7 @@ public class NotificationPanelView extends PanelView implements return false; } initDownStates(event); - if (mHeadsUpTouchHelper.onInterceptTouchEvent(event)) { + if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) { mIsExpansionFromHeadsUp = true; MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1); MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, 1); diff --git a/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 4ae13936..9c837ed8 100644 --- a/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -16,8 +16,12 @@ package com.android.systemui.statusbar.phone; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; + import android.app.ActivityManager; -import android.app.ActivityManager.StackId; import android.app.ActivityManager.StackInfo; import android.app.AlarmManager; import android.app.AlarmManager.AlarmClockInfo; @@ -523,12 +527,18 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, mCurrentNotifs.clear(); mUiOffloadThread.submit(() -> { try { - int focusedId = ActivityManager.getService().getFocusedStackId(); - if (focusedId == StackId.FULLSCREEN_WORKSPACE_STACK_ID) { - checkStack(StackId.FULLSCREEN_WORKSPACE_STACK_ID, notifs, noMan, pm); + final StackInfo focusedStack = ActivityManager.getService().getFocusedStackInfo(); + if (focusedStack != null) { + final int windowingMode = + focusedStack.configuration.windowConfiguration.getWindowingMode(); + if (windowingMode == WINDOWING_MODE_FULLSCREEN + || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) { + checkStack(focusedStack, notifs, noMan, pm); + } } if (mDockedStackExists) { - checkStack(StackId.DOCKED_STACK_ID, notifs, noMan, pm); + checkStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED, + notifs, noMan, pm); } } catch (RemoteException e) { e.rethrowFromSystemServer(); @@ -539,10 +549,19 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, }); } - private void checkStack(int stackId, ArraySet<Pair<String, Integer>> notifs, + private void checkStack(int windowingMode, int activityType, + ArraySet<Pair<String, Integer>> notifs, NotificationManager noMan, IPackageManager pm) { + try { + final StackInfo info = + ActivityManager.getService().getStackInfo(windowingMode, activityType); + checkStack(info, notifs, noMan, pm); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + private void checkStack(StackInfo info, ArraySet<Pair<String, Integer>> notifs, NotificationManager noMan, IPackageManager pm) { try { - StackInfo info = ActivityManager.getService().getStackInfo(stackId); if (info == null || info.topActivity == null) return; String pkg = info.topActivity.getPackageName(); if (!hasNotif(notifs, pkg, info.userId)) { diff --git a/com/android/systemui/statusbar/phone/StatusBar.java b/com/android/systemui/statusbar/phone/StatusBar.java index efc8d8b9..54be857d 100644 --- a/com/android/systemui/statusbar/phone/StatusBar.java +++ b/com/android/systemui/statusbar/phone/StatusBar.java @@ -20,6 +20,7 @@ import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN; import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.app.StatusBarManager.windowStateToString; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP; import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE; import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING; @@ -37,7 +38,6 @@ import android.animation.AnimatorListenerAdapter; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; -import android.app.ActivityManager.StackId; import android.app.ActivityOptions; import android.app.INotificationManager; import android.app.KeyguardManager; @@ -69,9 +69,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.database.ContentObserver; import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.ColorFilter; -import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PointF; import android.graphics.PorterDuff; @@ -105,10 +102,10 @@ import android.os.UserManager; import android.os.Vibrator; import android.provider.Settings; import android.service.notification.NotificationListenerService.RankingMap; +import android.service.notification.NotificationStats; import android.service.notification.StatusBarNotification; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; -import android.text.TextUtils; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.EventLog; @@ -127,13 +124,11 @@ import android.view.View; import android.view.ViewAnimationUtils; import android.view.ViewGroup; import android.view.ViewParent; -import android.view.ViewStub; import android.view.ViewTreeObserver; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; import android.view.animation.AccelerateInterpolator; -import android.view.animation.Interpolator; import android.widget.DateTimeView; import android.widget.ImageView; import android.widget.RemoteViews; @@ -259,7 +254,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.Stack; @@ -279,11 +273,9 @@ public class StatusBar extends SystemUI implements DemoMode, = SystemProperties.getBoolean("debug.child_notifs", true); public static final boolean FORCE_REMOTE_INPUT_HISTORY = SystemProperties.getBoolean("debug.force_remoteinput_history", false); - private static boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false; + private static final boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false; - protected static final int MSG_SHOW_RECENT_APPS = 1019; protected static final int MSG_HIDE_RECENT_APPS = 1020; - protected static final int MSG_TOGGLE_RECENTS_APPS = 1021; protected static final int MSG_PRELOAD_RECENT_APPS = 1022; protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023; protected static final int MSG_TOGGLE_KEYBOARD_SHORTCUTS_MENU = 1026; @@ -339,7 +331,7 @@ public class StatusBar extends SystemUI implements DemoMode, private static final int STATUS_OR_NAV_TRANSIENT = View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT; - private static final long AUTOHIDE_TIMEOUT_MS = 3000; + private static final long AUTOHIDE_TIMEOUT_MS = 2250; /** The minimum delay in ms between reports of notification visibility. */ private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500; @@ -365,10 +357,6 @@ public class StatusBar extends SystemUI implements DemoMode, /** If true, the lockscreen will show a distinct wallpaper */ private static final boolean ENABLE_LOCKSCREEN_WALLPAPER = true; - /* If true, the device supports freeform window management. - * This affects the status bar UI. */ - private static final boolean FREEFORM_WINDOW_MANAGEMENT; - /** * How long to wait before auto-dismissing a notification that was kept for remote input, and * has now sent a remote input. We auto-dismiss, because the app may not see a reason to cancel @@ -387,19 +375,14 @@ public class StatusBar extends SystemUI implements DemoMode, static { boolean onlyCoreApps; - boolean freeformWindowManagement; try { IPackageManager packageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); onlyCoreApps = packageManager.isOnlyCoreApps(); - freeformWindowManagement = packageManager.hasSystemFeature( - PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT, 0); } catch (RemoteException e) { onlyCoreApps = false; - freeformWindowManagement = false; } ONLY_CORE_APPS = onlyCoreApps; - FREEFORM_WINDOW_MANAGEMENT = freeformWindowManagement; } /** @@ -410,17 +393,17 @@ public class StatusBar extends SystemUI implements DemoMode, protected boolean mShowLockscreenNotifications; protected boolean mAllowLockscreenRemoteInput; - PhoneStatusBarPolicy mIconPolicy; + private PhoneStatusBarPolicy mIconPolicy; - VolumeComponent mVolumeComponent; - BrightnessMirrorController mBrightnessMirrorController; + private VolumeComponent mVolumeComponent; + private BrightnessMirrorController mBrightnessMirrorController; protected FingerprintUnlockController mFingerprintUnlockController; - LightBarController mLightBarController; + private LightBarController mLightBarController; protected LockscreenWallpaper mLockscreenWallpaper; - int mNaturalBarHeight = -1; + private int mNaturalBarHeight = -1; - Point mCurrentDisplaySize = new Point(); + private final Point mCurrentDisplaySize = new Point(); protected StatusBarWindowView mStatusBarWindow; protected PhoneStatusBarView mStatusBarView; @@ -431,15 +414,13 @@ public class StatusBar extends SystemUI implements DemoMode, private boolean mWakeUpComingFromTouch; private PointF mWakeUpTouchLocation; - int mPixelFormat; - Object mQueueLock = new Object(); + private final Object mQueueLock = new Object(); protected StatusBarIconController mIconController; // expanded notifications protected NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window - View mExpandedContents; - TextView mNotificationPanelDebugText; + private TextView mNotificationPanelDebugText; /** * {@code true} if notifications not part of a group should by default be rendered in their @@ -452,12 +433,10 @@ public class StatusBar extends SystemUI implements DemoMode, private QSPanel mQSPanel; // top bar - protected KeyguardStatusBarView mKeyguardStatusBar; - boolean mLeaveOpenOnKeyguardHide; + private KeyguardStatusBarView mKeyguardStatusBar; + private boolean mLeaveOpenOnKeyguardHide; KeyguardIndicationController mKeyguardIndicationController; - // Keyguard is going away soon. - private boolean mKeyguardGoingAway; // Keyguard is actually fading away now. protected boolean mKeyguardFadingAway; protected long mKeyguardFadingAwayDelay; @@ -469,25 +448,19 @@ public class StatusBar extends SystemUI implements DemoMode, private View mReportRejectedTouch; - int mMaxAllowedKeyguardNotifications; - - boolean mExpandedVisible; - - // the tracker view - int mTrackingPosition; // the position of the top of the tracking view. + private int mMaxAllowedKeyguardNotifications; - // Tracking finger for opening/closing. - boolean mTracking; + private boolean mExpandedVisible; - int[] mAbsPos = new int[2]; - ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>(); + private final int[] mAbsPos = new int[2]; + private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>(); // for disabling the status bar - int mDisabled1 = 0; - int mDisabled2 = 0; + private int mDisabled1 = 0; + private int mDisabled2 = 0; // tracking calls to View.setSystemUiVisibility() - int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; + private int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE; private final Rect mLastFullscreenStackBounds = new Rect(); private final Rect mLastDockedStackBounds = new Rect(); private final Rect mTmpRect = new Rect(); @@ -495,7 +468,7 @@ public class StatusBar extends SystemUI implements DemoMode, // last value sent to window manager private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE; - DisplayMetrics mDisplayMetrics = new DisplayMetrics(); + private final DisplayMetrics mDisplayMetrics = new DisplayMetrics(); // XXX: gesture research private final GestureRecorder mGestureRec = DEBUG_GESTURES @@ -507,14 +480,17 @@ public class StatusBar extends SystemUI implements DemoMode, private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); // ensure quick settings is disabled until the current user makes it through the setup wizard - private boolean mUserSetup = false; - private DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() { + @VisibleForTesting + protected boolean mUserSetup = false; + private final DeviceProvisionedListener mUserSetupObserver = new DeviceProvisionedListener() { @Override public void onUserSetupChanged() { final boolean userSetup = mDeviceProvisionedController.isUserSetup( mDeviceProvisionedController.getCurrentUser()); - if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " + - "userSetup=%s mUserSetup=%s", userSetup, mUserSetup)); + if (MULTIUSER_DEBUG) { + Log.d(TAG, String.format("User setup changed: userSetup=%s mUserSetup=%s", + userSetup, mUserSetup)); + } if (userSetup != mUserSetup) { mUserSetup = userSetup; @@ -528,7 +504,7 @@ public class StatusBar extends SystemUI implements DemoMode, } }; - protected H mHandler = createHandler(); + protected final H mHandler = createHandler(); final private ContentObserver mHeadsUpObserver = new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange) { @@ -537,8 +513,6 @@ public class StatusBar extends SystemUI implements DemoMode, && Settings.Global.HEADS_UP_OFF != Settings.Global.getInt( mContext.getContentResolver(), Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED, Settings.Global.HEADS_UP_OFF); - mHeadsUpTicker = mUseHeadsUp && 0 != Settings.Global.getInt( - mContext.getContentResolver(), SETTING_HEADS_UP_TICKER, 0); Log.d(TAG, "heads up is " + (mUseHeadsUp ? "enabled" : "disabled")); if (wasUsing != mUseHeadsUp) { if (!mUseHeadsUp) { @@ -566,26 +540,21 @@ public class StatusBar extends SystemUI implements DemoMode, } }; - private boolean mWaitingForKeyguardExit; protected boolean mDozing; private boolean mDozingRequested; protected boolean mScrimSrcModeEnabled; - public static final Interpolator ALPHA_IN = Interpolators.ALPHA_IN; - public static final Interpolator ALPHA_OUT = Interpolators.ALPHA_OUT; - protected BackDropView mBackdrop; protected ImageView mBackdropFront, mBackdropBack; - protected PorterDuffXfermode mSrcXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC); - protected PorterDuffXfermode mSrcOverXferMode = + protected final PorterDuffXfermode mSrcXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC); + protected final PorterDuffXfermode mSrcOverXferMode = new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER); private MediaSessionManager mMediaSessionManager; private MediaController mMediaController; private String mMediaNotificationKey; private MediaMetadata mMediaMetadata; - private MediaController.Callback mMediaListener - = new MediaController.Callback() { + private final MediaController.Callback mMediaListener = new MediaController.Callback() { @Override public void onPlaybackStateChanged(PlaybackState state) { super.onPlaybackStateChanged(state); @@ -607,17 +576,6 @@ public class StatusBar extends SystemUI implements DemoMode, } }; - private final OnChildLocationsChangedListener mOnChildLocationsChangedListener = - new OnChildLocationsChangedListener() { - @Override - public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout) { - userActivity(); - } - }; - - private int mDisabledUnmodified1; - private int mDisabledUnmodified2; - /** Keys of notifications currently visible to the user. */ private final ArraySet<NotificationVisibility> mCurrentlyVisibleNotifications = new ArraySet<>(); @@ -644,15 +602,6 @@ public class StatusBar extends SystemUI implements DemoMode, private boolean mWereIconsJustHidden; private boolean mBouncerWasShowingWhenHidden; - public boolean isStartedGoingToSleep() { - return mStartedGoingToSleep; - } - - /** - * If set, the device has started going to sleep but isn't fully non-interactive yet. - */ - protected boolean mStartedGoingToSleep; - private final OnChildLocationsChangedListener mNotificationLocationsChangedListener = new OnChildLocationsChangedListener() { @Override @@ -686,7 +635,6 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void run() { mLastVisibilityReportUptimeMs = SystemClock.uptimeMillis(); - final String mediaKey = getCurrentMediaNotificationKey(); // 1. Loop over mNotificationData entries: // A. Keep list of visible notifications. @@ -743,10 +691,10 @@ public class StatusBar extends SystemUI implements DemoMode, private boolean mKeyguardRequested; private boolean mIsKeyguard; private LogMaker mStatusBarStateLog; - private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger(); + private final LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger(); protected NotificationIconAreaController mNotificationIconAreaController; private boolean mReinflateNotificationsOnUserSwitched; - private HashMap<String, Entry> mPendingNotifications = new HashMap<>(); + private final HashMap<String, Entry> mPendingNotifications = new HashMap<>(); private boolean mClearAllEnabled; @Nullable private View mAmbientIndicationContainer; private String mKeyToRemoveOnGutsClosed; @@ -769,20 +717,21 @@ public class StatusBar extends SystemUI implements DemoMode, goToLockedShade(null); } }; - private HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>> mTmpChildOrderMap - = new HashMap<>(); + private final HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>> + mTmpChildOrderMap = new HashMap<>(); private RankingMap mLatestRankingMap; private boolean mNoAnimationOnNextBarModeChange; private FalsingManager mFalsingManager; - private KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { - @Override - public void onDreamingStateChanged(boolean dreaming) { - if (dreaming) { - maybeEscalateHeadsUp(); - } - } - }; + private final KeyguardUpdateMonitorCallback mUpdateCallback = + new KeyguardUpdateMonitorCallback() { + @Override + public void onDreamingStateChanged(boolean dreaming) { + if (dreaming) { + maybeEscalateHeadsUp(); + } + } + }; private NavigationBarFragment mNavigationBar; private View mNavigationBarView; @@ -861,10 +810,6 @@ public class StatusBar extends SystemUI implements DemoMode, mRecents = getComponent(Recents.class); - final Configuration currentConfig = res.getConfiguration(); - mLocale = currentConfig.locale; - mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale); - mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); mLockPatternUtils = new LockPatternUtils(mContext); @@ -994,9 +939,6 @@ public class StatusBar extends SystemUI implements DemoMode, Dependency.get(ConfigurationController.class).addCallback(this); } - protected void createIconController() { - } - // ================================================================================ // Constructing the view // ================================================================================ @@ -1012,16 +954,14 @@ public class StatusBar extends SystemUI implements DemoMode, // TODO: Deal with the ugliness that comes from having some of the statusbar broken out // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot. - mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById( - R.id.notification_panel); - mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById( - R.id.notification_stack_scroller); + mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel); + mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller); mNotificationPanel.setStatusBar(this); mNotificationPanel.setGroupManager(mGroupManager); mAboveShelfObserver = new AboveShelfObserver(mStackScroller); mAboveShelfObserver.setListener(mStatusBarWindow.findViewById( R.id.notification_container_parent)); - mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header); + mKeyguardStatusBar = mStatusBarWindow.findViewById(R.id.keyguard_header); mNotificationIconAreaController = SystemUIFactory.getInstance() .createNotificationIconAreaController(context, this); @@ -1057,10 +997,10 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationData.setHeadsUpManager(mHeadsUpManager); mGroupManager.setHeadsUpManager(mHeadsUpManager); mHeadsUpManager.setVisualStabilityManager(mVisualStabilityManager); + putComponent(HeadsUpManager.class, mHeadsUpManager); if (MULTIUSER_DEBUG) { - mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById( - R.id.header_debug_info); + mNotificationPanelDebugText = mNotificationPanel.findViewById(R.id.header_debug_info); mNotificationPanelDebugText.setVisibility(View.VISIBLE); } @@ -1074,9 +1014,6 @@ public class StatusBar extends SystemUI implements DemoMode, // no window manager? good luck with that } - // figure out which pixel-format to use for the status bar. - mPixelFormat = PixelFormat.OPAQUE; - mStackScroller.setLongPressListener(getNotificationLongClicker()); mStackScroller.setStatusBar(this); mStackScroller.setGroupManager(mGroupManager); @@ -1086,11 +1023,10 @@ public class StatusBar extends SystemUI implements DemoMode, inflateEmptyShadeView(); inflateDismissView(); - mExpandedContents = mStackScroller; - mBackdrop = (BackDropView) mStatusBarWindow.findViewById(R.id.backdrop); - mBackdropFront = (ImageView) mBackdrop.findViewById(R.id.backdrop_front); - mBackdropBack = (ImageView) mBackdrop.findViewById(R.id.backdrop_back); + mBackdrop = mStatusBarWindow.findViewById(R.id.backdrop); + mBackdropFront = mBackdrop.findViewById(R.id.backdrop_front); + mBackdropBack = mBackdrop.findViewById(R.id.backdrop_back); if (ENABLE_LOCKSCREEN_WALLPAPER) { mLockscreenWallpaper = new LockscreenWallpaper(mContext, this, mHandler); @@ -1098,8 +1034,8 @@ public class StatusBar extends SystemUI implements DemoMode, mKeyguardIndicationController = SystemUIFactory.getInstance().createKeyguardIndicationController(mContext, - (ViewGroup) mStatusBarWindow.findViewById(R.id.keyguard_indication_area), - mNotificationPanel.getLockIcon()); + mStatusBarWindow.findViewById(R.id.keyguard_indication_area), + mNotificationPanel.getLockIcon()); mNotificationPanel.setKeyguardIndicationController(mKeyguardIndicationController); @@ -1130,8 +1066,8 @@ public class StatusBar extends SystemUI implements DemoMode, mNavigationBar.setLightBarController(mLightBarController); } - ScrimView scrimBehind = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_behind); - ScrimView scrimInFront = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_in_front); + ScrimView scrimBehind = mStatusBarWindow.findViewById(R.id.scrim_behind); + ScrimView scrimInFront = mStatusBarWindow.findViewById(R.id.scrim_in_front); View headsUpScrim = mStatusBarWindow.findViewById(R.id.heads_up_scrim); mScrimController = SystemUIFactory.getInstance().createScrimController(mLightBarController, scrimBehind, scrimInFront, headsUpScrim, mLockscreenWallpaper, @@ -1141,13 +1077,10 @@ public class StatusBar extends SystemUI implements DemoMode, } }); if (mScrimSrcModeEnabled) { - Runnable runnable = new Runnable() { - @Override - public void run() { - boolean asSrc = mBackdrop.getVisibility() != View.VISIBLE; - mScrimController.setDrawBehindAsSrc(asSrc); - mStackScroller.setDrawBackgroundAsSrc(asSrc); - } + Runnable runnable = () -> { + boolean asSrc = mBackdrop.getVisibility() != View.VISIBLE; + mScrimController.setDrawBehindAsSrc(asSrc); + mStackScroller.setDrawBackgroundAsSrc(asSrc); }; mBackdrop.setOnVisibilityChangedRunnable(runnable); runnable.run(); @@ -1169,11 +1102,11 @@ public class StatusBar extends SystemUI implements DemoMode, if (container != null) { FragmentHostManager fragmentHostManager = FragmentHostManager.get(container); ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame, - Dependency.get(ExtensionController.class).newExtension(QS.class) + Dependency.get(ExtensionController.class) + .newExtension(QS.class) .withPlugin(QS.class) - .withFeature( - PackageManager.FEATURE_AUTOMOTIVE, () -> new CarQSFragment()) - .withDefault(() -> new QSFragment()) + .withFeature(PackageManager.FEATURE_AUTOMOTIVE, CarQSFragment::new) + .withDefault(QSFragment::new) .build()); final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this, mIconController); @@ -1275,7 +1208,7 @@ public class StatusBar extends SystemUI implements DemoMode, */ protected View.OnTouchListener getStatusBarWindowTouchListener() { return (v, event) -> { - checkUserAutohide(v, event); + checkUserAutohide(event); checkRemoteInputOutside(event); if (event.getAction() == MotionEvent.ACTION_DOWN) { if (mExpandedVisible) { @@ -1379,8 +1312,7 @@ public class StatusBar extends SystemUI implements DemoMode, public static SignalClusterView reinflateSignalCluster(View view) { Context context = view.getContext(); - SignalClusterView signalCluster = - (SignalClusterView) view.findViewById(R.id.signal_cluster); + SignalClusterView signalCluster = view.findViewById(R.id.signal_cluster); if (signalCluster != null) { ViewParent parent = signalCluster.getParent(); if (parent instanceof ViewGroup) { @@ -1420,20 +1352,17 @@ public class StatusBar extends SystemUI implements DemoMode, mDismissView = (DismissView) LayoutInflater.from(mContext).inflate( R.layout.status_bar_notification_dismiss_all, mStackScroller, false); - mDismissView.setOnButtonClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES); - clearAllNotifications(); - } + mDismissView.setOnButtonClickListener(v -> { + mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES); + clearAllNotifications(); }); mStackScroller.setDismissView(mDismissView); } protected void createUserSwitcher() { mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext, - (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher), - mKeyguardStatusBar, mNotificationPanel); + mStatusBarWindow.findViewById(R.id.keyguard_user_switcher), mKeyguardStatusBar, + mNotificationPanel); } protected void inflateStatusBarWindow(Context context) { @@ -1446,7 +1375,7 @@ public class StatusBar extends SystemUI implements DemoMode, // animate-swipe all dismissable notifications, then animate the shade closed int numChildren = mStackScroller.getChildCount(); - final ArrayList<View> viewsToHide = new ArrayList<View>(numChildren); + final ArrayList<View> viewsToHide = new ArrayList<>(numChildren); final ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>(numChildren); for (int i = 0; i < numChildren; i++) { final View child = mStackScroller.getChildAt(i); @@ -1486,20 +1415,18 @@ public class StatusBar extends SystemUI implements DemoMode, return; } - addPostCollapseAction(new Runnable() { - @Override - public void run() { - mStackScroller.setDismissAllInProgress(false); - for (ExpandableNotificationRow rowToRemove : viewsToRemove) { - if (mStackScroller.canChildBeDismissed(rowToRemove)) { - removeNotification(rowToRemove.getEntry().key, null); - } else { - rowToRemove.resetTranslation(); - } + addPostCollapseAction(() -> { + mStackScroller.setDismissAllInProgress(false); + for (ExpandableNotificationRow rowToRemove : viewsToRemove) { + if (mStackScroller.canChildBeDismissed(rowToRemove)) { + removeNotification(rowToRemove.getEntry().key, null); + } else { + rowToRemove.resetTranslation(); } - try { - mBarService.onClearAllNotifications(mCurrentUserId); - } catch (Exception ex) { } + } + try { + mBarService.onClearAllNotifications(mCurrentUserId); + } catch (Exception ex) { } }); @@ -1508,13 +1435,15 @@ public class StatusBar extends SystemUI implements DemoMode, } private void performDismissAllAnimations(ArrayList<View> hideAnimatedList) { - Runnable animationFinishAction = new Runnable() { - @Override - public void run() { - animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); - } + Runnable animationFinishAction = () -> { + animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); }; + if (hideAnimatedList.isEmpty()) { + animationFinishAction.run(); + return; + } + // let's disable our normal animations mStackScroller.setDismissAllInProgress(true); @@ -1631,10 +1560,6 @@ public class StatusBar extends SystemUI implements DemoMode, SystemServicesProxy.getInstance(mContext).awakenDreamsAsync(); } - public UserHandle getCurrentUserHandle() { - return new UserHandle(mCurrentUserId); - } - public void addNotification(StatusBarNotification notification, RankingMap ranking) throws InflationException { String key = notification.getKey(); @@ -1745,7 +1670,7 @@ public class StatusBar extends SystemUI implements DemoMode, boolean deferRemoval = false; abortExistingInflation(key); if (mHeadsUpManager.isHeadsUp(key)) { - // A cancel() in repsonse to a remote input shouldn't be delayed, as it makes the + // A cancel() in response to a remote input shouldn't be delayed, as it makes the // sending look longer than it takes. // Also we should not defer the removal if reordering isn't allowed since otherwise // some notifications can't disappear before the panel is closed. @@ -1771,9 +1696,7 @@ public class StatusBar extends SystemUI implements DemoMode, newHistory = new CharSequence[1]; } else { newHistory = new CharSequence[oldHistory.length + 1]; - for (int i = 0; i < oldHistory.length; i++) { - newHistory[i + 1] = oldHistory[i]; - } + System.arraycopy(oldHistory, 0, newHistory, 1, oldHistory.length); } newHistory[0] = String.valueOf(entry.remoteInputText); b.setRemoteInputHistory(newHistory); @@ -1832,7 +1755,7 @@ public class StatusBar extends SystemUI implements DemoMode, mStackScroller.cleanUpViewState(entry.row); } // Let's remove the children if this was a summary - handleGroupSummaryRemoved(key, ranking); + handleGroupSummaryRemoved(key); StatusBarNotification old = removeNotificationViews(key, ranking); if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old); @@ -1856,12 +1779,10 @@ public class StatusBar extends SystemUI implements DemoMode, * * This also ensures that the animation looks nice and only consists of a single disappear * animation instead of multiple. + * @param key the key of the notification was removed * - * @param key the key of the notification was removed - * @param ranking the current ranking */ - private void handleGroupSummaryRemoved(String key, - RankingMap ranking) { + private void handleGroupSummaryRemoved(String key) { Entry entry = mNotificationData.get(key); if (entry != null && entry.row != null && entry.row.isSummaryWithChildren()) { @@ -1872,15 +1793,13 @@ public class StatusBar extends SystemUI implements DemoMode, } List<ExpandableNotificationRow> notificationChildren = entry.row.getNotificationChildren(); - ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>(); for (int i = 0; i < notificationChildren.size(); i++) { ExpandableNotificationRow row = notificationChildren.get(i); if ((row.getStatusBarNotification().getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { - // the child is a forground service notification which we can't remove! + // the child is a foreground service notification which we can't remove! continue; } - toRemove.add(row); row.setKeepInParent(true); // we need to set this state earlier as otherwise we might generate some weird // animations @@ -1900,7 +1819,9 @@ public class StatusBar extends SystemUI implements DemoMode, final int id = n.getId(); final int userId = n.getUserId(); try { - mBarService.onNotificationClear(pkg, tag, id, userId); + // TODO: record actual dismissal surface + mBarService.onNotificationClear(pkg, tag, id, userId, n.getKey(), + NotificationStats.DISMISSAL_OTHER); if (FORCE_REMOTE_INPUT_HISTORY && mKeysKeptForRemoteInput.contains(n.getKey())) { mKeysKeptForRemoteInput.remove(n.getKey()); @@ -1923,19 +1844,14 @@ public class StatusBar extends SystemUI implements DemoMode, // Do not modify the notifications during collapse. if (isCollapsing()) { - addPostCollapseAction(new Runnable() { - @Override - public void run() { - updateNotificationShade(); - } - }); + addPostCollapseAction(this::updateNotificationShade); return; } ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications(); ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size()); final int N = activeNotifications.size(); - for (int i=0; i<N; i++) { + for (int i = 0; i < N; i++) { Entry ent = activeNotifications.get(i); if (ent.row.isDismissed() || ent.row.isRemoved()) { // we don't want to update removed notifications because they could @@ -1979,7 +1895,8 @@ public class StatusBar extends SystemUI implements DemoMode, for (ExpandableNotificationRow remove : toRemove) { if (mGroupManager.isChildInGroupWithSummary(remove.getStatusBarNotification())) { - // we are only transfering this notification to its parent, don't generate an animation + // we are only transferring this notification to its parent, don't generate an + // animation mStackScroller.setChildTransferInProgress(true); } if (remove.isSummaryWithChildren()) { @@ -1991,7 +1908,7 @@ public class StatusBar extends SystemUI implements DemoMode, removeNotificationChildren(); - for (int i=0; i<toShow.size(); i++) { + for (int i = 0; i < toShow.size(); i++) { View v = toShow.get(i); if (v.getParent() == null) { mVisualStabilityManager.notifyViewAddition(v); @@ -2065,6 +1982,7 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned() && (mUserSetup || mUserSwitcherController == null || !mUserSwitcherController.isSimpleUserSwitcher()) + && ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0) && ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0) && !mDozing && !ONLY_CORE_APPS); @@ -2101,7 +2019,7 @@ public class StatusBar extends SystemUI implements DemoMode, } } - // Finally after removing and adding has been beformed we can apply the order. + // Finally after removing and adding has been performed we can apply the order. orderChanged |= parent.applyChildOrder(orderedChildren, mVisualStabilityManager, this); } if (orderChanged) { @@ -2274,10 +2192,11 @@ public class StatusBar extends SystemUI implements DemoMode, MediaController controller = null; for (int i = 0; i < N; i++) { final Entry entry = activeNotifications.get(i); + if (isMediaNotification(entry)) { final MediaSession.Token token = - entry.notification.getNotification().extras - .getParcelable(Notification.EXTRA_MEDIA_SESSION); + entry.notification.getNotification().extras.getParcelable( + Notification.EXTRA_MEDIA_SESSION); if (token != null) { MediaController aController = new MediaController(mContext, token); if (PlaybackState.STATE_PLAYING == @@ -2315,7 +2234,7 @@ public class StatusBar extends SystemUI implements DemoMode, if (entry.notification.getPackageName().equals(pkg)) { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: found controller matching " - + entry.notification.getKey()); + + entry.notification.getKey()); } controller = aController; mediaNotification = entry; @@ -2366,12 +2285,8 @@ public class StatusBar extends SystemUI implements DemoMode, } private boolean isPlaybackActive(int state) { - if (state != PlaybackState.STATE_STOPPED - && state != PlaybackState.STATE_ERROR - && state != PlaybackState.STATE_NONE) { - return true; - } - return false; + return state != PlaybackState.STATE_STOPPED && state != PlaybackState.STATE_ERROR + && state != PlaybackState.STATE_NONE; } private void clearCurrentMediaNotification() { @@ -2396,7 +2311,7 @@ public class StatusBar extends SystemUI implements DemoMode, /** * Hide the album artwork that is fading out and release its bitmap. */ - protected Runnable mHideBackdropFront = new Runnable() { + protected final Runnable mHideBackdropFront = new Runnable() { @Override public void run() { if (DEBUG_MEDIA) { @@ -2548,14 +2463,11 @@ public class StatusBar extends SystemUI implements DemoMode, .setInterpolator(Interpolators.ACCELERATE_DECELERATE) .setDuration(300) .setStartDelay(0) - .withEndAction(new Runnable() { - @Override - public void run() { - mBackdrop.setVisibility(View.GONE); - mBackdropFront.animate().cancel(); - mBackdropBack.setImageDrawable(null); - mHandler.post(mHideBackdropFront); - } + .withEndAction(() -> { + mBackdrop.setVisibility(View.GONE); + mBackdropFront.animate().cancel(); + mBackdropBack.setImageDrawable(null); + mHandler.post(mHideBackdropFront); }); if (mKeyguardFadingAway) { mBackdrop.animate() @@ -2586,8 +2498,6 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void disable(int state1, int state2, boolean animate) { animate &= mStatusBarWindowState != WINDOW_STATE_HIDDEN; - mDisabledUnmodified1 = state1; - mDisabledUnmodified2 = state2; final int old1 = mDisabled1; final int diff1 = state1 ^ old1; mDisabled1 = state1; @@ -2623,8 +2533,13 @@ public class StatusBar extends SystemUI implements DemoMode, flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_CLOCK)) ? '!' : ' '); flagdbg.append(0 != ((state1 & StatusBarManager.DISABLE_SEARCH)) ? 'S' : 's'); flagdbg.append(0 != ((diff1 & StatusBarManager.DISABLE_SEARCH)) ? '!' : ' '); + flagdbg.append("> disable2<"); flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_QUICK_SETTINGS)) ? 'Q' : 'q'); flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS)) ? '!' : ' '); + flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_SYSTEM_ICONS)) ? 'I' : 'i'); + flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_SYSTEM_ICONS)) ? '!' : ' '); + flagdbg.append(0 != ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE)) ? 'N' : 'n'); + flagdbg.append(0 != ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE)) ? '!' : ' '); flagdbg.append('>'); Log.d(TAG, flagdbg.toString()); @@ -2651,6 +2566,13 @@ public class StatusBar extends SystemUI implements DemoMode, if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) { updateQsExpansionEnabled(); } + + if ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) { + updateQsExpansionEnabled(); + if ((state1 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) { + animateCollapsePanels(); + } + } } /** @@ -2738,11 +2660,8 @@ public class StatusBar extends SystemUI implements DemoMode, // make sure that the window stays small for one frame until the touchableRegion is set. mNotificationPanel.requestLayout(); mStatusBarWindowManager.setForceWindowCollapsed(true); - mNotificationPanel.post(new Runnable() { - @Override - public void run() { - mStatusBarWindowManager.setForceWindowCollapsed(false); - } + mNotificationPanel.post(() -> { + mStatusBarWindowManager.setForceWindowCollapsed(false); }); } } else { @@ -2754,15 +2673,12 @@ public class StatusBar extends SystemUI implements DemoMode, // we need to keep the panel open artificially, let's wait until the animation // is finished. mHeadsUpManager.setHeadsUpGoingAway(true); - mStackScroller.runAfterAnimationFinished(new Runnable() { - @Override - public void run() { - if (!mHeadsUpManager.hasPinnedHeadsUp()) { - mStatusBarWindowManager.setHeadsUpShowing(false); - mHeadsUpManager.setHeadsUpGoingAway(false); - } - removeRemoteInputEntriesKeptUntilCollapsed(); + mStackScroller.runAfterAnimationFinished(() -> { + if (!mHeadsUpManager.hasPinnedHeadsUp()) { + mStatusBarWindowManager.setHeadsUpShowing(false); + mHeadsUpManager.setHeadsUpGoingAway(false); } + removeRemoteInputEntriesKeptUntilCollapsed(); }); } } @@ -3013,7 +2929,9 @@ public class StatusBar extends SystemUI implements DemoMode, } boolean panelsEnabled() { - return (mDisabled1 & StatusBarManager.DISABLE_EXPAND) == 0 && !ONLY_CORE_APPS; + return (mDisabled1 & StatusBarManager.DISABLE_EXPAND) == 0 + && (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0 + && !ONLY_CORE_APPS; } void makeExpandedVisible(boolean force) { @@ -3029,7 +2947,6 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarWindowManager.setPanelVisible(true); visibilityChanged(true); - mWaitingForKeyguardExit = false; recomputeDisableFlags(!force /* animate */); setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true); } @@ -3038,23 +2955,15 @@ public class StatusBar extends SystemUI implements DemoMode, animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); } - private final Runnable mAnimateCollapsePanels = new Runnable() { - @Override - public void run() { - animateCollapsePanels(); - } - }; + private final Runnable mAnimateCollapsePanels = this::animateCollapsePanels; public void postAnimateCollapsePanels() { mHandler.post(mAnimateCollapsePanels); } public void postAnimateForceCollapsePanels() { - mHandler.post(new Runnable() { - @Override - public void run() { - animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */); - } + mHandler.post(() -> { + animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */); }); } @@ -3104,6 +3013,9 @@ public class StatusBar extends SystemUI implements DemoMode, } } + // TODO(b/62444020): remove when this bug is fixed + Log.v(TAG, "mStatusBarWindow: " + mStatusBarWindow + " canPanelBeCollapsed(): " + + mNotificationPanel.canPanelBeCollapsed()); if (mStatusBarWindow != null && mNotificationPanel.canPanelBeCollapsed()) { // release focus immediately to kick off focus change transition mStatusBarWindowManager.setStatusBarFocusable(false); @@ -3209,7 +3121,7 @@ public class StatusBar extends SystemUI implements DemoMode, if (SPEW) { Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled1=" - + mDisabled1 + " mDisabled2=" + mDisabled2 + " mTracking=" + mTracking); + + mDisabled1 + " mDisabled2=" + mDisabled2); } else if (CHATTY) { if (event.getAction() != MotionEvent.ACTION_MOVE) { Log.d(TAG, String.format( @@ -3294,10 +3206,8 @@ public class StatusBar extends SystemUI implements DemoMode, sbModeChanged = sbMode != -1; if (sbModeChanged && sbMode != mStatusBarMode) { - if (sbMode != mStatusBarMode) { - mStatusBarMode = sbMode; - checkBarModes(); - } + mStatusBarMode = sbMode; + checkBarModes(); touchAutoHide(); } @@ -3321,7 +3231,6 @@ public class StatusBar extends SystemUI implements DemoMode, } else { cancelAutohide(); } - touchAutoDim(); } protected int computeStatusBarMode(int oldVal, int newVal) { @@ -3385,12 +3294,7 @@ public class StatusBar extends SystemUI implements DemoMode, } } - private final Runnable mCheckBarModes = new Runnable() { - @Override - public void run() { - checkBarModes(); - } - }; + private final Runnable mCheckBarModes = this::checkBarModes; public void setInteracting(int barWindow, boolean interacting) { final boolean changing = ((mInteractingWindows & barWindow) != 0) != interacting; @@ -3404,10 +3308,10 @@ public class StatusBar extends SystemUI implements DemoMode, } // manually dismiss the volume panel when interacting with the nav bar if (changing && interacting && barWindow == StatusBarManager.WINDOW_NAVIGATION_BAR) { + touchAutoDim(); dismissVolumeDialog(); } checkBarModes(); - touchAutoDim(); } private void dismissVolumeDialog() { @@ -3449,7 +3353,7 @@ public class StatusBar extends SystemUI implements DemoMode, } } - void checkUserAutohide(View v, MotionEvent event) { + void checkUserAutohide(MotionEvent event) { if ((mSystemUiVisibility & STATUS_OR_NAV_TRANSIENT) != 0 // a transient bar is revealed && event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar && event.getX() == 0 && event.getY() == 0 // a touch outside both bars @@ -3516,9 +3420,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { synchronized (mQueueLock) { pw.println("Current Status Bar state:"); - pw.println(" mExpandedVisible=" + mExpandedVisible - + ", mTrackingPosition=" + mTrackingPosition); - pw.println(" mTracking=" + mTracking); + pw.println(" mExpandedVisible=" + mExpandedVisible); pw.println(" mDisplayMetrics=" + mDisplayMetrics); pw.println(" mStackScroller: " + viewInfo(mStackScroller)); pw.println(" mStackScroller: " + viewInfo(mStackScroller) @@ -3606,16 +3508,12 @@ public class StatusBar extends SystemUI implements DemoMode, if (false) { pw.println("see the logcat for a dump of the views we have created."); // must happen on ui thread - mHandler.post(new Runnable() { - @Override - public void run() { - mStatusBarView.getLocationOnScreen(mAbsPos); - Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] - + ") " + mStatusBarView.getWidth() + "x" - + getStatusBarHeight()); - mStatusBarView.debug(); - } - }); + mHandler.post(() -> { + mStatusBarView.getLocationOnScreen(mAbsPos); + Log.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1] + + ") " + mStatusBarView.getWidth() + "x" + getStatusBarHeight()); + mStatusBarView.debug(); + }); } } @@ -3695,49 +3593,43 @@ public class StatusBar extends SystemUI implements DemoMode, final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity( mContext, intent, mCurrentUserId); - Runnable runnable = new Runnable() { - @Override - public void run() { - mAssistManager.hideAssist(); - intent.setFlags( - Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - int result = ActivityManager.START_CANCELED; - ActivityOptions options = new ActivityOptions(getActivityOptions()); - options.setDisallowEnterPictureInPictureWhileLaunching( - disallowEnterPictureInPictureWhileLaunching); - if (intent == KeyguardBottomAreaView.INSECURE_CAMERA_INTENT) { - // Normally an activity will set it's requested rotation - // animation on its window. However when launching an activity - // causes the orientation to change this is too late. In these cases - // the default animation is used. This doesn't look good for - // the camera (as it rotates the camera contents out of sync - // with physical reality). So, we ask the WindowManager to - // force the crossfade animation if an orientation change - // happens to occur during the launch. - options.setRotationAnimationHint( - WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS); - } - try { - result = ActivityManager.getService().startActivityAsUser( - null, mContext.getBasePackageName(), - intent, - intent.resolveTypeIfNeeded(mContext.getContentResolver()), - null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, - options.toBundle(), UserHandle.CURRENT.getIdentifier()); - } catch (RemoteException e) { - Log.w(TAG, "Unable to start activity", e); - } - if (callback != null) { - callback.onActivityStarted(result); - } + Runnable runnable = () -> { + mAssistManager.hideAssist(); + intent.setFlags( + Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + int result = ActivityManager.START_CANCELED; + ActivityOptions options = new ActivityOptions(getActivityOptions()); + options.setDisallowEnterPictureInPictureWhileLaunching( + disallowEnterPictureInPictureWhileLaunching); + if (intent == KeyguardBottomAreaView.INSECURE_CAMERA_INTENT) { + // Normally an activity will set it's requested rotation + // animation on its window. However when launching an activity + // causes the orientation to change this is too late. In these cases + // the default animation is used. This doesn't look good for + // the camera (as it rotates the camera contents out of sync + // with physical reality). So, we ask the WindowManager to + // force the crossfade animation if an orientation change + // happens to occur during the launch. + options.setRotationAnimationHint( + WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS); + } + try { + result = ActivityManager.getService().startActivityAsUser( + null, mContext.getBasePackageName(), + intent, + intent.resolveTypeIfNeeded(mContext.getContentResolver()), + null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, + options.toBundle(), UserHandle.CURRENT.getIdentifier()); + } catch (RemoteException e) { + Log.w(TAG, "Unable to start activity", e); + } + if (callback != null) { + callback.onActivityStarted(result); } }; - Runnable cancelRunnable = new Runnable() { - @Override - public void run() { - if (callback != null) { - callback.onActivityStarted(ActivityManager.START_CANCELED); - } + Runnable cancelRunnable = () -> { + if (callback != null) { + callback.onActivityStarted(ActivityManager.START_CANCELED); } }; executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShade, @@ -3782,7 +3674,7 @@ public class StatusBar extends SystemUI implements DemoMode, }, cancelAction, afterKeyguardGone); } - private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (DEBUG) Log.v(TAG, "onReceive: " + intent); @@ -3811,7 +3703,7 @@ public class StatusBar extends SystemUI implements DemoMode, } }; - private BroadcastReceiver mDemoReceiver = new BroadcastReceiver() { + private final BroadcastReceiver mDemoReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (DEBUG) Log.v(TAG, "onReceive: " + intent); @@ -3972,7 +3864,6 @@ public class StatusBar extends SystemUI implements DemoMode, * The LEDs are turned off when the notification panel is shown, even just a little bit. * See also StatusBar.setPanelExpanded for another place where we attempt to do this. */ - // Old BaseStatusBar.handleVisibileToUserChanged private void handleVisibleToUserChangedImpl(boolean visibleToUser) { try { if (visibleToUser) { @@ -3997,8 +3888,8 @@ public class StatusBar extends SystemUI implements DemoMode, // Report all notifications as invisible and turn down the // reporter. if (!mCurrentlyVisibleNotifications.isEmpty()) { - logNotificationVisibilityChanges(Collections.<NotificationVisibility>emptyList(), - mCurrentlyVisibleNotifications); + logNotificationVisibilityChanges( + Collections.emptyList(), mCurrentlyVisibleNotifications); recycleAllVisibilityObjects(mCurrentlyVisibleNotifications); } mHandler.removeCallbacks(mVisibilityReporter); @@ -4105,7 +3996,7 @@ public class StatusBar extends SystemUI implements DemoMode, vib.vibrate(250, VIBRATION_ATTRIBUTES); } - Runnable mStartTracing = new Runnable() { + final Runnable mStartTracing = new Runnable() { @Override public void run() { vibrate(); @@ -4116,13 +4007,10 @@ public class StatusBar extends SystemUI implements DemoMode, } }; - Runnable mStopTracing = new Runnable() { - @Override - public void run() { - android.os.Debug.stopMethodTracing(); - Log.d(TAG, "stopTracing"); - vibrate(); - } + final Runnable mStopTracing = () -> { + android.os.Debug.stopMethodTracing(); + Log.d(TAG, "stopTracing"); + vibrate(); }; @Override @@ -4149,40 +4037,6 @@ public class StatusBar extends SystemUI implements DemoMode, startActivityDismissingKeyguard(intent, onlyProvisioned, true /* dismissShade */); } - private static class FastColorDrawable extends Drawable { - private final int mColor; - - public FastColorDrawable(int color) { - mColor = 0xff000000 | color; - } - - @Override - public void draw(Canvas canvas) { - canvas.drawColor(mColor, PorterDuff.Mode.SRC); - } - - @Override - public void setAlpha(int alpha) { - } - - @Override - public void setColorFilter(ColorFilter colorFilter) { - } - - @Override - public int getOpacity() { - return PixelFormat.OPAQUE; - } - - @Override - public void setBounds(int left, int top, int right, int bottom) { - } - - @Override - public void setBounds(Rect bounds) { - } - } - public void destroy() { // Begin old BaseStatusBar.destroy(). mContext.unregisterReceiver(mBaseBroadcastReceiver); @@ -4400,31 +4254,23 @@ public class StatusBar extends SystemUI implements DemoMode, Runnable endRunnable) { mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); mLaunchTransitionEndRunnable = endRunnable; - Runnable hideRunnable = new Runnable() { - @Override - public void run() { - mLaunchTransitionFadingAway = true; - if (beforeFading != null) { - beforeFading.run(); - } - mScrimController.forceHideScrims(true /* hide */, false /* animated */); - updateMediaMetaData(false, true); - mNotificationPanel.setAlpha(1); - mStackScroller.setParentNotFullyVisible(true); - mNotificationPanel.animate() - .alpha(0) - .setStartDelay(FADE_KEYGUARD_START_DELAY) - .setDuration(FADE_KEYGUARD_DURATION) - .withLayer() - .withEndAction(new Runnable() { - @Override - public void run() { - onLaunchTransitionFadingEnded(); - } - }); - mCommandQueue.appTransitionStarting(SystemClock.uptimeMillis(), - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true); - } + Runnable hideRunnable = () -> { + mLaunchTransitionFadingAway = true; + if (beforeFading != null) { + beforeFading.run(); + } + mScrimController.forceHideScrims(true /* hide */, false /* animated */); + updateMediaMetaData(false, true); + mNotificationPanel.setAlpha(1); + mStackScroller.setParentNotFullyVisible(true); + mNotificationPanel.animate() + .alpha(0) + .setStartDelay(FADE_KEYGUARD_START_DELAY) + .setDuration(FADE_KEYGUARD_DURATION) + .withLayer() + .withEndAction(this::onLaunchTransitionFadingEnded); + mCommandQueue.appTransitionStarting(SystemClock.uptimeMillis(), + LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true); }; if (mNotificationPanel.isLaunchTransitionRunning()) { mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable); @@ -4553,7 +4399,6 @@ public class StatusBar extends SystemUI implements DemoMode, // Treat Keyguard exit animation as an app transition to achieve nice transition for status // bar. - mKeyguardGoingAway = true; mKeyguardMonitor.notifyKeyguardGoingAway(true); mCommandQueue.appTransitionPending(true); } @@ -4562,14 +4407,13 @@ public class StatusBar extends SystemUI implements DemoMode, * Notifies the status bar the Keyguard is fading away with the specified timings. * * @param startTime the start time of the animations in uptime millis - * @param delay the precalculated animation delay in miliseconds + * @param delay the precalculated animation delay in milliseconds * @param fadeoutDuration the duration of the exit animation, in milliseconds */ public void setKeyguardFadingAway(long startTime, long delay, long fadeoutDuration) { mKeyguardFadingAway = true; mKeyguardFadingAwayDelay = delay; mKeyguardFadingAwayDuration = fadeoutDuration; - mWaitingForKeyguardExit = false; mCommandQueue.appTransitionStarting(startTime + fadeoutDuration - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true); @@ -4589,14 +4433,9 @@ public class StatusBar extends SystemUI implements DemoMode, */ public void finishKeyguardFadingAway() { mKeyguardFadingAway = false; - mKeyguardGoingAway = false; mKeyguardMonitor.notifyKeyguardDoneFading(); } - public void stopWaitingForKeyguardExit() { - mWaitingForKeyguardExit = false; - } - private void updatePublicMode() { final boolean showingKeyguard = mStatusBarKeyguardViewManager.isShowing(); final boolean devicePublic = showingKeyguard @@ -4810,7 +4649,6 @@ public class StatusBar extends SystemUI implements DemoMode, } protected void showBouncer() { - mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing(); mStatusBarKeyguardViewManager.dismiss(); } @@ -4827,7 +4665,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onActivated(ActivatableNotificationView view) { - onActivated((View)view); + onActivated((View) view); mStackScroller.setActivatedChild(view); } @@ -5022,6 +4860,10 @@ public class StatusBar extends SystemUI implements DemoMode, * @param expandView The view to expand after going to the shade. */ public void goToLockedShade(View expandView) { + if ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) { + return; + } + int userId = mCurrentUserId; ExpandableNotificationRow row = null; if (expandView instanceof ExpandableNotificationRow) { @@ -5129,51 +4971,41 @@ public class StatusBar extends SystemUI implements DemoMode, updateNotifications(); if (mPendingWorkRemoteInputView != null && !isAnyProfilePublicMode()) { // Expand notification panel and the notification row, then click on remote input view - final Runnable clickPendingViewRunnable = new Runnable() { - @Override - public void run() { - final View pendingWorkRemoteInputView = mPendingWorkRemoteInputView; - if (pendingWorkRemoteInputView == null) { + final Runnable clickPendingViewRunnable = () -> { + final View pendingWorkRemoteInputView = mPendingWorkRemoteInputView; + if (pendingWorkRemoteInputView == null) { + return; + } + + // Climb up the hierarchy until we get to the container for this row. + ViewParent p = pendingWorkRemoteInputView.getParent(); + while (!(p instanceof ExpandableNotificationRow)) { + if (p == null) { return; } + p = p.getParent(); + } - // Climb up the hierarchy until we get to the container for this row. - ViewParent p = pendingWorkRemoteInputView.getParent(); - while (!(p instanceof ExpandableNotificationRow)) { - if (p == null) { - return; + final ExpandableNotificationRow row = (ExpandableNotificationRow) p; + ViewParent viewParent = row.getParent(); + if (viewParent instanceof NotificationStackScrollLayout) { + final NotificationStackScrollLayout scrollLayout = + (NotificationStackScrollLayout) viewParent; + row.makeActionsVisibile(); + row.post(() -> { + final Runnable finishScrollingCallback = () -> { + mPendingWorkRemoteInputView.callOnClick(); + mPendingWorkRemoteInputView = null; + scrollLayout.setFinishScrollingCallback(null); + }; + if (scrollLayout.scrollTo(row)) { + // It scrolls! So call it when it's finished. + scrollLayout.setFinishScrollingCallback(finishScrollingCallback); + } else { + // It does not scroll, so call it now! + finishScrollingCallback.run(); } - p = p.getParent(); - } - - final ExpandableNotificationRow row = (ExpandableNotificationRow) p; - ViewParent viewParent = row.getParent(); - if (viewParent instanceof NotificationStackScrollLayout) { - final NotificationStackScrollLayout scrollLayout = - (NotificationStackScrollLayout) viewParent; - row.makeActionsVisibile(); - row.post(new Runnable() { - @Override - public void run() { - final Runnable finishScrollingCallback = new Runnable() { - @Override - public void run() { - mPendingWorkRemoteInputView.callOnClick(); - mPendingWorkRemoteInputView = null; - scrollLayout.setFinishScrollingCallback(null); - } - }; - if (scrollLayout.scrollTo(row)) { - // It scrolls! So call it when it's finished. - scrollLayout.setFinishScrollingCallback( - finishScrollingCallback); - } else { - // It does not scroll, so call it now! - finishScrollingCallback.run(); - } - } - }); - } + }); } }; mNotificationPanel.getViewTreeObserver().addOnGlobalLayoutListener( @@ -5236,7 +5068,7 @@ public class StatusBar extends SystemUI implements DemoMode, } } - WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() { + final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() { @Override public void onFinishedGoingToSleep() { mNotificationPanel.onAffordanceLaunchEnded(); @@ -5259,12 +5091,7 @@ public class StatusBar extends SystemUI implements DemoMode, // This gets executed before we will show Keyguard, so post it in order that the state // is correct. - mHandler.post(new Runnable() { - @Override - public void run() { - onCameraLaunchGestureDetected(mLastCameraLaunchSource); - } - }); + mHandler.post(() -> onCameraLaunchGestureDetected(mLastCameraLaunchSource)); } updateIsKeyguard(); } @@ -5290,7 +5117,7 @@ public class StatusBar extends SystemUI implements DemoMode, } }; - ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { + final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { @Override public void onScreenTurningOn() { mFalsingManager.onScreenTurningOn(); @@ -5487,7 +5314,7 @@ public class StatusBar extends SystemUI implements DemoMode, } private final class DozeServiceHost implements DozeHost { - private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); + private final ArrayList<Callback> mCallbacks = new ArrayList<>(); private boolean mAnimateWakeup; private boolean mIgnoreTouchWhilePulsing; @@ -5700,7 +5527,7 @@ public class StatusBar extends SystemUI implements DemoMode, protected NotificationData mNotificationData; protected NotificationStackScrollLayout mStackScroller; - protected NotificationGroupManager mGroupManager = new NotificationGroupManager(); + protected final NotificationGroupManager mGroupManager = new NotificationGroupManager(); protected RemoteInputController mRemoteInputController; @@ -5710,34 +5537,30 @@ public class StatusBar extends SystemUI implements DemoMode, private AboveShelfObserver mAboveShelfObserver; // handling reordering - protected VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager(); + protected final VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager(); protected int mCurrentUserId = 0; - final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>(); + final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>(); - protected int mLayoutDirection = -1; // invalid protected AccessibilityManager mAccessibilityManager; protected boolean mDeviceInteractive; protected boolean mVisible; - protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>(); - protected ArraySet<Entry> mRemoteInputEntriesToRemoveOnCollapse = new ArraySet<>(); + protected final ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>(); + protected final ArraySet<Entry> mRemoteInputEntriesToRemoveOnCollapse = new ArraySet<>(); /** * Notifications with keys in this set are not actually around anymore. We kept them around * when they were canceled in response to a remote input interaction. This allows us to show * what you replied and allows you to continue typing into it. */ - protected ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>(); + protected final ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>(); // mScreenOnFromKeyguard && mVisible. private boolean mVisibleToUser; - private Locale mLocale; - protected boolean mUseHeadsUp = false; - protected boolean mHeadsUpTicker = false; protected boolean mDisableNotificationAlerts = false; protected DevicePolicyManager mDevicePolicyManager; @@ -5772,13 +5595,11 @@ public class StatusBar extends SystemUI implements DemoMode, private NotificationGuts mNotificationGutsExposed; private MenuItem mGutsMenuItem; - private KeyboardShortcuts mKeyboardShortcuts; - protected NotificationShelf mNotificationShelf; protected DismissView mDismissView; protected EmptyShadeView mEmptyShadeView; - private NotificationClicker mNotificationClicker = new NotificationClicker(); + private final NotificationClicker mNotificationClicker = new NotificationClicker(); protected AssistManager mAssistManager; @@ -5838,15 +5659,14 @@ public class StatusBar extends SystemUI implements DemoMode, } }; - private RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() { + private final RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() { @Override public boolean onClickHandler( final View view, final PendingIntent pendingIntent, final Intent fillInIntent) { wakeUpIfDozing(SystemClock.uptimeMillis(), view); - - if (handleRemoteInput(view, pendingIntent, fillInIntent)) { + if (handleRemoteInput(view, pendingIntent)) { return true; } @@ -5864,33 +5684,29 @@ public class StatusBar extends SystemUI implements DemoMode, } final boolean isActivity = pendingIntent.isActivity(); if (isActivity) { - final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity( mContext, pendingIntent.getIntent(), mCurrentUserId); - dismissKeyguardThenExecute(new OnDismissAction() { - @Override - public boolean onDismiss() { - try { - ActivityManager.getService().resumeAppSwitches(); - } catch (RemoteException e) { - } - - boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent); + dismissKeyguardThenExecute(() -> { + try { + ActivityManager.getService().resumeAppSwitches(); + } catch (RemoteException e) { + } - // close the shade if it was open - if (handled && !mNotificationPanel.isFullyCollapsed()) { - animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, - true /* force */); - visibilityChanged(false); - mAssistManager.hideAssist(); + boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent); - // Wait for activity start. - return true; - } else { - return false; - } + // close the shade if it was open + if (handled && !mNotificationPanel.isFullyCollapsed()) { + animateCollapsePanels( + CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */); + visibilityChanged(false); + mAssistManager.hideAssist(); + // Wait for activity start. + return true; + } else { + return false; } + }, afterKeyguardGone); return true; } else { @@ -5932,10 +5748,15 @@ public class StatusBar extends SystemUI implements DemoMode, private boolean superOnClickHandler(View view, PendingIntent pendingIntent, Intent fillInIntent) { return super.onClickHandler(view, pendingIntent, fillInIntent, - StackId.FULLSCREEN_WORKSPACE_STACK_ID); + WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY); } - private boolean handleRemoteInput(View view, PendingIntent pendingIntent, Intent fillInIntent) { + private boolean handleRemoteInput(View view, PendingIntent pendingIntent) { + if ((mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) { + // Skip remote input as doing so will expand the notification shade. + return true; + } + Object tag = view.getTag(com.android.internal.R.id.remote_input_tag); RemoteInput[] inputs = null; if (tag instanceof RemoteInput[]) { @@ -6050,7 +5871,7 @@ public class StatusBar extends SystemUI implements DemoMode, if (Intent.ACTION_USER_SWITCHED.equals(action)) { mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); updateCurrentProfilesCache(); - if (true) Log.v(TAG, "userId " + mCurrentUserId + " is in the house"); + Log.v(TAG, "userId " + mCurrentUserId + " is in the house"); updateLockscreenNotificationSetting(); @@ -6073,8 +5894,7 @@ public class StatusBar extends SystemUI implements DemoMode, Toast toast = Toast.makeText(mContext, R.string.managed_profile_foreground_toast, Toast.LENGTH_SHORT); - TextView text = (TextView) toast.getView().findViewById( - android.R.id.message); + TextView text = toast.getView().findViewById(android.R.id.message); text.setCompoundDrawablesRelativeWithIntrinsicBounds( R.drawable.stat_sys_managed_profile_status, 0, 0, 0); int paddingPx = mContext.getResources().getDimensionPixelSize( @@ -6150,15 +5970,12 @@ public class StatusBar extends SystemUI implements DemoMode, return; } final RankingMap currentRanking = getCurrentRanking(); - mHandler.post(new Runnable() { - @Override - public void run() { - for (StatusBarNotification sbn : notifications) { - try { - addNotification(sbn, currentRanking); - } catch (InflationException e) { - handleInflationException(sbn, e); - } + mHandler.post(() -> { + for (StatusBarNotification sbn : notifications) { + try { + addNotification(sbn, currentRanking); + } catch (InflationException e) { + handleInflationException(sbn, e); } } }); @@ -6169,40 +5986,37 @@ public class StatusBar extends SystemUI implements DemoMode, final RankingMap rankingMap) { if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn); if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) { - mHandler.post(new Runnable() { - @Override - public void run() { - processForRemoteInput(sbn.getNotification()); - String key = sbn.getKey(); - mKeysKeptForRemoteInput.remove(key); - boolean isUpdate = mNotificationData.get(key) != null; - // In case we don't allow child notifications, we ignore children of - // notifications that have a summary, since we're not going to show them - // anyway. This is true also when the summary is canceled, - // because children are automatically canceled by NoMan in that case. - if (!ENABLE_CHILD_NOTIFICATIONS + mHandler.post(() -> { + processForRemoteInput(sbn.getNotification()); + String key = sbn.getKey(); + mKeysKeptForRemoteInput.remove(key); + boolean isUpdate = mNotificationData.get(key) != null; + // In case we don't allow child notifications, we ignore children of + // notifications that have a summary, since we're not going to show them + // anyway. This is true also when the summary is canceled, + // because children are automatically canceled by NoMan in that case. + if (!ENABLE_CHILD_NOTIFICATIONS && mGroupManager.isChildInGroupWithSummary(sbn)) { - if (DEBUG) { - Log.d(TAG, "Ignoring group child due to existing summary: " + sbn); - } + if (DEBUG) { + Log.d(TAG, "Ignoring group child due to existing summary: " + sbn); + } - // Remove existing notification to avoid stale data. - if (isUpdate) { - removeNotification(key, rankingMap); - } else { - mNotificationData.updateRanking(rankingMap); - } - return; + // Remove existing notification to avoid stale data. + if (isUpdate) { + removeNotification(key, rankingMap); + } else { + mNotificationData.updateRanking(rankingMap); } - try { - if (isUpdate) { - updateNotification(sbn, rankingMap); - } else { - addNotification(sbn, rankingMap); - } - } catch (InflationException e) { - handleInflationException(sbn, e); + return; + } + try { + if (isUpdate) { + updateNotification(sbn, rankingMap); + } else { + addNotification(sbn, rankingMap); } + } catch (InflationException e) { + handleInflationException(sbn, e); } }); } @@ -6250,7 +6064,7 @@ public class StatusBar extends SystemUI implements DemoMode, Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0); return; } - Log.d(TAG, "disabling lockecreen notifications and alerting the user"); + Log.d(TAG, "disabling lockscreen notifications and alerting the user"); // disable lockscreen notifications until user acts on the banner. Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0); @@ -6291,11 +6105,10 @@ public class StatusBar extends SystemUI implements DemoMode, @Override // NotificationData.Environment public boolean isNotificationForCurrentProfiles(StatusBarNotification n) { - final int thisUserId = mCurrentUserId; final int notificationUserId = n.getUserId(); if (DEBUG && MULTIUSER_DEBUG) { - Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d", - n, thisUserId, notificationUserId)); + Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d", n, + mCurrentUserId, notificationUserId)); } return isCurrentProfile(notificationUserId); } @@ -6343,21 +6156,15 @@ public class StatusBar extends SystemUI implements DemoMode, } private void startNotificationGutsIntent(final Intent intent, final int appUid) { - dismissKeyguardThenExecute(new OnDismissAction() { - @Override - public boolean onDismiss() { - AsyncTask.execute(new Runnable() { - @Override - public void run() { - TaskStackBuilder.create(mContext) - .addNextIntentWithParentStack(intent) - .startActivities(getActivityOptions(), - new UserHandle(UserHandle.getUserId(appUid))); - } - }); - animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */); - return true; - } + dismissKeyguardThenExecute(() -> { + AsyncTask.execute(() -> { + TaskStackBuilder.create(mContext) + .addNextIntentWithParentStack(intent) + .startActivities(getActivityOptions(), + new UserHandle(UserHandle.getUserId(appUid))); + }); + animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */); + return true; }, false /* afterKeyguardGone */); } @@ -6428,7 +6235,7 @@ public class StatusBar extends SystemUI implements DemoMode, startNotificationGutsIntent(intent, sbn.getUid()); }; final View.OnClickListener onDoneClick = (View v) -> { - saveAndCloseNotificationMenu(info, row, guts, v); + saveAndCloseNotificationMenu(row, guts, v); }; final NotificationInfo.CheckSaveListener checkSaveListener = (Runnable saveImportance) -> { @@ -6445,7 +6252,7 @@ public class StatusBar extends SystemUI implements DemoMode, } }; - ArraySet<NotificationChannel> channels = new ArraySet<NotificationChannel>(); + ArraySet<NotificationChannel> channels = new ArraySet<>(); channels.add(row.getEntry().channel); if (row.isSummaryWithChildren()) { // If this is a summary, then add in the children notification channels for the @@ -6473,7 +6280,7 @@ public class StatusBar extends SystemUI implements DemoMode, } } - private void saveAndCloseNotificationMenu(NotificationInfo info, + private void saveAndCloseNotificationMenu( ExpandableNotificationRow row, NotificationGuts guts, View done) { guts.resetFalsingCheck(); int[] rowLocation = new int[2]; @@ -6642,13 +6449,6 @@ public class StatusBar extends SystemUI implements DemoMode, updateHideIconsForBouncer(true /* animate */); } - protected void sendCloseSystemWindows(String reason) { - try { - ActivityManager.getService().closeSystemDialogs(reason); - } catch (RemoteException e) { - } - } - protected void toggleKeyboardShortcuts(int deviceId) { KeyboardShortcuts.toggle(mContext, deviceId); } @@ -6750,18 +6550,6 @@ public class StatusBar extends SystemUI implements DemoMode, return isLockscreenPublicMode(userId); } - public void onNotificationClear(StatusBarNotification notification) { - try { - mBarService.onNotificationClear( - notification.getPackageName(), - notification.getTag(), - notification.getId(), - notification.getUserId()); - } catch (android.os.RemoteException ex) { - // oh well - } - } - /** * Called when the notification panel layouts */ @@ -6919,49 +6707,42 @@ public class StatusBar extends SystemUI implements DemoMode, public void startPendingIntentDismissingKeyguard(final PendingIntent intent) { if (!isDeviceProvisioned()) return; - final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); final boolean afterKeyguardGone = intent.isActivity() && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(), mCurrentUserId); - dismissKeyguardThenExecute(new OnDismissAction() { - @Override - public boolean onDismiss() { - new Thread() { - @Override - public void run() { - try { - // The intent we are sending is for the application, which - // won't have permission to immediately start an activity after - // the user switches to home. We know it is safe to do at this - // point, so make sure new activity switches are now allowed. - ActivityManager.getService().resumeAppSwitches(); - } catch (RemoteException e) { - } - try { - intent.send(null, 0, null, null, null, null, getActivityOptions()); - } catch (PendingIntent.CanceledException e) { - // the stack trace isn't very helpful here. - // Just log the exception message. - Log.w(TAG, "Sending intent failed: " + e); + dismissKeyguardThenExecute(() -> { + new Thread(() -> { + try { + // The intent we are sending is for the application, which + // won't have permission to immediately start an activity after + // the user switches to home. We know it is safe to do at this + // point, so make sure new activity switches are now allowed. + ActivityManager.getService().resumeAppSwitches(); + } catch (RemoteException e) { + } + try { + intent.send(null, 0, null, null, null, null, getActivityOptions()); + } catch (PendingIntent.CanceledException e) { + // the stack trace isn't very helpful here. + // Just log the exception message. + Log.w(TAG, "Sending intent failed: " + e); - // TODO: Dismiss Keyguard. - } - if (intent.isActivity()) { - mAssistManager.hideAssist(); - } - } - }.start(); + // TODO: Dismiss Keyguard. + } + if (intent.isActivity()) { + mAssistManager.hideAssist(); + } + }).start(); - if (!mNotificationPanel.isFullyCollapsed()) { - // close the shade if it was open - animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, - true /* force */, true /* delayed */); - visibilityChanged(false); + if (!mNotificationPanel.isFullyCollapsed()) { + // close the shade if it was open + animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */, + true /* delayed */); + visibilityChanged(false); - return true; - } else { - return false; - } + return true; + } else { + return false; } }, afterKeyguardGone); } @@ -6999,130 +6780,110 @@ public class StatusBar extends SystemUI implements DemoMode, // Mark notification for one frame. row.setJustClicked(true); - DejankUtils.postAfterTraversal(new Runnable() { - @Override - public void run() { - row.setJustClicked(false); - } - }); + DejankUtils.postAfterTraversal(() -> row.setJustClicked(false)); final boolean afterKeyguardGone = intent.isActivity() && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(), mCurrentUserId); - dismissKeyguardThenExecute(new OnDismissAction() { - @Override - public boolean onDismiss() { - if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) { - // Release the HUN notification to the shade. + dismissKeyguardThenExecute(() -> { + if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) { + // Release the HUN notification to the shade. - if (isPanelFullyCollapsed()) { - HeadsUpManager.setIsClickedNotification(row, true); - } - // - // In most cases, when FLAG_AUTO_CANCEL is set, the notification will - // become canceled shortly by NoMan, but we can't assume that. - mHeadsUpManager.releaseImmediately(notificationKey); + if (isPanelFullyCollapsed()) { + HeadsUpManager.setIsClickedNotification(row, true); } - StatusBarNotification parentToCancel = null; - if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) { - StatusBarNotification summarySbn = mGroupManager.getLogicalGroupSummary(sbn) - .getStatusBarNotification(); - if (shouldAutoCancel(summarySbn)) { - parentToCancel = summarySbn; - } + // + // In most cases, when FLAG_AUTO_CANCEL is set, the notification will + // become canceled shortly by NoMan, but we can't assume that. + mHeadsUpManager.releaseImmediately(notificationKey); + } + StatusBarNotification parentToCancel = null; + if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) { + StatusBarNotification summarySbn = + mGroupManager.getLogicalGroupSummary(sbn).getStatusBarNotification(); + if (shouldAutoCancel(summarySbn)) { + parentToCancel = summarySbn; } - final StatusBarNotification parentToCancelFinal = parentToCancel; - final Runnable runnable = new Runnable() { - @Override - public void run() { - try { - // The intent we are sending is for the application, which - // won't have permission to immediately start an activity after - // the user switches to home. We know it is safe to do at this - // point, so make sure new activity switches are now allowed. - ActivityManager.getService().resumeAppSwitches(); - } catch (RemoteException e) { - } - if (intent != null) { - // If we are launching a work activity and require to launch - // separate work challenge, we defer the activity action and cancel - // notification until work challenge is unlocked. - if (intent.isActivity()) { - final int userId = intent.getCreatorUserHandle() - .getIdentifier(); - if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId) - && mKeyguardManager.isDeviceLocked(userId)) { - // TODO(b/28935539): should allow certain activities to - // bypass work challenge - if (startWorkChallengeIfNecessary(userId, - intent.getIntentSender(), notificationKey)) { - // Show work challenge, do not run PendingIntent and - // remove notification - return; - } - } - } - try { - intent.send(null, 0, null, null, null, null, - getActivityOptions()); - } catch (PendingIntent.CanceledException e) { - // the stack trace isn't very helpful here. - // Just log the exception message. - Log.w(TAG, "Sending contentIntent failed: " + e); - - // TODO: Dismiss Keyguard. - } - if (intent.isActivity()) { - mAssistManager.hideAssist(); + } + final StatusBarNotification parentToCancelFinal = parentToCancel; + final Runnable runnable = () -> { + try { + // The intent we are sending is for the application, which + // won't have permission to immediately start an activity after + // the user switches to home. We know it is safe to do at this + // point, so make sure new activity switches are now allowed. + ActivityManager.getService().resumeAppSwitches(); + } catch (RemoteException e) { + } + if (intent != null) { + // If we are launching a work activity and require to launch + // separate work challenge, we defer the activity action and cancel + // notification until work challenge is unlocked. + if (intent.isActivity()) { + final int userId = intent.getCreatorUserHandle().getIdentifier(); + if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId) + && mKeyguardManager.isDeviceLocked(userId)) { + // TODO(b/28935539): should allow certain activities to + // bypass work challenge + if (startWorkChallengeIfNecessary(userId, intent.getIntentSender(), + notificationKey)) { + // Show work challenge, do not run PendingIntent and + // remove notification + return; } } + } + try { + intent.send(null, 0, null, null, null, null, getActivityOptions()); + } catch (PendingIntent.CanceledException e) { + // the stack trace isn't very helpful here. + // Just log the exception message. + Log.w(TAG, "Sending contentIntent failed: " + e); - try { - mBarService.onNotificationClick(notificationKey); - } catch (RemoteException ex) { - // system process is dead if we're here. - } - if (parentToCancelFinal != null) { - // We have to post it to the UI thread for synchronization - mHandler.post(new Runnable() { - @Override - public void run() { - Runnable removeRunnable = new Runnable() { - @Override - public void run() { - performRemoveNotification(parentToCancelFinal); - } - }; - if (isCollapsing()) { - // To avoid lags we're only performing the remove - // after the shade was collapsed - addPostCollapseAction(removeRunnable); - } else { - removeRunnable.run(); - } - } - }); - } + // TODO: Dismiss Keyguard. + } + if (intent.isActivity()) { + mAssistManager.hideAssist(); } - }; + } - if (mStatusBarKeyguardViewManager.isShowing() - && mStatusBarKeyguardViewManager.isOccluded()) { - mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable); - } else { - new Thread(runnable).start(); + try { + mBarService.onNotificationClick(notificationKey); + } catch (RemoteException ex) { + // system process is dead if we're here. + } + if (parentToCancelFinal != null) { + // We have to post it to the UI thread for synchronization + mHandler.post(() -> { + Runnable removeRunnable = + () -> performRemoveNotification(parentToCancelFinal); + if (isCollapsing()) { + // To avoid lags we're only performing the remove + // after the shade was collapsed + addPostCollapseAction(removeRunnable); + } else { + removeRunnable.run(); + } + }); } + }; - if (!mNotificationPanel.isFullyCollapsed()) { - // close the shade if it was open - animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, - true /* force */, true /* delayed */); - visibilityChanged(false); + if (mStatusBarKeyguardViewManager.isShowing() + && mStatusBarKeyguardViewManager.isOccluded()) { + mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable); + } else { + new Thread(runnable).start(); + } - return true; - } else { - return false; - } + if (!mNotificationPanel.isFullyCollapsed()) { + // close the shade if it was open + animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */, + true /* delayed */); + visibilityChanged(false); + + return true; + } else { + return false; } }, afterKeyguardGone); } @@ -7149,10 +6910,10 @@ public class StatusBar extends SystemUI implements DemoMode, } protected Bundle getActivityOptions() { - // Anything launched from the notification shade should always go into the - // fullscreen stack. - ActivityOptions options = ActivityOptions.makeBasic(); - options.setLaunchStackId(StackId.FULLSCREEN_WORKSPACE_STACK_ID); + // Anything launched from the notification shade should always go into the secondary + // split-screen windowing mode. + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY); return options.toBundle(); } diff --git a/com/android/systemui/statusbar/phone/StatusBarIconController.java b/com/android/systemui/statusbar/phone/StatusBarIconController.java index c2407652..bcda60eb 100644 --- a/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -14,10 +14,10 @@ package com.android.systemui.statusbar.phone; -import android.annotation.ColorInt; +import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS; +import static android.app.StatusBarManager.DISABLE_NONE; + import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Color; import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import android.util.ArraySet; @@ -29,11 +29,11 @@ import android.widget.LinearLayout; import android.widget.LinearLayout.LayoutParams; import com.android.internal.statusbar.StatusBarIcon; -import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.policy.DarkIconDispatcher; +import com.android.systemui.util.Utils.DisableStateTracker; public interface StatusBarIconController { @@ -149,6 +149,14 @@ public interface StatusBarIconController { mContext = group.getContext(); mIconSize = mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.status_bar_icon_size); + + DisableStateTracker tracker = + new DisableStateTracker(DISABLE_NONE, DISABLE2_SYSTEM_ICONS); + mGroup.addOnAttachStateChangeListener(tracker); + if (mGroup.isAttachedToWindow()) { + // In case we miss the first onAttachedToWindow event + tracker.onViewAttachedToWindow(mGroup); + } } protected void onIconAdded(int index, String slot, boolean blocked, diff --git a/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java index 68f8e065..1c3ee758 100644 --- a/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java +++ b/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java @@ -33,7 +33,6 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; -import com.android.systemui.statusbar.policy.DarkIconDispatcher; import com.android.systemui.statusbar.policy.IconLogger; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; @@ -50,21 +49,17 @@ import java.util.ArrayList; public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable, ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController { - private final DarkIconDispatcher mDarkIconDispatcher; - - private Context mContext; - private DemoStatusIcons mDemoStatusIcons; - private final ArrayList<IconManager> mIconGroups = new ArrayList<>(); - private final ArraySet<String> mIconBlacklist = new ArraySet<>(); private final IconLogger mIconLogger = Dependency.get(IconLogger.class); + private Context mContext; + private DemoStatusIcons mDemoStatusIcons; + public StatusBarIconControllerImpl(Context context) { super(context.getResources().getStringArray( com.android.internal.R.array.config_statusBarIcons)); Dependency.get(ConfigurationController.class).addCallback(this); - mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class); mContext = context; loadDimens(); diff --git a/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index bbce751d..09828dcd 100644 --- a/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -225,7 +225,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (mShowing) { if (mOccluded && !mDozing) { mStatusBar.hideKeyguard(); - mStatusBar.stopWaitingForKeyguardExit(); if (hideBouncerWhenShowing || mBouncer.needsFullscreenBouncer()) { hideBouncer(false /* destroyView */); } diff --git a/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java index c0a68373..0d21c4ef 100644 --- a/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java +++ b/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java @@ -20,7 +20,6 @@ import android.app.ActivityManager; import android.content.Context; import android.content.Intent; import android.net.wifi.WifiManager.ActionListener; -import android.os.Looper; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -59,13 +58,19 @@ public class AccessPointControllerImpl private int mCurrentUser; - public AccessPointControllerImpl(Context context, Looper bgLooper) { + public AccessPointControllerImpl(Context context) { mContext = context; mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - mWifiTracker = new WifiTracker(context, this, bgLooper, false, true); + mWifiTracker = new WifiTracker(context, this, false, true); mCurrentUser = ActivityManager.getCurrentUser(); } + @Override + protected void finalize() throws Throwable { + super.finalize(); + mWifiTracker.onDestroy(); + } + public boolean canConfigWifi() { return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, new UserHandle(mCurrentUser)); @@ -81,7 +86,7 @@ public class AccessPointControllerImpl if (DEBUG) Log.d(TAG, "addCallback " + callback); mCallbacks.add(callback); if (mCallbacks.size() == 1) { - mWifiTracker.startTracking(); + mWifiTracker.onStart(); } } @@ -91,7 +96,7 @@ public class AccessPointControllerImpl if (DEBUG) Log.d(TAG, "removeCallback " + callback); mCallbacks.remove(callback); if (mCallbacks.isEmpty()) { - mWifiTracker.stopTracking(); + mWifiTracker.onStop(); } } diff --git a/com/android/systemui/statusbar/policy/CallbackHandler.java b/com/android/systemui/statusbar/policy/CallbackHandler.java index a456786d..5159e8d0 100644 --- a/com/android/systemui/statusbar/policy/CallbackHandler.java +++ b/com/android/systemui/statusbar/policy/CallbackHandler.java @@ -71,7 +71,7 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa break; case MSG_NO_SIM_VISIBLE_CHANGED: for (SignalCallback signalCluster : mSignalCallbacks) { - signalCluster.setNoSims(msg.arg1 != 0); + signalCluster.setNoSims(msg.arg1 != 0, msg.arg2 != 0); } break; case MSG_ETHERNET_CHANGED: @@ -144,8 +144,8 @@ public class CallbackHandler extends Handler implements EmergencyListener, Signa } @Override - public void setNoSims(boolean show) { - obtainMessage(MSG_NO_SIM_VISIBLE_CHANGED, show ? 1 : 0, 0).sendToTarget(); + public void setNoSims(boolean show, boolean simDetected) { + obtainMessage(MSG_NO_SIM_VISIBLE_CHANGED, show ? 1 : 0, simDetected ? 1 : 0).sendToTarget(); } @Override diff --git a/com/android/systemui/statusbar/policy/NetworkController.java b/com/android/systemui/statusbar/policy/NetworkController.java index 2771011c..9eee906b 100644 --- a/com/android/systemui/statusbar/policy/NetworkController.java +++ b/com/android/systemui/statusbar/policy/NetworkController.java @@ -52,7 +52,7 @@ public interface NetworkController extends CallbackController<SignalCallback>, D int qsType, boolean activityIn, boolean activityOut, String typeContentDescription, String description, boolean isWide, int subId, boolean roaming) {} default void setSubs(List<SubscriptionInfo> subs) {} - default void setNoSims(boolean show) {} + default void setNoSims(boolean show, boolean simDetected) {} default void setEthernetIndicators(IconState icon) {} diff --git a/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index c217bda9..d24e51c7 100644 --- a/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -58,10 +58,8 @@ import java.util.ArrayList; import java.util.BitSet; import java.util.Collections; import java.util.Comparator; -import java.util.HashMap; import java.util.List; import java.util.Locale; -import java.util.Map; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; @@ -116,7 +114,7 @@ public class NetworkControllerImpl extends BroadcastReceiver // States that don't belong to a subcontroller. private boolean mAirplaneMode = false; - private boolean mHasNoSims; + private boolean mHasNoSubs; private Locale mLocale = null; // This list holds our ordering. private List<SubscriptionInfo> mCurrentSubscriptions = new ArrayList<>(); @@ -140,6 +138,7 @@ public class NetworkControllerImpl extends BroadcastReceiver @VisibleForTesting ServiceState mLastServiceState; private boolean mUserSetup; + private boolean mSimDetected; /** * Construct this controller object and register for updates. @@ -151,7 +150,7 @@ public class NetworkControllerImpl extends BroadcastReceiver (WifiManager) context.getSystemService(Context.WIFI_SERVICE), SubscriptionManager.from(context), Config.readConfig(context), bgLooper, new CallbackHandler(), - new AccessPointControllerImpl(context, bgLooper), + new AccessPointControllerImpl(context), new DataUsageController(context), new SubscriptionDefaults(), deviceProvisionedController); @@ -363,7 +362,7 @@ public class NetworkControllerImpl extends BroadcastReceiver cb.setSubs(mCurrentSubscriptions); cb.setIsAirplaneMode(new IconState(mAirplaneMode, TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext)); - cb.setNoSims(mHasNoSims); + cb.setNoSims(mHasNoSubs, mSimDetected); mWifiSignalController.notifyListeners(cb); mEthernetSignalController.notifyListeners(cb); for (int i = 0; i < mMobileSignalControllers.size(); i++) { @@ -498,11 +497,25 @@ public class NetworkControllerImpl extends BroadcastReceiver @VisibleForTesting protected void updateNoSims() { - boolean hasNoSims = mHasMobileDataFeature && mMobileSignalControllers.size() == 0; - if (hasNoSims != mHasNoSims) { - mHasNoSims = hasNoSims; - mCallbackHandler.setNoSims(mHasNoSims); + boolean hasNoSubs = mHasMobileDataFeature && mMobileSignalControllers.size() == 0; + boolean simDetected = hasAnySim(); + if (hasNoSubs != mHasNoSubs || simDetected != mSimDetected) { + mHasNoSubs = hasNoSubs; + mSimDetected = simDetected; + mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected); + } + } + + private boolean hasAnySim() { + int simCount = mPhone.getSimCount(); + for (int i = 0; i < simCount; i++) { + int state = mPhone.getSimState(i); + if (state != TelephonyManager.SIM_STATE_ABSENT + && state != TelephonyManager.SIM_STATE_UNKNOWN) { + return true; + } } + return false; } @VisibleForTesting @@ -631,7 +644,7 @@ public class NetworkControllerImpl extends BroadcastReceiver private void notifyListeners() { mCallbackHandler.setIsAirplaneMode(new IconState(mAirplaneMode, TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext)); - mCallbackHandler.setNoSims(mHasNoSims); + mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected); } /** @@ -804,6 +817,10 @@ public class NetworkControllerImpl extends BroadcastReceiver } else { mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_NONE); } + String ssid = args.getString("ssid"); + if (ssid != null) { + mDemoWifiState.ssid = ssid; + } mDemoWifiState.enabled = show; mWifiSignalController.notifyListeners(); } @@ -822,8 +839,8 @@ public class NetworkControllerImpl extends BroadcastReceiver } String nosim = args.getString("nosim"); if (nosim != null) { - mHasNoSims = nosim.equals("show"); - mCallbackHandler.setNoSims(mHasNoSims); + mHasNoSubs = nosim.equals("show"); + mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected); } String mobile = args.getString("mobile"); if (mobile != null) { diff --git a/com/android/systemui/util/Utils.java b/com/android/systemui/util/Utils.java index f4aebae7..eca61277 100644 --- a/com/android/systemui/util/Utils.java +++ b/com/android/systemui/util/Utils.java @@ -14,6 +14,11 @@ package com.android.systemui.util; +import android.view.View; + +import com.android.systemui.SysUiServiceProvider; +import com.android.systemui.statusbar.CommandQueue; + import java.util.List; import java.util.function.Consumer; @@ -28,4 +33,52 @@ public class Utils { c.accept(list.get(i)); } } + + /** + * Sets the visibility of an UI element according to the DISABLE_* flags in + * {@link android.app.StatusBarManager}. + */ + public static class DisableStateTracker implements CommandQueue.Callbacks, + View.OnAttachStateChangeListener { + private final int mMask1; + private final int mMask2; + private View mView; + private boolean mDisabled; + + public DisableStateTracker(int disableMask, int disable2Mask) { + mMask1 = disableMask; + mMask2 = disable2Mask; + } + + @Override + public void onViewAttachedToWindow(View v) { + mView = v; + SysUiServiceProvider.getComponent(v.getContext(), CommandQueue.class) + .addCallbacks(this); + } + + @Override + public void onViewDetachedFromWindow(View v) { + SysUiServiceProvider.getComponent(mView.getContext(), CommandQueue.class) + .removeCallbacks(this); + mView = null; + } + + /** + * Sets visibility of this {@link View} given the states passed from + * {@link com.android.systemui.statusbar.CommandQueue.Callbacks#disable(int, int)}. + */ + @Override + public void disable(int state1, int state2, boolean animate) { + final boolean disabled = ((state1 & mMask1) != 0) || ((state2 & mMask2) != 0); + if (disabled == mDisabled) return; + mDisabled = disabled; + mView.setVisibility(disabled ? View.GONE : View.VISIBLE); + } + + /** @return {@code true} if and only if this {@link View} is currently disabled */ + public boolean isDisabled() { + return mDisabled; + } + } } diff --git a/com/android/systemui/volume/ZenModePanel.java b/com/android/systemui/volume/ZenModePanel.java index a3aca6e1..7bb987ca 100644 --- a/com/android/systemui/volume/ZenModePanel.java +++ b/com/android/systemui/volume/ZenModePanel.java @@ -524,18 +524,17 @@ public class ZenModePanel extends FrameLayout { bindGenericCountdown(); bindNextAlarm(getTimeUntilNextAlarmCondition()); } else if (isForever(c)) { + getConditionTagAt(FOREVER_CONDITION_INDEX).rb.setChecked(true); bindGenericCountdown(); bindNextAlarm(getTimeUntilNextAlarmCondition()); } else { if (isAlarm(c)) { bindGenericCountdown(); - bindNextAlarm(c); getConditionTagAt(COUNTDOWN_ALARM_CONDITION_INDEX).rb.setChecked(true); } else if (isCountdown(c)) { bindNextAlarm(getTimeUntilNextAlarmCondition()); - bind(c, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX), COUNTDOWN_CONDITION_INDEX); getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true); @@ -568,8 +567,8 @@ public class ZenModePanel extends FrameLayout { tag = (ConditionTag) alarmContent.getTag(); boolean showAlarm = tag != null && tag.condition != null; mZenRadioGroup.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX).setVisibility( - showAlarm ? View.VISIBLE : View.GONE); - alarmContent.setVisibility(showAlarm ? View.VISIBLE : View.GONE); + showAlarm ? View.VISIBLE : View.INVISIBLE); + alarmContent.setVisibility(showAlarm ? View.VISIBLE : View.INVISIBLE); } private Condition forever() { diff --git a/com/android/uiautomator/testrunner/UiAutomatorTestCase.java b/com/android/uiautomator/testrunner/UiAutomatorTestCase.java index 7c9aeded..3d5476d0 100644 --- a/com/android/uiautomator/testrunner/UiAutomatorTestCase.java +++ b/com/android/uiautomator/testrunner/UiAutomatorTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2012 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. @@ -16,24 +16,55 @@ package com.android.uiautomator.testrunner; -import android.app.Instrumentation; +import android.content.Context; import android.os.Bundle; +import android.os.RemoteException; +import android.os.ServiceManager; import android.os.SystemClock; -import android.test.InstrumentationTestCase; +import android.view.inputmethod.InputMethodInfo; -import com.android.uiautomator.core.InstrumentationUiAutomatorBridge; +import com.android.internal.view.IInputMethodManager; import com.android.uiautomator.core.UiDevice; +import junit.framework.TestCase; + +import java.util.List; + /** - * UI Automator test case that is executed on the device. + * UI automation test should extend this class. This class provides access + * to the following: + * {@link UiDevice} instance + * {@link Bundle} for command line parameters. + * @since API Level 16 * @deprecated New tests should be written using UI Automator 2.0 which is available as part of the * Android Testing Support Library. */ @Deprecated -public class UiAutomatorTestCase extends InstrumentationTestCase { +public class UiAutomatorTestCase extends TestCase { + private static final String DISABLE_IME = "disable_ime"; + private static final String DUMMY_IME_PACKAGE = "com.android.testing.dummyime"; + private UiDevice mUiDevice; private Bundle mParams; private IAutomationSupport mAutomationSupport; + private boolean mShouldDisableIme = false; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mShouldDisableIme = "true".equals(mParams.getString(DISABLE_IME)); + if (mShouldDisableIme) { + setDummyIme(); + } + } + + @Override + protected void tearDown() throws Exception { + if (mShouldDisableIme) { + restoreActiveIme(); + } + super.tearDown(); + } /** * Get current instance of {@link UiDevice}. Works similar to calling the static @@ -41,7 +72,7 @@ public class UiAutomatorTestCase extends InstrumentationTestCase { * @since API Level 16 */ public UiDevice getUiDevice() { - return UiDevice.getInstance(); + return mUiDevice; } /** @@ -54,43 +85,34 @@ public class UiAutomatorTestCase extends InstrumentationTestCase { return mParams; } - void setAutomationSupport(IAutomationSupport automationSupport) { - mAutomationSupport = automationSupport; - } - /** * Provides support for running tests to report interim status * * @return IAutomationSupport * @since API Level 16 - * @deprecated Use {@link Instrumentation#sendStatus(int, Bundle)} instead */ public IAutomationSupport getAutomationSupport() { - if (mAutomationSupport == null) { - mAutomationSupport = new InstrumentationAutomationSupport(getInstrumentation()); - } return mAutomationSupport; } /** - * Initializes this test case. - * - * @param params Instrumentation arguments. + * package private + * @param uiDevice */ - void initialize(Bundle params) { - mParams = params; + void setUiDevice(UiDevice uiDevice) { + mUiDevice = uiDevice; + } - // check if this is a monkey test mode - String monkeyVal = mParams.getString("monkey"); - if (monkeyVal != null) { - // only if the monkey key is specified, we alter the state of monkey - // else we should leave things as they are. - getInstrumentation().getUiAutomation().setRunAsMonkey(Boolean.valueOf(monkeyVal)); - } + /** + * package private + * @param params + */ + void setParams(Bundle params) { + mParams = params; + } - UiDevice.getInstance().initialize(new InstrumentationUiAutomatorBridge( - getInstrumentation().getContext(), - getInstrumentation().getUiAutomation())); + void setAutomationSupport(IAutomationSupport automationSupport) { + mAutomationSupport = automationSupport; } /** @@ -101,4 +123,28 @@ public class UiAutomatorTestCase extends InstrumentationTestCase { public void sleep(long ms) { SystemClock.sleep(ms); } + + private void setDummyIme() throws RemoteException { + IInputMethodManager im = IInputMethodManager.Stub.asInterface(ServiceManager + .getService(Context.INPUT_METHOD_SERVICE)); + List<InputMethodInfo> infos = im.getInputMethodList(); + String id = null; + for (InputMethodInfo info : infos) { + if (DUMMY_IME_PACKAGE.equals(info.getComponent().getPackageName())) { + id = info.getId(); + } + } + if (id == null) { + throw new RuntimeException(String.format( + "Required testing fixture missing: IME package (%s)", DUMMY_IME_PACKAGE)); + } + im.setInputMethod(null, id); + } + + private void restoreActiveIme() throws RemoteException { + // TODO: figure out a way to restore active IME + // Currently retrieving active IME requires querying secure settings provider, which is hard + // to do without a Context; so the caveat here is that to make the post test device usable, + // the active IME needs to be manually switched. + } } diff --git a/com/android/webview/nullwebview/NullWebViewFactoryProvider.java b/com/android/webview/nullwebview/NullWebViewFactoryProvider.java deleted file mode 100644 index ed12446d..00000000 --- a/com/android/webview/nullwebview/NullWebViewFactoryProvider.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2014 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.webview.nullwebview; - -import android.content.Context; -import android.webkit.CookieManager; -import android.webkit.GeolocationPermissions; -import android.webkit.ServiceWorkerController; -import android.webkit.TokenBindingService; -import android.webkit.WebIconDatabase; -import android.webkit.WebStorage; -import android.webkit.WebView; -import android.webkit.WebViewDatabase; -import android.webkit.WebViewDelegate; -import android.webkit.WebViewFactoryProvider; -import android.webkit.WebViewProvider; - -public class NullWebViewFactoryProvider implements WebViewFactoryProvider { - - public static WebViewFactoryProvider create(WebViewDelegate delegate) { - return new NullWebViewFactoryProvider(delegate); - } - - public NullWebViewFactoryProvider(WebViewDelegate delegate) { - } - - @Override - public WebViewFactoryProvider.Statics getStatics() { - throw new UnsupportedOperationException(); - } - - @Override - public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) { - throw new UnsupportedOperationException(); - } - - @Override - public GeolocationPermissions getGeolocationPermissions() { - throw new UnsupportedOperationException(); - } - - @Override - public CookieManager getCookieManager() { - throw new UnsupportedOperationException(); - } - - @Override - public TokenBindingService getTokenBindingService() { - throw new UnsupportedOperationException(); - } - - @Override - public ServiceWorkerController getServiceWorkerController() { - throw new UnsupportedOperationException(); - } - - @Override - public WebIconDatabase getWebIconDatabase() { - throw new UnsupportedOperationException(); - } - - @Override - public WebStorage getWebStorage() { - throw new UnsupportedOperationException(); - } - - @Override - public WebViewDatabase getWebViewDatabase(Context context) { - throw new UnsupportedOperationException(); - } -} |