diff options
80 files changed, 2508 insertions, 389 deletions
diff --git a/cmds/svc/src/com/android/commands/svc/NfcCommand.java b/cmds/svc/src/com/android/commands/svc/NfcCommand.java new file mode 100644 index 000000000000..e0f09ee2c666 --- /dev/null +++ b/cmds/svc/src/com/android/commands/svc/NfcCommand.java @@ -0,0 +1,85 @@ +/* + * 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.commands.svc; + +import android.content.Context; +import android.content.pm.IPackageManager; +import android.content.pm.PackageManager; +import android.nfc.INfcAdapter; +import android.os.RemoteException; +import android.os.ServiceManager; + +public class NfcCommand extends Svc.Command { + + public NfcCommand() { + super("nfc"); + } + + @Override + public String shortHelp() { + return "Control NFC functions"; + } + + @Override + public String longHelp() { + return shortHelp() + "\n" + + "\n" + + "usage: svc nfc [enable|disable]\n" + + " Turn NFC on or off.\n\n"; + } + + @Override + public void run(String[] args) { + boolean validCommand = false; + if (args.length >= 2) { + boolean flag = false; + if ("enable".equals(args[1])) { + flag = true; + validCommand = true; + } else if ("disable".equals(args[1])) { + flag = false; + validCommand = true; + } + if (validCommand) { + IPackageManager pm = IPackageManager.Stub.asInterface( + ServiceManager.getService("package")); + try { + if (pm.hasSystemFeature(PackageManager.FEATURE_NFC)) { + INfcAdapter nfc = INfcAdapter.Stub + .asInterface(ServiceManager.getService(Context.NFC_SERVICE)); + try { + if (flag) { + nfc.enable(); + } else + nfc.disable(true); + } catch (RemoteException e) { + System.err.println("NFC operation failed: " + e); + } + } else { + System.err.println("NFC feature not supported."); + } + } catch (RemoteException e) { + System.err.println("RemoteException while calling PackageManager, is the " + + "system running?"); + } + return; + } + } + System.err.println(longHelp()); + } + +} diff --git a/cmds/svc/src/com/android/commands/svc/Svc.java b/cmds/svc/src/com/android/commands/svc/Svc.java index 0fbba11e927d..2cccd1a4dc92 100644 --- a/cmds/svc/src/com/android/commands/svc/Svc.java +++ b/cmds/svc/src/com/android/commands/svc/Svc.java @@ -95,6 +95,7 @@ public class Svc { new PowerCommand(), new DataCommand(), new WifiCommand(), - new UsbCommand() + new UsbCommand(), + new NfcCommand(), }; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index da21eaffa95b..fd88a0549a10 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4235,6 +4235,11 @@ public final class ActivityThread { configDiff = mConfiguration.updateFrom(config); config = applyCompatConfiguration(mCurDefaultDisplayDpi); + + final Theme systemTheme = getSystemContext().getTheme(); + if ((systemTheme.getChangingConfigurations() & configDiff) != 0) { + systemTheme.rebase(); + } } ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config); diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 87d52e4ee07e..12c26323296d 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1604,6 +1604,23 @@ public class Intent implements Parcelable, Cloneable { = "android.intent.action.GET_PERMISSIONS_COUNT"; /** + * Broadcast action that requests list of all apps that have runtime permissions. It will + * respond to the request by sending a broadcast with action defined by + * {@link #EXTRA_GET_PERMISSIONS_PACKAGES_RESPONSE_INTENT}. The response will contain + * {@link #EXTRA_GET_PERMISSIONS_APP_LIST_RESULT}, as well as + * {@link #EXTRA_GET_PERMISSIONS_APP_LABEL_LIST_RESULT}, with contents described below or + * a null upon failure. + * + * <p>{@link #EXTRA_GET_PERMISSIONS_APP_LIST_RESULT} will contain a list of package names of + * apps that have runtime permissions. {@link #EXTRA_GET_PERMISSIONS_APP_LABEL_LIST_RESULT} + * will contain the list of app labels corresponding ot the apps in the first list. + * + * @hide + */ + public static final String ACTION_GET_PERMISSIONS_PACKAGES + = "android.intent.action.GET_PERMISSIONS_PACKAGES"; + + /** * Extra included in response to {@link #ACTION_GET_PERMISSIONS_COUNT}. * @hide */ @@ -1618,6 +1635,20 @@ public class Intent implements Parcelable, Cloneable { = "android.intent.extra.GET_PERMISSIONS_GROUP_LIST_RESULT"; /** + * String list of apps that have one or more runtime permissions. + * @hide + */ + public static final String EXTRA_GET_PERMISSIONS_APP_LIST_RESULT + = "android.intent.extra.GET_PERMISSIONS_APP_LIST_RESULT"; + + /** + * String list of app labels for apps that have one or more runtime permissions. + * @hide + */ + public static final String EXTRA_GET_PERMISSIONS_APP_LABEL_LIST_RESULT + = "android.intent.extra.GET_PERMISSIONS_APP_LABEL_LIST_RESULT"; + + /** * Required extra to be sent with {@link #ACTION_GET_PERMISSIONS_COUNT} broadcasts. * @hide */ @@ -1625,6 +1656,13 @@ public class Intent implements Parcelable, Cloneable { = "android.intent.extra.GET_PERMISSIONS_RESONSE_INTENT"; /** + * Required extra to be sent with {@link #ACTION_GET_PERMISSIONS_PACKAGES} broadcasts. + * @hide + */ + public static final String EXTRA_GET_PERMISSIONS_PACKAGES_RESPONSE_INTENT + = "android.intent.extra.GET_PERMISSIONS_PACKAGES_RESONSE_INTENT"; + + /** * Activity action: Launch UI to manage which apps have a given permission. * <p> * Input: {@link #EXTRA_PERMISSION_NAME} specifies the permission access diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index ed7a2a3ea30d..7032c9a861cf 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -4520,6 +4520,17 @@ public class PackageParser { return applicationInfo.isUpdatedSystemApp(); } + /** + * @hide + */ + public boolean canHaveOatDir() { + // The following app types CANNOT have oat directory + // - non-updated system apps + // - forward-locked apps or apps installed in ASEC containers + return (!isSystemApp() || isUpdatedSystemApp()) + && !isForwardLocked() && !applicationInfo.isExternalAsec(); + } + public String toString() { return "Package{" + Integer.toHexString(System.identityHashCode(this)) diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 731903c158be..82724587d15d 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1438,10 +1438,12 @@ public class Resources { * if not already defined in the theme. */ public void applyStyle(int resId, boolean force) { - AssetManager.applyThemeStyle(mTheme, resId, force); + synchronized (mKey) { + AssetManager.applyThemeStyle(mTheme, resId, force); - mThemeResId = resId; - mKey.append(resId, force); + mThemeResId = resId; + mKey.append(resId, force); + } } /** @@ -1454,10 +1456,14 @@ public class Resources { * @param other The existing Theme to copy from. */ public void setTo(Theme other) { - AssetManager.copyTheme(mTheme, other.mTheme); + synchronized (mKey) { + synchronized (other.mKey) { + AssetManager.copyTheme(mTheme, other.mTheme); - mThemeResId = other.mThemeResId; - mKey.setTo(other.getKey()); + mThemeResId = other.mThemeResId; + mKey.setTo(other.getKey()); + } + } } /** @@ -1480,11 +1486,13 @@ public class Resources { * @see #obtainStyledAttributes(AttributeSet, int[], int, int) */ public TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) { - final int len = attrs.length; - final TypedArray array = TypedArray.obtain(Resources.this, len); - array.mTheme = this; - AssetManager.applyStyle(mTheme, 0, 0, 0, attrs, array.mData, array.mIndices); - return array; + synchronized (mKey) { + final int len = attrs.length; + final TypedArray array = TypedArray.obtain(Resources.this, len); + array.mTheme = this; + AssetManager.applyStyle(mTheme, 0, 0, 0, attrs, array.mData, array.mIndices); + return array; + } } /** @@ -1494,7 +1502,7 @@ public class Resources { * <p>Be sure to call {@link TypedArray#recycle() TypedArray.recycle()} when you are done * with the array. * - * @param resid The desired style resource. + * @param resId The desired style resource. * @param attrs The desired attributes in the style. * * @throws NotFoundException Throws NotFoundException if the given ID does not exist. @@ -1507,39 +1515,15 @@ public class Resources { * @see #obtainStyledAttributes(int[]) * @see #obtainStyledAttributes(AttributeSet, int[], int, int) */ - public TypedArray obtainStyledAttributes(@StyleRes int resid, @StyleableRes int[] attrs) + public TypedArray obtainStyledAttributes(@StyleRes int resId, @StyleableRes int[] attrs) throws NotFoundException { - final int len = attrs.length; - final TypedArray array = TypedArray.obtain(Resources.this, len); - array.mTheme = this; - if (false) { - int[] data = array.mData; - - System.out.println("**********************************************************"); - System.out.println("**********************************************************"); - System.out.println("**********************************************************"); - System.out.println("Attributes:"); - String s = " Attrs:"; - int i; - for (i=0; i<attrs.length; i++) { - s = s + " 0x" + Integer.toHexString(attrs[i]); - } - System.out.println(s); - s = " Found:"; - TypedValue value = new TypedValue(); - for (i=0; i<attrs.length; i++) { - int d = i*AssetManager.STYLE_NUM_ENTRIES; - value.type = data[d+AssetManager.STYLE_TYPE]; - value.data = data[d+AssetManager.STYLE_DATA]; - value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE]; - value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID]; - s = s + " 0x" + Integer.toHexString(attrs[i]) - + "=" + value; - } - System.out.println(s); + synchronized (mKey) { + final int len = attrs.length; + final TypedArray array = TypedArray.obtain(Resources.this, len); + array.mTheme = this; + AssetManager.applyStyle(mTheme, 0, resId, 0, attrs, array.mData, array.mIndices); + return array; } - AssetManager.applyStyle(mTheme, 0, resid, 0, attrs, array.mData, array.mIndices); - return array; } /** @@ -1592,50 +1576,23 @@ public class Resources { */ public TypedArray obtainStyledAttributes(AttributeSet set, @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { - final int len = attrs.length; - final TypedArray array = TypedArray.obtain(Resources.this, len); - - // XXX note that for now we only work with compiled XML files. - // To support generic XML files we will need to manually parse - // out the attributes from the XML file (applying type information - // contained in the resources and such). - final XmlBlock.Parser parser = (XmlBlock.Parser)set; - AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes, - parser != null ? parser.mParseState : 0, attrs, array.mData, array.mIndices); + synchronized (mKey) { + final int len = attrs.length; + final TypedArray array = TypedArray.obtain(Resources.this, len); - array.mTheme = this; - array.mXml = parser; + // XXX note that for now we only work with compiled XML files. + // To support generic XML files we will need to manually parse + // out the attributes from the XML file (applying type information + // contained in the resources and such). + final XmlBlock.Parser parser = (XmlBlock.Parser) set; + AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes, + parser != null ? parser.mParseState : 0, + attrs, array.mData, array.mIndices); + array.mTheme = this; + array.mXml = parser; - if (false) { - int[] data = array.mData; - - System.out.println("Attributes:"); - String s = " Attrs:"; - int i; - for (i=0; i<set.getAttributeCount(); i++) { - s = s + " " + set.getAttributeName(i); - int id = set.getAttributeNameResource(i); - if (id != 0) { - s = s + "(0x" + Integer.toHexString(id) + ")"; - } - s = s + "=" + set.getAttributeValue(i); - } - System.out.println(s); - s = " Found:"; - TypedValue value = new TypedValue(); - for (i=0; i<attrs.length; i++) { - int d = i*AssetManager.STYLE_NUM_ENTRIES; - value.type = data[d+AssetManager.STYLE_TYPE]; - value.data = data[d+AssetManager.STYLE_DATA]; - value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE]; - value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID]; - s = s + " 0x" + Integer.toHexString(attrs[i]) - + "=" + value; - } - System.out.println(s); + return array; } - - return array; } /** @@ -1654,18 +1611,20 @@ public class Resources { */ @NonNull public TypedArray resolveAttributes(@NonNull int[] values, @NonNull int[] attrs) { - final int len = attrs.length; - if (values == null || len != values.length) { - throw new IllegalArgumentException( - "Base attribute values must the same length as attrs"); - } + synchronized (mKey) { + final int len = attrs.length; + if (values == null || len != values.length) { + throw new IllegalArgumentException( + "Base attribute values must the same length as attrs"); + } - final TypedArray array = TypedArray.obtain(Resources.this, len); - AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); - array.mTheme = this; - array.mXml = null; + final TypedArray array = TypedArray.obtain(Resources.this, len); + AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices); + array.mTheme = this; + array.mXml = null; - return array; + return array; + } } /** @@ -1686,14 +1645,9 @@ public class Resources { * <var>outValue</var> is valid, else false. */ public boolean resolveAttribute(int resid, TypedValue outValue, boolean resolveRefs) { - boolean got = mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs); - if (false) { - System.out.println( - "resolveAttribute #" + Integer.toHexString(resid) - + " got=" + got + ", type=0x" + Integer.toHexString(outValue.type) - + ", data=0x" + Integer.toHexString(outValue.data)); + synchronized (mKey) { + return mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs); } - return got; } /** @@ -1739,8 +1693,11 @@ public class Resources { * @see ActivityInfo */ public int getChangingConfigurations() { - final int nativeChangingConfig = AssetManager.getThemeChangingConfigurations(mTheme); - return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig); + synchronized (mKey) { + final int nativeChangingConfig = + AssetManager.getThemeChangingConfigurations(mTheme); + return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig); + } } /** @@ -1751,7 +1708,9 @@ public class Resources { * @param prefix Text to prefix each line printed. */ public void dump(int priority, String tag, String prefix) { - AssetManager.dumpTheme(mTheme, priority, tag, prefix); + synchronized (mKey) { + AssetManager.dumpTheme(mTheme, priority, tag, prefix); + } } @Override @@ -1801,19 +1760,21 @@ public class Resources { */ @ViewDebug.ExportedProperty(category = "theme", hasAdjacentMapping = true) public String[] getTheme() { - final int N = mKey.mCount; - final String[] themes = new String[N * 2]; - for (int i = 0, j = N - 1; i < themes.length; i += 2, --j) { - final int resId = mKey.mResId[j]; - final boolean forced = mKey.mForce[j]; - try { - themes[i] = getResourceName(resId); - } catch (NotFoundException e) { - themes[i] = Integer.toHexString(i); + synchronized (mKey) { + final int N = mKey.mCount; + final String[] themes = new String[N * 2]; + for (int i = 0, j = N - 1; i < themes.length; i += 2, --j) { + final int resId = mKey.mResId[j]; + final boolean forced = mKey.mForce[j]; + try { + themes[i] = getResourceName(resId); + } catch (NotFoundException e) { + themes[i] = Integer.toHexString(i); + } + themes[i + 1] = forced ? "forced" : "not forced"; } - themes[i + 1] = forced ? "forced" : "not forced"; + return themes; } - return themes; } /** @hide */ @@ -1834,13 +1795,15 @@ public class Resources { * @hide */ public void rebase() { - AssetManager.clearTheme(mTheme); - - // Reapply the same styles in the same order. - for (int i = 0; i < mKey.mCount; i++) { - final int resId = mKey.mResId[i]; - final boolean force = mKey.mForce[i]; - AssetManager.applyThemeStyle(mTheme, resId, force); + synchronized (mKey) { + AssetManager.clearTheme(mTheme); + + // Reapply the same styles in the same order. + for (int i = 0; i < mKey.mCount; i++) { + final int resId = mKey.mResId[i]; + final boolean force = mKey.mForce[i]; + AssetManager.applyThemeStyle(mTheme, resId, force); + } } } } diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 21ba7bda493e..121a187b3639 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -359,6 +359,14 @@ public final class DisplayManagerGlobal { } } + public void requestColorTransform(int displayId, int colorTransformId) { + try { + mDm.requestColorTransform(displayId, colorTransformId); + } catch (RemoteException ex) { + Log.e(TAG, "Failed to request color transform.", ex); + } + } + public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection, String name, int width, int height, int densityDpi, Surface surface, int flags, VirtualDisplay.Callback callback, Handler handler) { diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 4486dd4e0887..8a1abf18602a 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -59,6 +59,9 @@ interface IDisplayManager { // No permissions required. WifiDisplayStatus getWifiDisplayStatus(); + // Requires CONFIGURE_DISPLAY_COLOR_TRANSFORM + void requestColorTransform(int displayId, int colorTransformId); + // Requires CAPTURE_VIDEO_OUTPUT, CAPTURE_SECURE_VIDEO_OUTPUT, or an appropriate // MediaProjection token for certain combinations of flags. int createVirtualDisplay(in IVirtualDisplayCallback callback, diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 7fef5e17c5cb..122df2394b6b 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -775,7 +775,7 @@ public class FingerprintManager { if (fingerId != reqFingerId) { Log.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId); } - if (fingerId != reqFingerId) { + if (groupId != reqGroupId) { Log.w(TAG, "Group id didn't match: " + groupId + " != " + reqGroupId); } mRemovalCallback.onRemovalSucceeded(mRemovalFingerprint); diff --git a/core/java/android/net/IpReachabilityMonitor.java b/core/java/android/net/IpReachabilityMonitor.java index 88fb01437cd0..2283004c7759 100644 --- a/core/java/android/net/IpReachabilityMonitor.java +++ b/core/java/android/net/IpReachabilityMonitor.java @@ -18,6 +18,7 @@ package android.net; import com.android.internal.annotations.GuardedBy; +import android.content.Context; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.LinkProperties.ProvisioningChange; @@ -31,6 +32,7 @@ import android.net.netlink.RtNetlinkNeighborMessage; import android.net.netlink.StructNdaCacheInfo; import android.net.netlink.StructNdMsg; import android.net.netlink.StructNlMsgHdr; +import android.os.PowerManager; import android.os.SystemClock; import android.system.ErrnoException; import android.system.NetlinkSocketAddress; @@ -74,6 +76,7 @@ public class IpReachabilityMonitor { } private final Object mLock = new Object(); + private final PowerManager.WakeLock mWakeLock; private final String mInterfaceName; private final int mInterfaceIndex; private final Callback mCallback; @@ -136,7 +139,8 @@ public class IpReachabilityMonitor { return returnValue; } - public IpReachabilityMonitor(String ifName, Callback callback) throws IllegalArgumentException { + public IpReachabilityMonitor(Context context, String ifName, Callback callback) + throws IllegalArgumentException { mInterfaceName = ifName; int ifIndex = -1; try { @@ -145,6 +149,8 @@ public class IpReachabilityMonitor { } catch (SocketException | NullPointerException e) { throw new IllegalArgumentException("invalid interface '" + ifName + "': ", e); } + mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock( + PowerManager.PARTIAL_WAKE_LOCK, TAG + "." + mInterfaceName); mCallback = callback; mNetlinkSocketObserver = new NetlinkSocketObserver(); mObserverThread = new Thread(mNetlinkSocketObserver); @@ -291,6 +297,17 @@ public class IpReachabilityMonitor { synchronized (mLock) { ipProbeList.addAll(mIpWatchList.keySet()); } + + if (!ipProbeList.isEmpty() && stillRunning()) { + // Keep the CPU awake long enough to allow all ARP/ND + // probes a reasonable chance at success. See b/23197666. + // + // The wakelock we use is (by default) refcounted, and this version + // of acquire(timeout) queues a release message to keep acquisitions + // and releases balanced. + mWakeLock.acquire(getProbeWakeLockDuration()); + } + for (InetAddress target : ipProbeList) { if (!stillRunning()) { break; @@ -299,6 +316,22 @@ public class IpReachabilityMonitor { } } + private long getProbeWakeLockDuration() { + // Ideally, this would be computed by examining the values of: + // + // /proc/sys/net/ipv[46]/neigh/<ifname>/ucast_solicit + // + // and: + // + // /proc/sys/net/ipv[46]/neigh/<ifname>/retrans_time_ms + // + // For now, just make some assumptions. + final long numUnicastProbes = 3; + final long retransTimeMs = 1000; + final long gracePeriodMs = 500; + return (numUnicastProbes * retransTimeMs) + gracePeriodMs; + } + // TODO: simply the number of objects by making this extend Thread. private final class NetlinkSocketObserver implements Runnable { diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index bad94fce20aa..8e86a53072e7 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -463,13 +463,15 @@ public abstract class BatteryStats implements Parcelable { public abstract long getCpuPowerMaUs(int which); /** - * Returns the approximate cpu time (in milliseconds) spent at a certain CPU speed. + * Returns the approximate cpu time (in milliseconds) spent at a certain CPU speed for a + * given CPU cluster. + * @param cluster the index of the CPU cluster. * @param step the index of the CPU speed. This is not the actual speed of the CPU. * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT. - * @see BatteryStats#getCpuSpeedSteps() + * @see PowerProfile.getNumCpuClusters() + * @see PowerProfile.getNumSpeedStepsInCpuCluster(int) */ - @Deprecated - public abstract long getTimeAtCpuSpeed(int step, int which); + public abstract long getTimeAtCpuSpeed(int cluster, int step, int which); public static abstract class Sensor { /* @@ -2276,9 +2278,6 @@ public abstract class BatteryStats implements Parcelable { public abstract Map<String, ? extends Timer> getKernelWakelockStats(); - /** Returns the number of different speeds that the CPU can run at */ - public abstract int getCpuSpeedSteps(); - public abstract void writeToParcelWithoutUids(Parcel out, int flags); private final static void formatTimeRaw(StringBuilder out, long seconds) { diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java index 70cff00636ac..b6d0fcb448f5 100644 --- a/core/java/android/os/PowerManagerInternal.java +++ b/core/java/android/os/PowerManagerInternal.java @@ -53,6 +53,15 @@ public abstract class PowerManagerInternal { */ public static final int WAKEFULNESS_DOZING = 3; + + /** + * Power hint: The user is interacting with the device. The corresponding data field must be + * the expected duration of the fling, or 0 if unknown. + * + * This must be kept in sync with the values in hardware/libhardware/include/hardware/power.h + */ + public static final int POWER_HINT_INTERACTION = 2; + public static String wakefulnessToString(int wakefulness) { switch (wakefulness) { case WAKEFULNESS_ASLEEP: @@ -148,4 +157,6 @@ public abstract class PowerManagerInternal { public abstract void updateUidProcState(int uid, int procState); public abstract void uidGone(int uid); + + public abstract void powerHint(int hintId, int data); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 501b771ec67a..33f94763608d 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3283,7 +3283,6 @@ public final class Settings { DOCK_SOUNDS_ENABLED, // moved to global LOCKSCREEN_SOUNDS_ENABLED, SHOW_WEB_SUGGESTIONS, - NOTIFICATION_LIGHT_PULSE, SIP_CALL_OPTIONS, SIP_RECEIVE_CALLS, POINTER_SPEED, diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 35c41928f214..1269ad93d306 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -16,7 +16,10 @@ package android.view; +import android.annotation.RequiresPermission; +import android.content.Context; import android.content.res.CompatibilityInfo; +import android.content.res.Resources; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; @@ -30,6 +33,8 @@ import android.util.Log; import java.util.Arrays; +import static android.Manifest.permission.CONFIGURE_DISPLAY_COLOR_TRANSFORM; + /** * Provides information about the size and density of a logical display. * <p> @@ -679,6 +684,49 @@ public final class Display { } /** + * Request the display applies a color transform. + * @hide + */ + @RequiresPermission(CONFIGURE_DISPLAY_COLOR_TRANSFORM) + public void requestColorTransform(ColorTransform colorTransform) { + mGlobal.requestColorTransform(mDisplayId, colorTransform.getId()); + } + + /** + * Returns the active color transform of this display + * @hide + */ + public ColorTransform getColorTransform() { + synchronized (this) { + updateDisplayInfoLocked(); + return mDisplayInfo.getColorTransform(); + } + } + + /** + * Returns the default color transform of this display + * @hide + */ + public ColorTransform getDefaultColorTransform() { + synchronized (this) { + updateDisplayInfoLocked(); + return mDisplayInfo.getDefaultColorTransform(); + } + } + + /** + * Gets the supported color transforms of this device. + * @hide + */ + public ColorTransform[] getSupportedColorTransforms() { + synchronized (this) { + updateDisplayInfoLocked(); + ColorTransform[] transforms = mDisplayInfo.supportedColorTransforms; + return Arrays.copyOf(transforms, transforms.length); + } + } + + /** * Gets the app VSYNC offset, in nanoseconds. This is a positive value indicating * the phase offset of the VSYNC events provided by Choreographer relative to the * display refresh. For example, if Choreographer reports that the refresh occurred @@ -1054,4 +1102,89 @@ public final class Display { } }; } + + /** + * A color transform supported by a given display. + * + * @see Display#getSupportedColorTransforms() + * @hide + */ + public static final class ColorTransform implements Parcelable { + public static final ColorTransform[] EMPTY_ARRAY = new ColorTransform[0]; + + private final int mId; + private final int mColorTransform; + + public ColorTransform(int id, int colorTransform) { + mId = id; + mColorTransform = colorTransform; + } + + public int getId() { + return mId; + } + + public int getColorTransform() { + return mColorTransform; + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof ColorTransform)) { + return false; + } + ColorTransform that = (ColorTransform) other; + return mId == that.mId + && mColorTransform == that.mColorTransform; + } + + @Override + public int hashCode() { + int hash = 1; + hash = hash * 17 + mId; + hash = hash * 17 + mColorTransform; + return hash; + } + + @Override + public String toString() { + return new StringBuilder("{") + .append("id=").append(mId) + .append(", colorTransform=").append(mColorTransform) + .append("}") + .toString(); + } + + @Override + public int describeContents() { + return 0; + } + + private ColorTransform(Parcel in) { + this(in.readInt(), in.readInt()); + } + + @Override + public void writeToParcel(Parcel out, int parcelableFlags) { + out.writeInt(mId); + out.writeInt(mColorTransform); + } + + @SuppressWarnings("hiding") + public static final Parcelable.Creator<ColorTransform> CREATOR + = new Parcelable.Creator<ColorTransform>() { + @Override + public ColorTransform createFromParcel(Parcel in) { + return new ColorTransform(in); + } + + @Override + public ColorTransform[] newArray(int size) { + return new ColorTransform[size]; + } + }; + } } diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index cf1799040f5f..ee76274ca5a6 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -169,6 +169,15 @@ public final class DisplayInfo implements Parcelable { */ public Display.Mode[] supportedModes = Display.Mode.EMPTY_ARRAY; + /** The active color transform. */ + public int colorTransformId; + + /** The default color transform. */ + public int defaultColorTransformId; + + /** The list of supported color transforms */ + public Display.ColorTransform[] supportedColorTransforms = Display.ColorTransform.EMPTY_ARRAY; + /** * The logical display density which is the basis for density-independent * pixels. @@ -279,6 +288,8 @@ public final class DisplayInfo implements Parcelable { && rotation == other.rotation && modeId == other.modeId && defaultModeId == other.defaultModeId + && colorTransformId == other.colorTransformId + && defaultColorTransformId == other.defaultColorTransformId && logicalDensityDpi == other.logicalDensityDpi && physicalXDpi == other.physicalXDpi && physicalYDpi == other.physicalYDpi @@ -317,6 +328,10 @@ public final class DisplayInfo implements Parcelable { modeId = other.modeId; defaultModeId = other.defaultModeId; supportedModes = Arrays.copyOf(other.supportedModes, other.supportedModes.length); + colorTransformId = other.colorTransformId; + defaultColorTransformId = other.defaultColorTransformId; + supportedColorTransforms = Arrays.copyOf( + other.supportedColorTransforms, other.supportedColorTransforms.length); logicalDensityDpi = other.logicalDensityDpi; physicalXDpi = other.physicalXDpi; physicalYDpi = other.physicalYDpi; @@ -353,6 +368,13 @@ public final class DisplayInfo implements Parcelable { for (int i = 0; i < nModes; i++) { supportedModes[i] = Display.Mode.CREATOR.createFromParcel(source); } + colorTransformId = source.readInt(); + defaultColorTransformId = source.readInt(); + int nColorTransforms = source.readInt(); + supportedColorTransforms = new Display.ColorTransform[nColorTransforms]; + for (int i = 0; i < nColorTransforms; i++) { + supportedColorTransforms[i] = Display.ColorTransform.CREATOR.createFromParcel(source); + } logicalDensityDpi = source.readInt(); physicalXDpi = source.readFloat(); physicalYDpi = source.readFloat(); @@ -390,6 +412,12 @@ public final class DisplayInfo implements Parcelable { for (int i = 0; i < supportedModes.length; i++) { supportedModes[i].writeToParcel(dest, flags); } + dest.writeInt(colorTransformId); + dest.writeInt(defaultColorTransformId); + dest.writeInt(supportedColorTransforms.length); + for (int i = 0; i < supportedColorTransforms.length; i++) { + supportedColorTransforms[i].writeToParcel(dest, flags); + } dest.writeInt(logicalDensityDpi); dest.writeFloat(physicalXDpi); dest.writeFloat(physicalYDpi); @@ -461,6 +489,24 @@ public final class DisplayInfo implements Parcelable { return result; } + public Display.ColorTransform getColorTransform() { + return findColorTransform(colorTransformId); + } + + public Display.ColorTransform getDefaultColorTransform() { + return findColorTransform(defaultColorTransformId); + } + + private Display.ColorTransform findColorTransform(int colorTransformId) { + for (int i = 0; i < supportedColorTransforms.length; i++) { + Display.ColorTransform colorTransform = supportedColorTransforms[i]; + if (colorTransform.getId() == colorTransformId) { + return colorTransform; + } + } + throw new IllegalStateException("Unable to locate color transform: " + colorTransformId); + } + public void getAppMetrics(DisplayMetrics outMetrics) { getAppMetrics(outMetrics, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null); } @@ -562,6 +608,12 @@ public final class DisplayInfo implements Parcelable { sb.append(defaultModeId); sb.append(", modes "); sb.append(Arrays.toString(supportedModes)); + sb.append(", colorTransformId "); + sb.append(colorTransformId); + sb.append(", defaultColorTransformId "); + sb.append(defaultColorTransformId); + sb.append(", supportedColorTransforms "); + sb.append(Arrays.toString(supportedColorTransforms)); sb.append(", rotation "); sb.append(rotation); sb.append(", density "); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 3b7f6aa0dc21..43c578fe7faf 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -7391,7 +7391,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @hide */ public void getOutsets(Rect outOutsetRect) { - outOutsetRect.set(mAttachInfo.mOutsets); + if (mAttachInfo != null) { + outOutsetRect.set(mAttachInfo.mOutsets); + } else { + outOutsetRect.setEmpty(); + } } /** diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index ed858e7a9836..6e9a41857851 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -2395,6 +2395,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te lp.itemId = mAdapter.getItemId(position); } lp.viewType = mAdapter.getItemViewType(position); + lp.isEnabled = mAdapter.isEnabled(position); if (lp != vlp) { child.setLayoutParams(lp); } @@ -2416,19 +2417,33 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } final int position = getPositionForView(host); - final ListAdapter adapter = getAdapter(); - - if ((position == INVALID_POSITION) || (adapter == null)) { + if (position == INVALID_POSITION || mAdapter == null) { // Cannot perform actions on invalid items. return false; } - if (!isEnabled() || !adapter.isEnabled(position)) { - // Cannot perform actions on disabled items. + if (position >= mAdapter.getCount()) { + // The position is no longer valid, likely due to a data set + // change. We could fail here for all data set changes, since + // there is a chance that the data bound to the view may no + // longer exist at the same position within the adapter, but + // it's more consistent with the standard touch interaction to + // click at whatever may have moved into that position. return false; } - final long id = getItemIdAtPosition(position); + final boolean isItemEnabled; + final ViewGroup.LayoutParams lp = host.getLayoutParams(); + if (lp instanceof AbsListView.LayoutParams) { + isItemEnabled = ((AbsListView.LayoutParams) lp).isEnabled; + } else { + isItemEnabled = false; + } + + if (!isEnabled() || !isItemEnabled) { + // Cannot perform actions on disabled items. + return false; + } switch (action) { case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: { @@ -2445,11 +2460,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } return false; case AccessibilityNodeInfo.ACTION_CLICK: { if (isClickable()) { + final long id = getItemIdAtPosition(position); return performItemClick(host, position, id); } } return false; case AccessibilityNodeInfo.ACTION_LONG_CLICK: { if (isLongClickable()) { + final long id = getItemIdAtPosition(position); return performLongPress(host, position, id); } } return false; @@ -2469,13 +2486,20 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te */ public void onInitializeAccessibilityNodeInfoForItem( View view, int position, AccessibilityNodeInfo info) { - final ListAdapter adapter = getAdapter(); - if (position == INVALID_POSITION || adapter == null) { + if (position == INVALID_POSITION) { // The item doesn't exist, so there's not much we can do here. return; } - if (!isEnabled() || !adapter.isEnabled(position)) { + final boolean isItemEnabled; + final ViewGroup.LayoutParams lp = view.getLayoutParams(); + if (lp instanceof AbsListView.LayoutParams) { + isItemEnabled = ((AbsListView.LayoutParams) lp).isEnabled; + } else { + isItemEnabled = false; + } + + if (!isEnabled() || !isItemEnabled) { info.setEnabled(false); return; } @@ -6315,6 +6339,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te */ long itemId = -1; + /** Whether the adapter considers the item enabled. */ + boolean isEnabled; + public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); } @@ -6340,6 +6367,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te encoder.addProperty("list:viewType", viewType); encoder.addProperty("list:recycledHeaderFooter", recycledHeaderFooter); encoder.addProperty("list:forceAdd", forceAdd); + encoder.addProperty("list:isEnabled", isEnabled); } } diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index 0cc1b25d19b7..2cfefba10c57 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -600,13 +600,20 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { } /** - * Get the position within the adapter's data set for the view, where view is a an adapter item - * or a descendant of an adapter item. + * Returns the position within the adapter's data set for the view, where + * view is a an adapter item or a descendant of an adapter item. + * <p> + * <strong>Note:</strong> The result of this method only reflects the + * position of the data bound to <var>view</var> during the most recent + * layout pass. If the adapter's data set has changed without a subsequent + * layout pass, the position returned by this method may not match the + * current position of the data within the adapter. * - * @param view an adapter item, or a descendant of an adapter item. This must be visible in this - * AdapterView at the time of the call. - * @return the position within the adapter's data set of the view, or {@link #INVALID_POSITION} - * if the view does not correspond to a list item (or it is not currently visible). + * @param view an adapter item, or a descendant of an adapter item. This + * must be visible in this AdapterView at the time of the call. + * @return the position within the adapter's data set of the view, or + * {@link #INVALID_POSITION} if the view does not correspond to a + * list item (or it is not currently visible) */ public int getPositionForView(View view) { View listItem = view; diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index f994d4ad91be..607e955d5698 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -1070,6 +1070,7 @@ public class GridView extends AbsListView { child.setLayoutParams(p); } p.viewType = mAdapter.getItemViewType(0); + p.isEnabled = mAdapter.isEnabled(0); p.forceAdd = true; int childHeightSpec = getChildMeasureSpec( @@ -1480,6 +1481,7 @@ public class GridView extends AbsListView { p = (AbsListView.LayoutParams) generateDefaultLayoutParams(); } p.viewType = mAdapter.getItemViewType(position); + p.isEnabled = mAdapter.isEnabled(position); if (recycled && !p.forceAdd) { attachViewToParent(child, where, p); diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index c5632ec91fe4..00d017f9f576 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -1200,6 +1200,7 @@ public class ListView extends AbsListView { child.setLayoutParams(p); } p.viewType = mAdapter.getItemViewType(position); + p.isEnabled = mAdapter.isEnabled(position); p.forceAdd = true; final int childWidthSpec = ViewGroup.getChildMeasureSpec(widthMeasureSpec, @@ -1913,6 +1914,7 @@ public class ListView extends AbsListView { p = (AbsListView.LayoutParams) generateDefaultLayoutParams(); } p.viewType = mAdapter.getItemViewType(position); + p.isEnabled = mAdapter.isEnabled(position); if ((recycled && !p.forceAdd) || (p.recycledHeaderFooter && p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER)) { diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index d9faece9485f..3219dcb5ad8b 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -104,6 +104,7 @@ public class ChooserActivity extends ResolverActivity { sri.resultTargets); } unbindService(sri.connection); + sri.connection.destroy(); mServiceConnections.remove(sri.connection); if (mServiceConnections.isEmpty()) { mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); @@ -208,6 +209,8 @@ public class ChooserActivity extends ResolverActivity { mRefinementResultReceiver.destroy(); mRefinementResultReceiver = null; } + unbindRemainingServices(); + mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_RESULT); } @Override @@ -265,6 +268,11 @@ public class ChooserActivity extends ResolverActivity { return true; } + @Override + boolean shouldAutoLaunchSingleChoice() { + return false; + } + private void modifyTargetIntent(Intent in) { final String action = in.getAction(); if (Intent.ACTION_SEND.equals(action) || @@ -371,7 +379,8 @@ public class ChooserActivity extends ResolverActivity { continue; } - final ChooserTargetServiceConnection conn = new ChooserTargetServiceConnection(dri); + final ChooserTargetServiceConnection conn = + new ChooserTargetServiceConnection(this, dri); if (bindServiceAsUser(serviceIntent, conn, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND, UserHandle.CURRENT)) { if (DEBUG) { @@ -425,6 +434,7 @@ public class ChooserActivity extends ResolverActivity { final ChooserTargetServiceConnection conn = mServiceConnections.get(i); if (DEBUG) Log.d(TAG, "unbinding " + conn); unbindService(conn); + conn.destroy(); } mServiceConnections.clear(); mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); @@ -637,7 +647,8 @@ public class ChooserActivity extends ResolverActivity { @Override public CharSequence getExtendedInfo() { - return mSourceInfo != null ? mSourceInfo.getExtendedInfo() : null; + // ChooserTargets have badge icons, so we won't show the extended info to disambiguate. + return null; } @Override @@ -730,9 +741,8 @@ public class ChooserActivity extends ResolverActivity { @Override public boolean showsExtendedInfo(TargetInfo info) { - // Reserve space to show extended info if any one of the items in the adapter has - // extended info. This keeps grid item sizes uniform. - return hasExtendedInfo(); + // We have badges so we don't need this text shown. + return false; } @Override @@ -1024,54 +1034,93 @@ public class ChooserActivity extends ResolverActivity { } } - class ChooserTargetServiceConnection implements ServiceConnection { + static class ChooserTargetServiceConnection implements ServiceConnection { private final DisplayResolveInfo mOriginalTarget; + private ComponentName mConnectedComponent; + private ChooserActivity mChooserActivity; + private final Object mLock = new Object(); private final IChooserTargetResult mChooserTargetResult = new IChooserTargetResult.Stub() { @Override public void sendResult(List<ChooserTarget> targets) throws RemoteException { - filterServiceTargets(mOriginalTarget.getResolveInfo().activityInfo.packageName, - targets); - final Message msg = Message.obtain(); - msg.what = CHOOSER_TARGET_SERVICE_RESULT; - msg.obj = new ServiceResultInfo(mOriginalTarget, targets, - ChooserTargetServiceConnection.this); - mChooserHandler.sendMessage(msg); + synchronized (mLock) { + if (mChooserActivity == null) { + Log.e(TAG, "destroyed ChooserTargetServiceConnection received result from " + + mConnectedComponent + "; ignoring..."); + return; + } + mChooserActivity.filterServiceTargets( + mOriginalTarget.getResolveInfo().activityInfo.packageName, targets); + final Message msg = Message.obtain(); + msg.what = CHOOSER_TARGET_SERVICE_RESULT; + msg.obj = new ServiceResultInfo(mOriginalTarget, targets, + ChooserTargetServiceConnection.this); + mChooserActivity.mChooserHandler.sendMessage(msg); + } } }; - public ChooserTargetServiceConnection(DisplayResolveInfo dri) { + public ChooserTargetServiceConnection(ChooserActivity chooserActivity, + DisplayResolveInfo dri) { + mChooserActivity = chooserActivity; mOriginalTarget = dri; } @Override public void onServiceConnected(ComponentName name, IBinder service) { if (DEBUG) Log.d(TAG, "onServiceConnected: " + name); - final IChooserTargetService icts = IChooserTargetService.Stub.asInterface(service); - try { - icts.getChooserTargets(mOriginalTarget.getResolvedComponentName(), - mOriginalTarget.getResolveInfo().filter, mChooserTargetResult); - } catch (RemoteException e) { - Log.e(TAG, "Querying ChooserTargetService " + name + " failed.", e); - unbindService(this); - mServiceConnections.remove(this); + synchronized (mLock) { + if (mChooserActivity == null) { + Log.e(TAG, "destroyed ChooserTargetServiceConnection got onServiceConnected"); + return; + } + + final IChooserTargetService icts = IChooserTargetService.Stub.asInterface(service); + try { + icts.getChooserTargets(mOriginalTarget.getResolvedComponentName(), + mOriginalTarget.getResolveInfo().filter, mChooserTargetResult); + } catch (RemoteException e) { + Log.e(TAG, "Querying ChooserTargetService " + name + " failed.", e); + mChooserActivity.unbindService(this); + destroy(); + mChooserActivity.mServiceConnections.remove(this); + } } } @Override public void onServiceDisconnected(ComponentName name) { if (DEBUG) Log.d(TAG, "onServiceDisconnected: " + name); - unbindService(this); - mServiceConnections.remove(this); - if (mServiceConnections.isEmpty()) { - mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); - sendVoiceChoicesIfNeeded(); + synchronized (mLock) { + if (mChooserActivity == null) { + Log.e(TAG, + "destroyed ChooserTargetServiceConnection got onServiceDisconnected"); + return; + } + + mChooserActivity.unbindService(this); + destroy(); + mChooserActivity.mServiceConnections.remove(this); + if (mChooserActivity.mServiceConnections.isEmpty()) { + mChooserActivity.mChooserHandler.removeMessages( + CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); + mChooserActivity.sendVoiceChoicesIfNeeded(); + } + mConnectedComponent = null; + } + } + + public void destroy() { + synchronized (mLock) { + mChooserActivity = null; } } @Override public String toString() { - return mOriginalTarget.getResolveInfo().activityInfo.toString(); + return "ChooserTargetServiceConnection{service=" + + mConnectedComponent + ", activity=" + + mOriginalTarget.getResolveInfo().activityInfo.toString() + "}"; } } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 7dd3bed079fc..ef9d1cebff57 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -234,7 +234,9 @@ public class ResolverActivity extends Activity { mResolverComparator = new ResolverComparator(this, getTargetIntent(), referrerPackage); - configureContentView(mIntents, initialIntents, rList, alwaysUseOption); + if (configureContentView(mIntents, initialIntents, rList, alwaysUseOption)) { + return; + } // Prevent the Resolver window from becoming the top fullscreen window and thus from taking // control of the system bars. @@ -794,6 +796,10 @@ public class ResolverActivity extends Activity { return false; } + boolean shouldAutoLaunchSingleChoice() { + return true; + } + void showAppDetails(ResolveInfo ri) { Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) .setData(Uri.fromParts("package", ri.activityInfo.packageName, null)) @@ -808,7 +814,10 @@ public class ResolverActivity extends Activity { launchedFromUid, filterLastUsed); } - void configureContentView(List<Intent> payloadIntents, Intent[] initialIntents, + /** + * Returns true if the activity is finishing and creation should halt + */ + boolean configureContentView(List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList, boolean alwaysUseOption) { // The last argument of createAdapter is whether to do special handling // of the last used choice to highlight it in the list. We need to always @@ -828,7 +837,9 @@ public class ResolverActivity extends Activity { mAlwaysUseOption = alwaysUseOption; int count = mAdapter.getUnfilteredCount(); - if (count > 1 || (count == 1 && mAdapter.getOtherProfile() != null)) { + if ((!shouldAutoLaunchSingleChoice() && count > 0) + || count > 1 + || (count == 1 && mAdapter.getOtherProfile() != null)) { setContentView(layoutId); mAdapterView = (AbsListView) findViewById(R.id.resolver_list); onPrepareAdapterView(mAdapterView, mAdapter, alwaysUseOption); @@ -837,7 +848,7 @@ public class ResolverActivity extends Activity { mPackageMonitor.unregister(); mRegistered = false; finish(); - return; + return true; } else { setContentView(R.layout.resolver_list); @@ -847,6 +858,7 @@ public class ResolverActivity extends Activity { mAdapterView = (AbsListView) findViewById(R.id.resolver_list); mAdapterView.setVisibility(View.GONE); } + return false; } void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter, @@ -884,6 +896,7 @@ public class ResolverActivity extends Activity { private final ResolveInfo mResolveInfo; private final CharSequence mDisplayLabel; private Drawable mDisplayIcon; + private Drawable mBadge; private final CharSequence mExtendedInfo; private final Intent mResolvedIntent; private final List<Intent> mSourceIntents = new ArrayList<>(); @@ -928,7 +941,25 @@ public class ResolverActivity extends Activity { } public Drawable getBadgeIcon() { - return null; + // We only expose a badge if we have extended info. + // The badge is a higher-priority disambiguation signal + // but we don't need one if we wouldn't show extended info at all. + if (TextUtils.isEmpty(getExtendedInfo())) { + return null; + } + + if (mBadge == null && mResolveInfo != null && mResolveInfo.activityInfo != null + && mResolveInfo.activityInfo.applicationInfo != null) { + if (mResolveInfo.activityInfo.icon == 0 || mResolveInfo.activityInfo.icon + == mResolveInfo.activityInfo.applicationInfo.icon) { + // Badging an icon with exactly the same icon is silly. + // If the activityInfo icon resid is 0 it will fall back + // to the application's icon, making it a match. + return null; + } + mBadge = mResolveInfo.activityInfo.applicationInfo.loadIcon(mPm); + } + return mBadge; } @Override @@ -1366,8 +1397,8 @@ public class ResolverActivity extends Activity { } else { mHasExtendedInfo = true; boolean usePkg = false; - CharSequence startApp = ro.getResolveInfoAt(0).activityInfo.applicationInfo - .loadLabel(mPm); + final ApplicationInfo ai = ro.getResolveInfoAt(0).activityInfo.applicationInfo; + final CharSequence startApp = ai.loadLabel(mPm); if (startApp == null) { usePkg = true; } diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index 4f4d3e0faa44..f178c8cf7ece 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -338,7 +338,7 @@ public final class BatteryStatsHelper { } if (mCpuPowerCalculator == null) { - mCpuPowerCalculator = new CpuPowerCalculator(); + mCpuPowerCalculator = new CpuPowerCalculator(mPowerProfile); } mCpuPowerCalculator.reset(); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 4ff786987028..6ccdd08498ce 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -105,7 +105,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 130 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 131 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -118,8 +118,6 @@ public final class BatteryStatsImpl extends BatteryStats { // in to one common name. private static final int MAX_WAKELOCKS_PER_UID = 100; - private static int sNumSpeedSteps; - private final JournaledFile mFile; public final AtomicFile mCheckinFile; public final AtomicFile mDailyFile; @@ -133,7 +131,7 @@ public final class BatteryStatsImpl extends BatteryStats { private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats(); private final KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader(); - private final KernelCpuSpeedReader mKernelCpuSpeedReader = new KernelCpuSpeedReader(); + private KernelCpuSpeedReader[] mKernelCpuSpeedReaders; public interface BatteryCallback { public void batteryNeedsCpuUpdate(); @@ -4411,7 +4409,7 @@ public final class BatteryStatsImpl extends BatteryStats { LongSamplingCounter mUserCpuTime = new LongSamplingCounter(mOnBatteryTimeBase); LongSamplingCounter mSystemCpuTime = new LongSamplingCounter(mOnBatteryTimeBase); LongSamplingCounter mCpuPower = new LongSamplingCounter(mOnBatteryTimeBase); - LongSamplingCounter[] mSpeedBins; + LongSamplingCounter[][] mCpuClusterSpeed; /** * The statistics we have collected for this uid's wake locks. @@ -4470,7 +4468,6 @@ public final class BatteryStatsImpl extends BatteryStats { mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED, mWifiMulticastTimers, mOnBatteryTimeBase); mProcessStateTimer = new StopwatchTimer[NUM_PROCESS_STATE]; - mSpeedBins = new LongSamplingCounter[getCpuSpeedSteps()]; } @Override @@ -5008,10 +5005,18 @@ public final class BatteryStatsImpl extends BatteryStats { } @Override - public long getTimeAtCpuSpeed(int step, int which) { - if (step >= 0 && step < mSpeedBins.length) { - if (mSpeedBins[step] != null) { - return mSpeedBins[step].getCountLocked(which); + public long getTimeAtCpuSpeed(int cluster, int step, int which) { + if (mCpuClusterSpeed != null) { + if (cluster >= 0 && cluster < mCpuClusterSpeed.length) { + final LongSamplingCounter[] cpuSpeeds = mCpuClusterSpeed[cluster]; + if (cpuSpeeds != null) { + if (step >= 0 && step < cpuSpeeds.length) { + final LongSamplingCounter c = cpuSpeeds[step]; + if (c != null) { + return c.getCountLocked(which); + } + } + } } } return 0; @@ -5128,10 +5133,16 @@ public final class BatteryStatsImpl extends BatteryStats { mUserCpuTime.reset(false); mSystemCpuTime.reset(false); mCpuPower.reset(false); - for (int i = 0; i < mSpeedBins.length; i++) { - LongSamplingCounter c = mSpeedBins[i]; - if (c != null) { - c.reset(false); + + if (mCpuClusterSpeed != null) { + for (LongSamplingCounter[] speeds : mCpuClusterSpeed) { + if (speeds != null) { + for (LongSamplingCounter speed : speeds) { + if (speed != null) { + speed.reset(false); + } + } + } } } @@ -5280,10 +5291,16 @@ public final class BatteryStatsImpl extends BatteryStats { mUserCpuTime.detach(); mSystemCpuTime.detach(); mCpuPower.detach(); - for (int i = 0; i < mSpeedBins.length; i++) { - LongSamplingCounter c = mSpeedBins[i]; - if (c != null) { - c.detach(); + + if (mCpuClusterSpeed != null) { + for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeed) { + if (cpuSpeeds != null) { + for (LongSamplingCounter c : cpuSpeeds) { + if (c != null) { + c.detach(); + } + } + } } } } @@ -5461,15 +5478,27 @@ public final class BatteryStatsImpl extends BatteryStats { mSystemCpuTime.writeToParcel(out); mCpuPower.writeToParcel(out); - out.writeInt(mSpeedBins.length); - for (int i = 0; i < mSpeedBins.length; i++) { - LongSamplingCounter c = mSpeedBins[i]; - if (c != null) { - out.writeInt(1); - c.writeToParcel(out); - } else { - out.writeInt(0); + if (mCpuClusterSpeed != null) { + out.writeInt(1); + out.writeInt(mCpuClusterSpeed.length); + for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeed) { + if (cpuSpeeds != null) { + out.writeInt(1); + out.writeInt(cpuSpeeds.length); + for (LongSamplingCounter c : cpuSpeeds) { + if (c != null) { + out.writeInt(1); + c.writeToParcel(out); + } else { + out.writeInt(0); + } + } + } else { + out.writeInt(0); + } } + } else { + out.writeInt(0); } } @@ -5653,13 +5682,32 @@ public final class BatteryStatsImpl extends BatteryStats { mSystemCpuTime = new LongSamplingCounter(mOnBatteryTimeBase, in); mCpuPower = new LongSamplingCounter(mOnBatteryTimeBase, in); - int bins = in.readInt(); - int steps = getCpuSpeedSteps(); - mSpeedBins = new LongSamplingCounter[bins >= steps ? bins : steps]; - for (int i = 0; i < bins; i++) { - if (in.readInt() != 0) { - mSpeedBins[i] = new LongSamplingCounter(mOnBatteryTimeBase, in); + if (in.readInt() != 0) { + int numCpuClusters = in.readInt(); + if (mPowerProfile != null && mPowerProfile.getNumCpuClusters() != numCpuClusters) { + throw new ParcelFormatException("Incompatible number of cpu clusters"); + } + + mCpuClusterSpeed = new LongSamplingCounter[numCpuClusters][]; + for (int cluster = 0; cluster < numCpuClusters; cluster++) { + if (in.readInt() != 0) { + int numSpeeds = in.readInt(); + if (mPowerProfile != null && + mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != numSpeeds) { + throw new ParcelFormatException("Incompatible number of cpu speeds"); + } + + final LongSamplingCounter[] cpuSpeeds = new LongSamplingCounter[numSpeeds]; + mCpuClusterSpeed[cluster] = cpuSpeeds; + for (int speed = 0; speed < numSpeeds; speed++) { + if (in.readInt() != 0) { + cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase, in); + } + } + } } + } else { + mCpuClusterSpeed = null; } } @@ -6874,6 +6922,19 @@ public final class BatteryStatsImpl extends BatteryStats { public void setPowerProfile(PowerProfile profile) { synchronized (this) { mPowerProfile = profile; + + // We need to initialize the KernelCpuSpeedReaders to read from + // the first cpu of each core. Once we have the PowerProfile, we have access to this + // information. + final int numClusters = mPowerProfile.getNumCpuClusters(); + mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters]; + int firstCpuOfCluster = 0; + for (int i = 0; i < numClusters; i++) { + final int numSpeedSteps = mPowerProfile.getNumSpeedStepsInCpuCluster(i); + mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster, + numSpeedSteps); + firstCpuOfCluster += mPowerProfile.getNumCoresInCpuCluster(i); + } } } @@ -6881,10 +6942,6 @@ public final class BatteryStatsImpl extends BatteryStats { mCallback = cb; } - public void setNumSpeedSteps(int steps) { - if (sNumSpeedSteps == 0) sNumSpeedSteps = steps; - } - public void setRadioScanningTimeout(long timeout) { if (mPhoneSignalScanningTimer != null) { mPhoneSignalScanningTimer.setTimeout(timeout); @@ -7997,9 +8054,11 @@ public final class BatteryStatsImpl extends BatteryStats { // If no app is holding a wakelock, then the distribution is normal. final int wakelockWeight = 50; - // Read the time spent at various cpu frequencies. - final int cpuSpeedSteps = getCpuSpeedSteps(); - final long[] cpuSpeeds = mKernelCpuSpeedReader.readDelta(); + // Read the time spent for each cluster at various cpu frequencies. + final long[][] clusterSpeeds = new long[mKernelCpuSpeedReaders.length][]; + for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) { + clusterSpeeds[cluster] = mKernelCpuSpeedReaders[cluster].readDelta(); + } int numWakelocks = 0; @@ -8072,11 +8131,23 @@ public final class BatteryStatsImpl extends BatteryStats { // Add the cpu speeds to this UID. These are used as a ratio // for computing the power this UID used. - for (int i = 0; i < cpuSpeedSteps; i++) { - if (u.mSpeedBins[i] == null) { - u.mSpeedBins[i] = new LongSamplingCounter(mOnBatteryTimeBase); + if (u.mCpuClusterSpeed == null) { + u.mCpuClusterSpeed = new LongSamplingCounter[clusterSpeeds.length][]; + } + + for (int cluster = 0; cluster < clusterSpeeds.length; cluster++) { + if (u.mCpuClusterSpeed[cluster] == null) { + u.mCpuClusterSpeed[cluster] = + new LongSamplingCounter[clusterSpeeds[cluster].length]; + } + + final LongSamplingCounter[] cpuSpeeds = u.mCpuClusterSpeed[cluster]; + for (int speed = 0; speed < clusterSpeeds[cluster].length; speed++) { + if (cpuSpeeds[speed] == null) { + cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase); + } + cpuSpeeds[speed].addCountLocked(clusterSpeeds[cluster][speed]); } - u.mSpeedBins[i].addCountLocked(cpuSpeeds[i]); } } }); @@ -8776,11 +8847,6 @@ public final class BatteryStatsImpl extends BatteryStats { } } - @Override - public int getCpuSpeedSteps() { - return sNumSpeedSteps; - } - /** * Retrieve the statistics object for a particular uid, creating if needed. */ @@ -9216,11 +9282,6 @@ public final class BatteryStatsImpl extends BatteryStats { } } - sNumSpeedSteps = in.readInt(); - if (sNumSpeedSteps < 0 || sNumSpeedSteps > 100) { - throw new ParcelFormatException("Bad speed steps in data: " + sNumSpeedSteps); - } - final int NU = in.readInt(); if (NU > 10000) { throw new ParcelFormatException("File corrupt: too many uids " + NU); @@ -9304,17 +9365,33 @@ public final class BatteryStatsImpl extends BatteryStats { u.mSystemCpuTime.readSummaryFromParcelLocked(in); u.mCpuPower.readSummaryFromParcelLocked(in); - int NSB = in.readInt(); - if (NSB > 100) { - throw new ParcelFormatException("File corrupt: too many speed bins " + NSB); - } + if (in.readInt() != 0) { + final int numClusters = in.readInt(); + if (mPowerProfile != null && mPowerProfile.getNumCpuClusters() != numClusters) { + throw new ParcelFormatException("Incompatible cpu cluster arrangement"); + } - u.mSpeedBins = new LongSamplingCounter[NSB]; - for (int i=0; i<NSB; i++) { - if (in.readInt() != 0) { - u.mSpeedBins[i] = new LongSamplingCounter(mOnBatteryTimeBase); - u.mSpeedBins[i].readSummaryFromParcelLocked(in); + u.mCpuClusterSpeed = new LongSamplingCounter[numClusters][]; + for (int cluster = 0; cluster < numClusters; cluster++) { + int NSB = in.readInt(); + if (mPowerProfile != null && + mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != NSB) { + throw new ParcelFormatException("File corrupt: too many speed bins " + NSB); + } + + if (in.readInt() != 0) { + u.mCpuClusterSpeed[cluster] = new LongSamplingCounter[NSB]; + for (int speed = 0; speed < NSB; speed++) { + if (in.readInt() != 0) { + u.mCpuClusterSpeed[cluster][speed] = new LongSamplingCounter( + mOnBatteryTimeBase); + u.mCpuClusterSpeed[cluster][speed].readSummaryFromParcelLocked(in); + } + } + } } + } else { + u.mCpuClusterSpeed = null; } int NW = in.readInt(); @@ -9531,7 +9608,6 @@ public final class BatteryStatsImpl extends BatteryStats { } } - out.writeInt(sNumSpeedSteps); final int NU = mUidStats.size(); out.writeInt(NU); for (int iu = 0; iu < NU; iu++) { @@ -9640,15 +9716,27 @@ public final class BatteryStatsImpl extends BatteryStats { u.mSystemCpuTime.writeSummaryFromParcelLocked(out); u.mCpuPower.writeSummaryFromParcelLocked(out); - out.writeInt(u.mSpeedBins.length); - for (int i = 0; i < u.mSpeedBins.length; i++) { - LongSamplingCounter speedBin = u.mSpeedBins[i]; - if (speedBin != null) { - out.writeInt(1); - speedBin.writeSummaryFromParcelLocked(out); - } else { - out.writeInt(0); + if (u.mCpuClusterSpeed != null) { + out.writeInt(1); + out.writeInt(u.mCpuClusterSpeed.length); + for (LongSamplingCounter[] cpuSpeeds : u.mCpuClusterSpeed) { + if (cpuSpeeds != null) { + out.writeInt(1); + out.writeInt(cpuSpeeds.length); + for (LongSamplingCounter c : cpuSpeeds) { + if (c != null) { + out.writeInt(1); + c.writeSummaryFromParcelLocked(out); + } else { + out.writeInt(0); + } + } + } else { + out.writeInt(0); + } } + } else { + out.writeInt(0); } final ArrayMap<String, Uid.Wakelock> wakeStats = u.mWakelockStats.getMap(); @@ -9897,8 +9985,6 @@ public final class BatteryStatsImpl extends BatteryStats { mFlashlightTurnedOnTimers.clear(); mCameraTurnedOnTimers.clear(); - sNumSpeedSteps = in.readInt(); - int numUids = in.readInt(); mUidStats.clear(); for (int i = 0; i < numUids; i++) { @@ -10037,8 +10123,6 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(0); } - out.writeInt(sNumSpeedSteps); - if (inclUids) { int size = mUidStats.size(); out.writeInt(size); diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java index d62f7a689157..8417856d16d5 100644 --- a/core/java/com/android/internal/os/CpuPowerCalculator.java +++ b/core/java/com/android/internal/os/CpuPowerCalculator.java @@ -22,12 +22,47 @@ import android.util.Log; public class CpuPowerCalculator extends PowerCalculator { private static final String TAG = "CpuPowerCalculator"; private static final boolean DEBUG = BatteryStatsHelper.DEBUG; + private final PowerProfile mProfile; + + public CpuPowerCalculator(PowerProfile profile) { + mProfile = profile; + } @Override public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, int statsType) { + app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000; - app.cpuPowerMah = (double) u.getCpuPowerMaUs(statsType) / (60.0 * 60.0 * 1000.0 * 1000.0); + + // Aggregate total time spent on each cluster. + long totalTime = 0; + final int numClusters = mProfile.getNumCpuClusters(); + for (int cluster = 0; cluster < numClusters; cluster++) { + final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster); + for (int speed = 0; speed < speedsForCluster; speed++) { + totalTime += u.getTimeAtCpuSpeed(cluster, speed, statsType); + } + } + totalTime = Math.max(totalTime, 1); + + double cpuPowerMaMs = 0; + for (int cluster = 0; cluster < numClusters; cluster++) { + final int speedsForCluster = mProfile.getNumSpeedStepsInCpuCluster(cluster); + for (int speed = 0; speed < speedsForCluster; speed++) { + final double ratio = (double) u.getTimeAtCpuSpeed(cluster, speed, statsType) / + totalTime; + final double cpuSpeedStepPower = ratio * app.cpuTimeMs * + mProfile.getAveragePowerForCpu(cluster, speed); + if (DEBUG && ratio != 0) { + Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #" + + speed + " ratio=" + BatteryStatsHelper.makemAh(ratio) + " power=" + + BatteryStatsHelper.makemAh(cpuSpeedStepPower / (60 * 60 * 1000))); + } + cpuPowerMaMs += cpuSpeedStepPower; + } + } + app.cpuPowerMah = cpuPowerMaMs / (60 * 60 * 1000); + if (DEBUG && (app.cpuTimeMs != 0 || app.cpuPowerMah != 0)) { Log.d(TAG, "UID " + u.getUid() + ": CPU time=" + app.cpuTimeMs + " ms power=" + BatteryStatsHelper.makemAh(app.cpuPowerMah)); diff --git a/core/java/com/android/internal/os/KernelCpuSpeedReader.java b/core/java/com/android/internal/os/KernelCpuSpeedReader.java index c30df28c5c1f..5b776acf0cc8 100644 --- a/core/java/com/android/internal/os/KernelCpuSpeedReader.java +++ b/core/java/com/android/internal/os/KernelCpuSpeedReader.java @@ -24,8 +24,8 @@ import java.io.IOException; import java.util.Arrays; /** - * Reads CPU time spent at various frequencies and provides a delta from the last call to - * {@link #readDelta}. Each line in the proc file has the format: + * Reads CPU time of a specific core spent at various frequencies and provides a delta from the + * last call to {@link #readDelta}. Each line in the proc file has the format: * * freq time * @@ -33,12 +33,20 @@ import java.util.Arrays; */ public class KernelCpuSpeedReader { private static final String TAG = "KernelCpuSpeedReader"; - private static final String sProcFile = - "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state"; - private static final int MAX_SPEEDS = 60; - private long[] mLastSpeedTimes = new long[MAX_SPEEDS]; - private long[] mDeltaSpeedTimes = new long[MAX_SPEEDS]; + private final String mProcFile; + private final long[] mLastSpeedTimes; + private final long[] mDeltaSpeedTimes; + + /** + * @param cpuNumber The cpu (cpu0, cpu1, etc) whose state to read. + */ + public KernelCpuSpeedReader(int cpuNumber, int numSpeedSteps) { + mProcFile = String.format("/sys/devices/system/cpu/cpu%d/cpufreq/stats/time_in_state", + cpuNumber); + mLastSpeedTimes = new long[numSpeedSteps]; + mDeltaSpeedTimes = new long[numSpeedSteps]; + } /** * The returned array is modified in subsequent calls to {@link #readDelta}. @@ -46,22 +54,28 @@ public class KernelCpuSpeedReader { * {@link #readDelta}. */ public long[] readDelta() { - try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) { + try (BufferedReader reader = new BufferedReader(new FileReader(mProcFile))) { TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' '); String line; int speedIndex = 0; - while ((line = reader.readLine()) != null) { + while (speedIndex < mLastSpeedTimes.length && (line = reader.readLine()) != null) { splitter.setString(line); Long.parseLong(splitter.next()); // The proc file reports time in 1/100 sec, so convert to milliseconds. long time = Long.parseLong(splitter.next()) * 10; - mDeltaSpeedTimes[speedIndex] = time - mLastSpeedTimes[speedIndex]; + if (time < mLastSpeedTimes[speedIndex]) { + // The stats reset when the cpu hotplugged. That means that the time + // we read is offset from 0, so the time is the delta. + mDeltaSpeedTimes[speedIndex] = time; + } else { + mDeltaSpeedTimes[speedIndex] = time - mLastSpeedTimes[speedIndex]; + } mLastSpeedTimes[speedIndex] = time; speedIndex++; } } catch (IOException e) { - Slog.e(TAG, "Failed to read cpu-freq", e); + Slog.e(TAG, "Failed to read cpu-freq: " + e.getMessage()); Arrays.fill(mDeltaSpeedTimes, 0); } return mDeltaSpeedTimes; diff --git a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java index 0df78ed6a68d..5d3043cc8a02 100644 --- a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java +++ b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java @@ -137,7 +137,7 @@ public class KernelUidCpuTimeReader { mLastPowerMaUs.put(uid, powerMaUs); } } catch (IOException e) { - Slog.e(TAG, "Failed to read uid_cputime", e); + Slog.e(TAG, "Failed to read uid_cputime: " + e.getMessage()); } mLastTimeReadUs = nowUs; } diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java index 4ede8ddab3bc..aaa9f734aba8 100644 --- a/core/java/com/android/internal/os/PowerProfile.java +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -59,6 +59,7 @@ public class PowerProfile { /** * Power consumption when CPU is in power collapse mode. */ + @Deprecated public static final String POWER_CPU_ACTIVE = "cpu.active"; /** @@ -163,6 +164,7 @@ public class PowerProfile { */ public static final String POWER_CAMERA = "camera.avg"; + @Deprecated public static final String POWER_CPU_SPEEDS = "cpu.speeds"; /** @@ -191,6 +193,7 @@ public class PowerProfile { if (sPowerMap.size() == 0) { readPowerValuesFromXml(context); } + initCpuClusters(); } private void readPowerValuesFromXml(Context context) { @@ -249,7 +252,7 @@ public class PowerProfile { } // Now collect other config variables. - int[] configResIds = new int[] { + int[] configResIds = new int[]{ com.android.internal.R.integer.config_bluetooth_idle_cur_ma, com.android.internal.R.integer.config_bluetooth_rx_cur_ma, com.android.internal.R.integer.config_bluetooth_tx_cur_ma, @@ -260,7 +263,7 @@ public class PowerProfile { com.android.internal.R.integer.config_wifi_operating_voltage_mv, }; - String[] configResIdKeys = new String[] { + String[] configResIdKeys = new String[]{ POWER_BLUETOOTH_CONTROLLER_IDLE, POWER_BLUETOOTH_CONTROLLER_RX, POWER_BLUETOOTH_CONTROLLER_TX, @@ -279,6 +282,69 @@ public class PowerProfile { } } + private CpuClusterKey[] mCpuClusters; + + private static final String POWER_CPU_CLUSTER_CORE_COUNT = "cpu.clusters.cores"; + private static final String POWER_CPU_CLUSTER_SPEED_PREFIX = "cpu.speeds.cluster"; + private static final String POWER_CPU_CLUSTER_ACTIVE_PREFIX = "cpu.active.cluster"; + + @SuppressWarnings("deprecated") + private void initCpuClusters() { + // Figure out how many CPU clusters we're dealing with + final Object obj = sPowerMap.get(POWER_CPU_CLUSTER_CORE_COUNT); + if (obj == null || !(obj instanceof Double[])) { + // Default to single. + mCpuClusters = new CpuClusterKey[1]; + mCpuClusters[0] = new CpuClusterKey(POWER_CPU_SPEEDS, POWER_CPU_ACTIVE, 1); + + } else { + final Double[] array = (Double[]) obj; + mCpuClusters = new CpuClusterKey[array.length]; + for (int cluster = 0; cluster < array.length; cluster++) { + int numCpusInCluster = (int) Math.round(array[cluster]); + mCpuClusters[cluster] = new CpuClusterKey( + POWER_CPU_CLUSTER_SPEED_PREFIX + cluster, + POWER_CPU_CLUSTER_ACTIVE_PREFIX + cluster, + numCpusInCluster); + } + } + } + + public static class CpuClusterKey { + private final String timeKey; + private final String powerKey; + private final int numCpus; + + private CpuClusterKey(String timeKey, String powerKey, int numCpus) { + this.timeKey = timeKey; + this.powerKey = powerKey; + this.numCpus = numCpus; + } + } + + public int getNumCpuClusters() { + return mCpuClusters.length; + } + + public int getNumCoresInCpuCluster(int index) { + return mCpuClusters[index].numCpus; + } + + public int getNumSpeedStepsInCpuCluster(int index) { + Object value = sPowerMap.get(mCpuClusters[index].timeKey); + if (value != null && value instanceof Double[]) { + return ((Double[])value).length; + } + return 1; // Only one speed + } + + public double getAveragePowerForCpu(int cluster, int step) { + if (cluster >= 0 && cluster < mCpuClusters.length) { + return getAveragePower(mCpuClusters[cluster].powerKey, step); + } + return 0; + } + /** * Returns the average current in mA consumed by the subsystem, or the given * default value if the subsystem has no recorded value. @@ -344,16 +410,4 @@ public class PowerProfile { public double getBatteryCapacity() { return getAveragePower(POWER_BATTERY_CAPACITY); } - - /** - * Returns the number of speeds that the CPU can be run at. - * @return - */ - public int getNumSpeedSteps() { - Object value = sPowerMap.get(POWER_CPU_SPEEDS); - if (value != null && value instanceof Double[]) { - return ((Double[])value).length; - } - return 1; // Only one speed - } } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index a873ef8e0f59..82ae2f3f032e 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -1071,12 +1071,22 @@ public class LockPatternUtils { * enter a pattern. */ public long getLockoutAttemptDeadline(int userId) { - final long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L, userId); + long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L, userId); final long timeoutMs = getLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, 0L, userId); final long now = SystemClock.elapsedRealtime(); - if (deadline < now || deadline > (now + timeoutMs)) { + if (deadline < now) { + // timeout expired + setLong(LOCKOUT_ATTEMPT_DEADLINE, 0, userId); + setLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, 0, userId); return 0L; } + + if (deadline > (now + timeoutMs)) { + // device was rebooted, set new deadline + deadline = now + timeoutMs; + setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline, userId); + } + return deadline; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 5f4dd31d0a2e..b051de490d49 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2167,6 +2167,13 @@ <permission android:name="android.permission.CONTROL_WIFI_DISPLAY" android:protectionLevel="signature" /> + <!-- Allows an application to control the color transforms applied to + displays system-wide. + <p>Not for use by third-party applications.</p> + @hide --> + <permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_TRANSFORM" + android:protectionLevel="signature" /> + <!-- @SystemApi Allows an application to control VPN. <p>Not for use by third-party applications.</p> @hide --> diff --git a/core/res/res/values-mcc240-mnc01/config.xml b/core/res/res/values-mcc240-mnc01/config.xml new file mode 100644 index 000000000000..7fb5c46309d0 --- /dev/null +++ b/core/res/res/values-mcc240-mnc01/config.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2013, 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 my 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. +*/ +--> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + + <!-- Do not set the system language as value of EF LI/EF PL --> + <bool name="config_use_sim_language_file">false</bool> + +</resources> diff --git a/core/res/res/values-mcc240-mnc05/config.xml b/core/res/res/values-mcc240-mnc05/config.xml new file mode 100644 index 000000000000..7fb5c46309d0 --- /dev/null +++ b/core/res/res/values-mcc240-mnc05/config.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2013, 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 my 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. +*/ +--> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + + <!-- Do not set the system language as value of EF LI/EF PL --> + <bool name="config_use_sim_language_file">false</bool> + +</resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 9e89b24ce919..33ef8f906b30 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1758,6 +1758,28 @@ --> <bool name="config_enableWifiDisplay">false</bool> + <!-- The color transform values that correspond to each respective configuration mode for the + built-in display, or -1 if the mode is unsupported by the device. The possible + configuration modes are: + 1. Wide-gamut ("Vibrant") + 2. Adobe RGB ("Natural") + 3. sRGB ("Standard") + + For example, if a device had Wide-gamut as color transform mode 1, sRGB mode as color + transform mode 7, and did not support Adobe RGB at all this would look like: + + <integer-array name="config_colorTransforms"> + <item>1</item> + <item>-1</item> + <item>7</item> + </integer-array> + --> + <integer-array name="config_colorTransforms"> + <item>-1</item> + <item>-1</item> + <item>-1</item> + </integer-array> + <!-- When true use the linux /dev/input/event subsystem to detect the switch changes on the headphone/microphone jack. When false use the older uevent framework. --> <bool name="config_useDevInputEventForAudioJack">false</bool> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 490d306d30b6..1122c6278227 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1134,6 +1134,7 @@ <java-symbol type="array" name="config_telephonyHardware" /> <java-symbol type="array" name="config_keySystemUuidMapping" /> <java-symbol type="array" name="config_gpsParameters" /> + <java-symbol type="array" name="config_colorTransforms" /> <java-symbol type="drawable" name="default_wallpaper" /> <java-symbol type="drawable" name="indicator_input_error" /> diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml index 28d99d8784db..ddd0ca2a4bb2 100644 --- a/core/res/res/xml/power_profile.xml +++ b/core/res/res/xml/power_profile.xml @@ -47,17 +47,38 @@ <value>0.2</value> <!-- ~2mA --> <value>0.1</value> <!-- ~1mA --> </array> - <!-- Different CPU speeds as reported in - /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state --> - <array name="cpu.speeds"> + + <!-- A list of heterogeneous CPU clusters, where the value for each cluster represents the + number of CPU cores for that cluster. + + Ex: + <array name="cpu.clusters.cores"> + <value>4</value> // cluster 0 has cpu0, cpu1, cpu2, cpu3 + <value>2</value> // cluster 1 has cpu4, cpu5 + </array> --> + <array name="cpu.clusters.cores"> + <value>1</value> <!-- cluster 0 has cpu0 --> + </array> + + <!-- Different CPU speeds for cluster 0 as reported in + /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state. + + There must be one of these for each cluster, labeled: + cpu.speeds.cluster0, cpu.speeds.cluster1, etc... --> + <array name="cpu.speeds.cluster0"> <value>400000</value> <!-- 400 MHz CPU speed --> </array> - <!-- Current when CPU is idle --> - <item name="cpu.idle">0.1</item> - <!-- Current at each CPU speed, as per 'cpu.speeds' --> - <array name="cpu.active"> + + <!-- Current at each CPU speed for cluster 0, as per 'cpu.speeds.cluster0'. + Like cpu.speeds.cluster0, there must be one of these present for + each heterogeneous CPU cluster. --> + <array name="cpu.active.cluster0"> <value>0.1</value> <!-- ~100mA --> </array> + + <!-- Current when CPU is idle --> + <item name="cpu.idle">0.1</item> + <!-- This is the battery capacity in mAh (measured at nominal voltage) --> <item name="battery.capacity">1000</item> diff --git a/core/tests/BTtraffic/Android.mk b/core/tests/BTtraffic/Android.mk new file mode 100644 index 000000000000..7d8352717a34 --- /dev/null +++ b/core/tests/BTtraffic/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_RESOURCE_DIR := \ + $(LOCAL_PATH)/res + +LOCAL_PACKAGE_NAME := bttraffic +LOCAL_CERTIFICATE := platform + +include $(BUILD_PACKAGE) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/core/tests/BTtraffic/AndroidManifest.xml b/core/tests/BTtraffic/AndroidManifest.xml new file mode 100644 index 000000000000..00d9707de2bf --- /dev/null +++ b/core/tests/BTtraffic/AndroidManifest.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.google.android.experimental.bttraffic" > + + <uses-permission android:name="android.permission.BLUETOOTH"/> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/> + + <uses-sdk + android:minSdkVersion="18" + android:targetSdkVersion="18" + /> + <application + android:allowBackup="false" + android:label="@string/app_name" > + <service + android:name=".BTtraffic" + android:enabled="true" + android:exported="true" > + </service> + </application> + +</manifest> diff --git a/core/tests/BTtraffic/README b/core/tests/BTtraffic/README new file mode 100644 index 000000000000..430488f656f9 --- /dev/null +++ b/core/tests/BTtraffic/README @@ -0,0 +1,45 @@ +This is a tool to generate classic Bluetooth traffic with specified period and package size. +Together with the SvcMonitor, which will be called automatically in this android service, can be +used to measure the CPU usage from the Java layer Bluetooth code and the underlying system service +com.android.bluetooth. + +1. Server (Listener) - Client (Sender) model. Both run as an Android service. +2. No pairing needed. Communicate via unsecured RFcomm. Client establishes the connection by +providing the MAC addr of the server. +3. Bluetooth has to be turned on on both side. +4. Client can configure the traffic by specifying the transfer period and package size. +5. A separate monitor process will be automatically forked and will be reading from /proc file +system to calculate the cpu usage. The measurement is updated once per second. +6. The monitor process (com.google.android.experimental.svcmonitor/.ScvMonitor) can be run as an +independent service to measure cpu usage on any similarly configured tests (e.g. wifi, BLE). Refer +to SvcMonitor's README for usage and details. + +Usage: +To instal the test: +On both the server and client device, install the 2 apk: +$ adb install $OUT/system/app/bttraffic/bttraffic.apk +$ adb install $OUT/system/app/svcmonitor/svcmonitor.apk + +To start the service on the SERVER side: +$ adb shell am startservice -a start --ez ack true \ +com.google.android.experimental.bttraffic/.BTtraffic + +To start the service on the CLIENT side: +$ adb shell am startservice -a start \ +-e addr "F8:A9:D0:A8:74:8E" --ei size 1000 --ei period 15 \ +com.google.android.experimental.bttraffic/.BTtraffic + +To stop the test: +On either the server or client: +$ adb shell am startservice -a stop \ +com.google.android.experimental.bttraffic/.BTtraffic + +To look at the data: +$ adb logcat | grep bttraffic + +Options: +-e addr: MAC addr of the server, in uppercase letter. +--ei size: package size, unit: byte; default: 1024, MAX: 20MB +--ei period: system sleep time between sending each package, unit: ms, default: 5000 + ** if -1 is provided, client will only send the package once. +--ez ack: whether acknowledge is required (true/false) diff --git a/core/tests/BTtraffic/res/values/strings.xml b/core/tests/BTtraffic/res/values/strings.xml new file mode 100644 index 000000000000..e70276e03647 --- /dev/null +++ b/core/tests/BTtraffic/res/values/strings.xml @@ -0,0 +1,3 @@ +<resources> + <string name="app_name">Bluetooth Test</string> +</resources> diff --git a/core/tests/BTtraffic/src/com/android/google/experimental/bttraffic/BTtraffic.java b/core/tests/BTtraffic/src/com/android/google/experimental/bttraffic/BTtraffic.java new file mode 100644 index 000000000000..286c0aa2915f --- /dev/null +++ b/core/tests/BTtraffic/src/com/android/google/experimental/bttraffic/BTtraffic.java @@ -0,0 +1,328 @@ +package com.google.android.experimental.bttraffic; + +import android.app.Service; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothServerSocket; +import android.bluetooth.BluetoothSocket; +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; +import android.os.SystemClock; +import android.util.Log; + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.Exception; +import java.lang.Runtime; +import java.lang.RuntimeException; +import java.lang.Process; +import java.nio.ByteBuffer; +import java.util.Random; +import java.util.Set; +import java.util.UUID; + +public class BTtraffic extends Service { + public static final String TAG = "bttraffic"; + static final String SERVICE_NAME = "bttraffic"; + static final String SYS_SERVICE_NAME = "com.android.bluetooth"; + static final UUID SERVICE_UUID = UUID.fromString("5e8945b0-1234-5432-a5e2-0800200c9a67"); + volatile Thread mWorkerThread; + volatile boolean isShuttingDown = false; + volatile boolean isServer = false; + + public BTtraffic() {} + + static void safeClose(Closeable closeable) { + try { + closeable.close(); + } catch (IOException e) { + Log.d(TAG, "Unable to close resource.\n"); + } + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (intent == null) { + stopSelf(); + return 0; + } + if ("stop".equals(intent.getAction())) { + stopService(); + } else if ("start".equals(intent.getAction())) { + startWorker(intent); + } else { + Log.d(TAG, "unknown action: + " + intent.getAction()); + } + return 0; + } + + private void startWorker(Intent intent) { + if (mWorkerThread != null) { + Log.d(TAG, "worker thread already active"); + return; + } + isShuttingDown = false; + String remoteAddr = intent.getStringExtra("addr"); + Log.d(TAG, "startWorker: addr=" + remoteAddr); + Runnable worker = + remoteAddr == null + ? new ListenerRunnable(this, intent) + : new SenderRunnable(this, remoteAddr, intent); + isServer = remoteAddr == null ? true: false; + mWorkerThread = new Thread(worker, "BTtrafficWorker"); + try { + startMonitor(); + Log.d(TAG, "Monitor service started"); + mWorkerThread.start(); + Log.d(TAG, "Worker thread started"); + } catch (Exception e) { + Log.d(TAG, "Failed to start service", e); + } + } + + private void startMonitor() + throws Exception { + if (isServer) { + Log.d(TAG, "Start monitor on server"); + String[] startmonitorCmd = { + "/system/bin/am", + "startservice", + "-a", "start", + "-e", "java", SERVICE_NAME, + "-e", "hal", SYS_SERVICE_NAME, + "com.google.android.experimental.svcmonitor/.SvcMonitor" + }; + Process ps = new ProcessBuilder() + .command(startmonitorCmd) + .redirectErrorStream(true) + .start(); + } else { + Log.d(TAG, "No need to start SvcMonitor on client"); + } + } + + private void stopMonitor() + throws Exception { + if (isServer) { + Log.d(TAG, "StopMonitor on server"); + String[] stopmonitorCmd = { + "/system/bin/am", + "startservice", + "-a", "stop", + "com.google.android.experimental.svcmonitor/.SvcMonitor" + }; + Process ps = new ProcessBuilder() + .command(stopmonitorCmd) + .redirectErrorStream(true) + .start(); + } else { + Log.d(TAG, "No need to stop Svcmonitor on client"); + } + } + + public void stopService() { + if (mWorkerThread == null) { + Log.d(TAG, "no active thread"); + return; + } + + isShuttingDown = true; + + try { + stopMonitor(); + } catch (Exception e) { + Log.d(TAG, "Unable to stop SvcMonitor!", e); + } + + if (Thread.currentThread() != mWorkerThread) { + mWorkerThread.interrupt(); + Log.d(TAG, "Interrupting thread"); + try { + mWorkerThread.join(); + } catch (InterruptedException e) { + Log.d(TAG, "Unable to join thread!"); + } + } + + mWorkerThread = null; + stopSelf(); + Log.d(TAG, "Service stopped"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + } + + @Override + public IBinder onBind(Intent intent) { + throw new UnsupportedOperationException("Not yet implemented"); + } + + public static class ListenerRunnable implements Runnable { + private final BTtraffic bttraffic; + private final boolean sendAck; + private Intent intent; + private final int maxbuffersize = 20 * 1024 * 1024; + + public ListenerRunnable(BTtraffic bttraffic, Intent intent) { + this.bttraffic = bttraffic; + this.sendAck = intent.getBooleanExtra("ack", true); + this.intent = intent; + } + + @Override + public void run() { + BluetoothServerSocket serverSocket; + + try { + Log.d(TAG, "getting server socket"); + serverSocket = BluetoothAdapter.getDefaultAdapter() + .listenUsingInsecureRfcommWithServiceRecord( + SERVICE_NAME, SERVICE_UUID); + } catch (IOException e) { + Log.d(TAG, "error creating server socket, stopping thread"); + bttraffic.stopService(); + return; + } + + Log.d(TAG, "got server socket, starting accept loop"); + BluetoothSocket socket = null; + try { + Log.d(TAG, "accepting"); + socket = serverSocket.accept(); + + if (!Thread.interrupted()) { + Log.d(TAG, "accepted, listening"); + doListening(socket.getInputStream(), socket.getOutputStream()); + Log.d(TAG, "listen finished"); + } + } catch (IOException e) { + Log.d(TAG, "error while accepting or listening", e); + } finally { + Log.d(TAG, "Linster interruped"); + Log.d(TAG, "closing socket and stopping service"); + safeClose(serverSocket); + safeClose(socket); + if (!bttraffic.isShuttingDown) + bttraffic.stopService(); + } + + } + + private void doListening(InputStream inputStream, OutputStream outputStream) + throws IOException { + ByteBuffer byteBuffer = ByteBuffer.allocate(maxbuffersize); + + while (!Thread.interrupted()) { + readBytesIntoBuffer(inputStream, byteBuffer, 4); + byteBuffer.flip(); + int length = byteBuffer.getInt(); + if (Thread.interrupted()) + break; + readBytesIntoBuffer(inputStream, byteBuffer, length); + + if (sendAck) + outputStream.write(0x55); + } + } + + void readBytesIntoBuffer(InputStream inputStream, ByteBuffer byteBuffer, int numToRead) + throws IOException { + byteBuffer.clear(); + while (true) { + int position = byteBuffer.position(); + int remaining = numToRead - position; + if (remaining == 0) { + break; + } + int count = inputStream.read(byteBuffer.array(), position, remaining); + if (count < 0) { + throw new IOException("read the EOF"); + } + byteBuffer.position(position + count); + } + } + } + + public static class SenderRunnable implements Runnable { + private final BTtraffic bttraffic; + private final String remoteAddr; + private final int pkgsize, period; + private final int defaultpkgsize = 1024; + private final int defaultperiod = 5000; + private static ByteBuffer lengthBuffer = ByteBuffer.allocate(4); + + public SenderRunnable(BTtraffic bttraffic, String remoteAddr, Intent intent) { + this.bttraffic = bttraffic; + this.remoteAddr = remoteAddr; + this.pkgsize = intent.getIntExtra("size", defaultpkgsize); + this.period = intent.getIntExtra("period", defaultperiod); + } + + @Override + public void run() { + BluetoothDevice device = null; + try { + device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(remoteAddr); + } catch (IllegalArgumentException e) { + Log.d(TAG, "Invalid BT MAC address!\n"); + } + if (device == null) { + Log.d(TAG, "can't find matching device, stopping thread and service"); + bttraffic.stopService(); + return; + } + + BluetoothSocket socket = null; + try { + Log.d(TAG, "connecting to device with MAC addr: " + remoteAddr); + socket = device.createInsecureRfcommSocketToServiceRecord(SERVICE_UUID); + socket.connect(); + Log.d(TAG, "connected, starting to send"); + doSending(socket.getOutputStream()); + Log.d(TAG, "send stopped, stopping service"); + } catch (Exception e) { + Log.d(TAG, "error while sending", e); + } finally { + Log.d(TAG, "finishing, closing thread and service"); + safeClose(socket); + if (!bttraffic.isShuttingDown) + bttraffic.stopService(); + } + } + + private void doSending(OutputStream outputStream) throws IOException { + Log.w(TAG, "doSending"); + try { + Random random = new Random(System.currentTimeMillis()); + + byte[] bytes = new byte[pkgsize]; + random.nextBytes(bytes); + while (!Thread.interrupted()) { + writeBytes(outputStream, bytes.length); + outputStream.write(bytes, 0, bytes.length); + if (period < 0) + break; + if (period == 0) + continue; + + SystemClock.sleep(period); + } + Log.d(TAG, "Sender interrupted"); + } catch (IOException e) { + Log.d(TAG, "doSending got error", e); + } + } + + private static void writeBytes(OutputStream outputStream, int value) throws IOException { + lengthBuffer.putInt(value); + lengthBuffer.flip(); + outputStream.write(lengthBuffer.array(), lengthBuffer.position(), lengthBuffer.limit()); + } + } + +} diff --git a/core/tests/SvcMonitor/Android.mk b/core/tests/SvcMonitor/Android.mk new file mode 100644 index 000000000000..2b8045506f02 --- /dev/null +++ b/core/tests/SvcMonitor/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_RESOURCE_DIR := \ + $(LOCAL_PATH)/res + +LOCAL_PACKAGE_NAME := svcmonitor +LOCAL_CERTIFICATE := platform + +include $(BUILD_PACKAGE) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/core/tests/SvcMonitor/AndroidManifest.xml b/core/tests/SvcMonitor/AndroidManifest.xml new file mode 100644 index 000000000000..de5a9bdaed41 --- /dev/null +++ b/core/tests/SvcMonitor/AndroidManifest.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.google.android.experimental.svcmonitor" > + + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/> + + <uses-sdk + android:minSdkVersion="18" + android:targetSdkVersion="18" + /> + <application + android:allowBackup="false" + android:label="@string/app_name" > + <service + android:name=".SvcMonitor" + android:enabled="true" + android:exported="true" > + </service> + </application> + +</manifest> diff --git a/core/tests/SvcMonitor/README b/core/tests/SvcMonitor/README new file mode 100644 index 000000000000..13a4380589b4 --- /dev/null +++ b/core/tests/SvcMonitor/README @@ -0,0 +1,27 @@ +This Android service measures CPU usage of a program and an underlying system service it relies on. +An example of this would be an android app XYZ communicates to some other device via Bluetooth. The +SvcMonitor service can monitor the CPU usage of XYZ and com.android.bluetooth. + +Usage: + +To start the service: +$ adb shell am startservice -a start \ +-e java XYZ -e hal com.android.bluetooth \ +com.google.android.experimental.svcmonitor/.SvcMonitor + +To stop the service: +$ adb shell am startservice -a stop \ +com.google.android.experimental.svcmonitor/.SvcMonitor + +To stop the service config: +$ adb shell am startservice -a change \ +-e java NewName -e hal NewService \ +com.google.android.experimental.svcmonitor/.SvcMonitor + +To monitor the data: +$ adb logcat | grep XYZ + +Options: +-e java NameOfProgram: any running process’s name. +-e hal NameOfSysService: name of the system service the previous process relies on. +--ei period: period between each measurement (frequency). Unit: ms, Default:1000, Min: 100 diff --git a/core/tests/SvcMonitor/res/values/strings.xml b/core/tests/SvcMonitor/res/values/strings.xml new file mode 100644 index 000000000000..e70276e03647 --- /dev/null +++ b/core/tests/SvcMonitor/res/values/strings.xml @@ -0,0 +1,3 @@ +<resources> + <string name="app_name">Bluetooth Test</string> +</resources> diff --git a/core/tests/SvcMonitor/src/com/android/google/experimental/svcmoniter/SvcMonitor.java b/core/tests/SvcMonitor/src/com/android/google/experimental/svcmoniter/SvcMonitor.java new file mode 100644 index 000000000000..a451445530cd --- /dev/null +++ b/core/tests/SvcMonitor/src/com/android/google/experimental/svcmoniter/SvcMonitor.java @@ -0,0 +1,209 @@ +package com.google.android.experimental.svcmonitor; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.os.SystemClock; +import android.util.Log; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.lang.Runnable; +import java.lang.Thread; +import java.util.Set; + +public class SvcMonitor extends Service { + public static final String TAG = "svcmonitor"; + String javaProc, halProc; + volatile Thread tMonitor; + int period; + + public SvcMonitor() {}; + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (intent == null) { + stopSelf(); + return 0; + } + Log.d(TAG, "Starting SvcMonitor"); + if ("stop".equals(intent.getAction())) { + stopService(); + } else if ("start".equals(intent.getAction())) { + startMonitor(intent); + } else if ("change".equals(intent.getAction())) { + changeConfig(intent); + } else { + Log.d(TAG, "unknown action: + " + intent.getAction()); + } + return 0; + } + + private void changeConfig(Intent intent) { + if (tMonitor == null) { + Log.d(TAG, "Service not active. Start service first"); + return; + } + stopThread(); + startMonitor(intent); + } + + private void startMonitor(Intent intent) { + if (tMonitor != null) { + Log.d(TAG, "thread already active"); + return; + } + javaProc = intent.getStringExtra("java"); + halProc = intent.getStringExtra("hal"); + period = intent.getIntExtra("period", 1000); + if (javaProc == null || halProc == null || period < 100) { + Log.d(TAG, "Failed starting monitor, invalid arguments."); + stopSelf(); + return; + } + Runnable monitor = new MonitorRunnable(this); + tMonitor = new Thread(monitor); + tMonitor.start(); + } + + private void stopService() { + stopThread(); + stopSelf(); + Log.d(TAG, "SvcMonitor stopped"); + } + + private void stopThread() { + if (tMonitor == null) { + Log.d(TAG, "no active thread"); + return; + } + Log.d(TAG, "interrupting monitor thread"); + tMonitor.interrupt(); + try { + tMonitor.join(); + } catch (InterruptedException e) { + Log.d(TAG, "Unable to finish monitor thread"); + } + tMonitor = null; + } + + @Override + public void onDestroy() { + super.onDestroy(); + } + + @Override + public IBinder onBind(Intent intent) { + throw new UnsupportedOperationException("Not yet implemented"); + } + + public static class MonitorRunnable implements Runnable { + long java_time_old, hal_time_old, cpu_time_old = -1; + String javaPID, halPID; + SvcMonitor svcmonitor; + static String javaProcTAG; + int period; + + public MonitorRunnable(SvcMonitor svcmonitor) { + this.svcmonitor = svcmonitor; + this.period = svcmonitor.period; + javaPID = getPIDof(svcmonitor.javaProc); + halPID = getPIDof(svcmonitor.halProc); + java_time_old = getPsTime(javaPID); + hal_time_old = getPsTime(halPID); + cpu_time_old = getPsTime(""); + javaProcTAG = String.valueOf(svcmonitor.javaProc.toCharArray()); + } + + @Override + public void run() { + if (halPID.isEmpty() || javaPID.isEmpty()) { + Log.d(javaProcTAG, "No such process: " + + (halPID.isEmpty() ? svcmonitor.halProc : svcmonitor.javaProc)); + return; + } + while (!Thread.interrupted()) { + calculateUsage(); + SystemClock.sleep(period); + } + Log.d(TAG, "Stopping monitor thread"); + } + + private void calculateUsage() { + long java_time = getPsTime(javaPID); + long hal_time = getPsTime(halPID); + long cpu_time = getPsTime(""); + + if (cpu_time_old >= 0) { + float java_diff = (float) (java_time - java_time_old); + float hal_diff = (float) (hal_time - hal_time_old); + float cpu_diff = (float) (cpu_time - cpu_time_old); + Log.w(javaProcTAG, "\n----------------\n"); + Log.w(javaProcTAG, "JAVA level CPU: " + + (java_diff * 100.0 / cpu_diff) + "%\n"); + Log.w(javaProcTAG, " HAL level CPU: " + + (hal_diff * 100.0 / cpu_diff) + "%\n"); + Log.w(javaProcTAG, " SYS level CPU: " + + ((java_diff + hal_diff) * 100.0 / cpu_diff) + "%\n"); + } else { + Log.w(TAG, "Waiting for status\n"); + } + + java_time_old = java_time; + hal_time_old = hal_time; + cpu_time_old = cpu_time; + } + + private String getPIDof(String psName) { + String pid = ""; + + try { + String[] cmd = {"/system/bin/sh", "-c", "ps | grep " + psName}; + Process ps = Runtime.getRuntime().exec(cmd); + BufferedReader in = new BufferedReader( + new InputStreamReader(ps.getInputStream())); + String temp = in.readLine(); + if (temp == null || temp.isEmpty()) + throw new IOException("No such process: " + psName); + pid = temp.split(" +")[1]; + in.close(); + } catch (IOException e) { + Log.d(javaProcTAG, "Error finding PID of process: " + psName + "\n", e); + } + return pid; + } + + private long getPsTime(String pid) { + String psStat = getPsStat("/" + pid); + String[] statBreakDown = psStat.split(" +"); + long psTime; + + if (pid.isEmpty()) { + psTime = Long.parseLong(statBreakDown[1]) + + Long.parseLong(statBreakDown[2]) + + Long.parseLong(statBreakDown[3]) + + Long.parseLong(statBreakDown[4]); + } else { + psTime = Long.parseLong(statBreakDown[13]) + + Long.parseLong(statBreakDown[14]); + } + + return psTime; + } + + private String getPsStat(String psname) { + String stat = ""; + try { + FileInputStream fs = new FileInputStream("/proc" + psname + "/stat"); + BufferedReader br = new BufferedReader(new InputStreamReader(fs)); + stat = br.readLine(); + fs.close(); + } catch (IOException e) { + Log.d(TAG, "Error retreiving stat. \n"); + } + return stat; + } + } +} diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java index 47d8e283257b..e34286572cb6 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -129,6 +129,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private static final int MSG_REPORT_EMERGENCY_CALL_ACTION = 318; private static final int MSG_STARTED_WAKING_UP = 319; private static final int MSG_FINISHED_GOING_TO_SLEEP = 320; + private static final int MSG_STARTED_GOING_TO_SLEEP = 321; private static final int MSG_KEYGUARD_BOUNCER_CHANGED = 322; private static final int MSG_FACE_UNLOCK_STATE_CHANGED = 327; private static final int MSG_SIM_SUBSCRIPTION_INFO_CHANGED = 328; @@ -170,6 +171,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { * until the Keyguard has been dismissed. */ private boolean mFingerprintAlreadyAuthenticated; + private boolean mGoingToSleep; private boolean mBouncer; private boolean mBootCompleted; @@ -249,6 +251,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { case MSG_REPORT_EMERGENCY_CALL_ACTION: handleReportEmergencyCallAction(); break; + case MSG_STARTED_GOING_TO_SLEEP: + handleStartedGoingToSleep(msg.arg1); + break; case MSG_FINISHED_GOING_TO_SLEEP: handleFinishedGoingToSleep(msg.arg1); break; @@ -884,19 +889,32 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } } - protected void handleFinishedGoingToSleep(int arg1) { + protected void handleStartedGoingToSleep(int arg1) { clearFingerprintRecognized(); final int count = mCallbacks.size(); for (int i = 0; i < count; i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { - cb.onFinishedGoingToSleep(arg1); + cb.onStartedGoingToSleep(arg1); } } + mGoingToSleep = true; mFingerprintAlreadyAuthenticated = false; updateFingerprintListeningState(); } + protected void handleFinishedGoingToSleep(int arg1) { + mGoingToSleep = false; + final int count = mCallbacks.size(); + for (int i = 0; i < count; i++) { + KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); + if (cb != null) { + cb.onFinishedGoingToSleep(arg1); + } + } + updateFingerprintListeningState(); + } + private void handleScreenTurnedOn() { final int count = mCallbacks.size(); for (int i = 0; i < count; i++) { @@ -1032,8 +1050,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } private boolean shouldListenForFingerprint() { - return (mKeyguardIsVisible || !mDeviceInteractive || mBouncer) && !mSwitchingUser - && !mFingerprintAlreadyAuthenticated && !isFingerprintDisabled(getCurrentUser()); + return (mKeyguardIsVisible || !mDeviceInteractive || mBouncer || mGoingToSleep) + && !mSwitchingUser && !mFingerprintAlreadyAuthenticated + && !isFingerprintDisabled(getCurrentUser()); } private void startListeningForFingerprint() { @@ -1605,6 +1624,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { mHandler.sendEmptyMessage(MSG_STARTED_WAKING_UP); } + public void dispatchStartedGoingToSleep(int why) { + mHandler.sendMessage(mHandler.obtainMessage(MSG_STARTED_GOING_TO_SLEEP, why, 0)); + } + public void dispatchFinishedGoingToSleep(int why) { synchronized(this) { mDeviceInteractive = false; @@ -1630,6 +1653,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { return mDeviceInteractive; } + public boolean isGoingToSleep() { + return mGoingToSleep; + } + /** * Find the next SubscriptionId for a SIM in the given state, favoring lower slot numbers first. * @param state diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index 15ffe9f5ca7e..bd6c51c3b89c 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -153,6 +153,12 @@ public class KeyguardUpdateMonitorCallback { public void onStartedWakingUp() { } /** + * Called when the device has started going to sleep. + * @param why see {@link #onFinishedGoingToSleep(int)} + */ + public void onStartedGoingToSleep(int why) { } + + /** * Called when the device has finished going to sleep. * @param why either {@link WindowManagerPolicy#OFF_BECAUSE_OF_ADMIN}, * {@link WindowManagerPolicy#OFF_BECAUSE_OF_USER}, or diff --git a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java index 5062423a964d..52941992c626 100644 --- a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java +++ b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java @@ -25,6 +25,7 @@ import android.net.wifi.WifiManager; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; +import android.telephony.CarrierConfigManager; public class TetherUtil { @@ -62,6 +63,13 @@ public class TetherUtil { return wifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED; } + private static boolean isEntitlementCheckRequired(Context context) { + final CarrierConfigManager configManager = (CarrierConfigManager) context + .getSystemService(Context.CARRIER_CONFIG_SERVICE); + return configManager.getConfig().getBoolean(CarrierConfigManager + .KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL); + } + public static boolean isProvisioningNeeded(Context context) { // Keep in sync with other usage of config_mobile_hotspot_provision_app. // ConnectivityManager#enforceTetherChangePermission @@ -71,6 +79,10 @@ public class TetherUtil { || provisionApp == null) { return false; } + // Check carrier config for entitlement checks + if (isEntitlementCheckRequired(context) == false) { + return false; + } return (provisionApp.length == 2); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index da4ffa4b9433..8a09b7c69f9d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -74,6 +74,8 @@ import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarWindowManager; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -152,6 +154,7 @@ public class KeyguardViewMediator extends SystemUI { private static final int NOTIFY_STARTED_WAKING_UP = 21; private static final int NOTIFY_SCREEN_TURNED_ON = 22; private static final int NOTIFY_SCREEN_TURNED_OFF = 23; + private static final int NOTIFY_STARTED_GOING_TO_SLEEP = 24; /** * The default amount of time we stay awake (used for all key input) @@ -649,6 +652,7 @@ public class KeyguardViewMediator extends SystemUI { final boolean lockImmediately = mLockPatternUtils.getPowerButtonInstantlyLocks(currentUser) || !mLockPatternUtils.isSecure(currentUser); + long timeout = getLockTimeout(); if (mExitSecureCallback != null) { if (DEBUG) Log.d(TAG, "pending exit secure callback cancelled"); @@ -663,9 +667,9 @@ public class KeyguardViewMediator extends SystemUI { } } else if (mShowing) { mPendingReset = true; - } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT + } else if ((why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT && timeout > 0) || (why == WindowManagerPolicy.OFF_BECAUSE_OF_USER && !lockImmediately)) { - doKeyguardLaterLocked(); + doKeyguardLaterLocked(timeout); } else if (!mLockPatternUtils.isLockScreenDisabled(currentUser)) { mPendingLock = true; } @@ -674,6 +678,8 @@ public class KeyguardViewMediator extends SystemUI { playSounds(true); } } + KeyguardUpdateMonitor.getInstance(mContext).dispatchStartedGoingToSleep(why); + notifyStartedGoingToSleep(); } public void onFinishedGoingToSleep(int why) { @@ -699,7 +705,7 @@ public class KeyguardViewMediator extends SystemUI { KeyguardUpdateMonitor.getInstance(mContext).dispatchFinishedGoingToSleep(why); } - private void doKeyguardLaterLocked() { + private long getLockTimeout() { // if the screen turned off because of timeout or the user hit the power button // and we don't need to lock immediately, set an alarm // to enable it a little bit later (i.e, give the user a chance @@ -728,23 +734,30 @@ public class KeyguardViewMediator extends SystemUI { } else { timeout = lockAfterTimeout; } + return timeout; + } - if (timeout <= 0) { - // Lock now + private void doKeyguardLaterLocked() { + long timeout = getLockTimeout(); + if (timeout == 0) { doKeyguardLocked(null); } else { - // Lock in the future - long when = SystemClock.elapsedRealtime() + timeout; - Intent intent = new Intent(DELAYED_KEYGUARD_ACTION); - intent.putExtra("seq", mDelayedShowingSequence); - PendingIntent sender = PendingIntent.getBroadcast(mContext, - 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); - mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, sender); - if (DEBUG) Log.d(TAG, "setting alarm to turn off keyguard, seq = " - + mDelayedShowingSequence); + doKeyguardLaterLocked(timeout); } } + private void doKeyguardLaterLocked(long timeout) { + // Lock in the future + long when = SystemClock.elapsedRealtime() + timeout; + Intent intent = new Intent(DELAYED_KEYGUARD_ACTION); + intent.putExtra("seq", mDelayedShowingSequence); + PendingIntent sender = PendingIntent.getBroadcast(mContext, + 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, sender); + if (DEBUG) Log.d(TAG, "setting alarm to turn off keyguard, seq = " + + mDelayedShowingSequence); + } + private void cancelDoKeyguardLaterLocked() { mDelayedShowingSequence++; } @@ -1090,6 +1103,11 @@ public class KeyguardViewMediator extends SystemUI { mHandler.sendEmptyMessage(VERIFY_UNLOCK); } + private void notifyStartedGoingToSleep() { + if (DEBUG) Log.d(TAG, "notifyStartedGoingToSleep"); + mHandler.sendEmptyMessage(NOTIFY_STARTED_GOING_TO_SLEEP); + } + private void notifyFinishedGoingToSleep() { if (DEBUG) Log.d(TAG, "notifyFinishedGoingToSleep"); mHandler.sendEmptyMessage(NOTIFY_FINISHED_GOING_TO_SLEEP); @@ -1201,6 +1219,9 @@ public class KeyguardViewMediator extends SystemUI { case VERIFY_UNLOCK: handleVerifyUnlock(); break; + case NOTIFY_STARTED_GOING_TO_SLEEP: + handleNotifyStartedGoingToSleep(); + break; case NOTIFY_FINISHED_GOING_TO_SLEEP: handleNotifyFinishedGoingToSleep(); break; @@ -1528,6 +1549,13 @@ public class KeyguardViewMediator extends SystemUI { } } + private void handleNotifyStartedGoingToSleep() { + synchronized (KeyguardViewMediator.this) { + if (DEBUG) Log.d(TAG, "handleNotifyStartedGoingToSleep"); + mStatusBarKeyguardViewManager.onStartedGoingToSleep(); + } + } + /** * Handle message sent by {@link #notifyFinishedGoingToSleep()} * @see #NOTIFY_FINISHED_GOING_TO_SLEEP @@ -1625,6 +1653,30 @@ public class KeyguardViewMediator extends SystemUI { return mViewMediatorCallback; } + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.print(" mSystemReady: "); pw.println(mSystemReady); + pw.print(" mBootCompleted: "); pw.println(mBootCompleted); + pw.print(" mBootSendUserPresent: "); pw.println(mBootSendUserPresent); + pw.print(" mExternallyEnabled: "); pw.println(mExternallyEnabled); + pw.print(" mNeedToReshowWhenReenabled: "); pw.println(mNeedToReshowWhenReenabled); + pw.print(" mShowing: "); pw.println(mShowing); + pw.print(" mInputRestricted: "); pw.println(mInputRestricted); + pw.print(" mOccluded: "); pw.println(mOccluded); + pw.print(" mDelayedShowingSequence: "); pw.println(mDelayedShowingSequence); + pw.print(" mExitSecureCallback: "); pw.println(mExitSecureCallback); + pw.print(" mDeviceInteractive: "); pw.println(mDeviceInteractive); + pw.print(" mGoingToSleep: "); pw.println(mGoingToSleep); + pw.print(" mHiding: "); pw.println(mHiding); + pw.print(" mWaitingUntilKeyguardVisible: "); pw.println(mWaitingUntilKeyguardVisible); + pw.print(" mKeyguardDonePending: "); pw.println(mKeyguardDonePending); + pw.print(" mHideAnimationRun: "); pw.println(mHideAnimationRun); + pw.print(" mPendingReset: "); pw.println(mPendingReset); + pw.print(" mPendingLock: "); pw.println(mPendingLock); + pw.print(" mWakeAndUnlocking: "); pw.println(mWakeAndUnlocking); + pw.print(" mDrawnCallback: "); pw.println(mDrawnCallback); + } + private static class StartKeyguardExitAnimParams { long startTime; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java index 84082dbe0d12..29129638fc6c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java @@ -93,6 +93,8 @@ public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback { private KeyguardViewMediator mKeyguardViewMediator; private ScrimController mScrimController; private PhoneStatusBar mPhoneStatusBar; + private boolean mGoingToSleep; + private int mPendingAuthenticatedUserId = -1; public FingerprintUnlockController(Context context, StatusBarWindowManager statusBarWindowManager, @@ -161,6 +163,10 @@ public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback { @Override public void onFingerprintAuthenticated(int userId) { + if (mUpdateMonitor.isGoingToSleep()) { + mPendingAuthenticatedUserId = userId; + return; + } boolean wasDeviceInteractive = mUpdateMonitor.isDeviceInteractive(); mMode = calculateMode(); if (!wasDeviceInteractive) { @@ -205,6 +211,26 @@ public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback { mPhoneStatusBar.notifyFpAuthModeChanged(); } + @Override + public void onStartedGoingToSleep(int why) { + mPendingAuthenticatedUserId = -1; + } + + @Override + public void onFinishedGoingToSleep(int why) { + if (mPendingAuthenticatedUserId != -1) { + + // Post this to make sure it's executed after the device is fully locked. + mHandler.post(new Runnable() { + @Override + public void run() { + onFingerprintAuthenticated(mPendingAuthenticatedUserId); + } + }); + } + mPendingAuthenticatedUserId = -1; + } + public int getMode() { return mMode; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 548125d4bc32..2bedef70c49a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -312,6 +312,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, boolean mLeaveOpenOnKeyguardHide; KeyguardIndicationController mKeyguardIndicationController; + // Keyguard is going away soon. + private boolean mKeyguardGoingAway; + // Keyguard is actually fading away now. private boolean mKeyguardFadingAway; private long mKeyguardFadingAwayDelay; private long mKeyguardFadingAwayDuration; @@ -485,12 +488,18 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private boolean mLaunchTransitionFadingAway; private ExpandableNotificationRow mDraggedDownRow; private boolean mLaunchCameraOnScreenTurningOn; + private boolean mLaunchCameraOnFinishedGoingToSleep; private PowerManager.WakeLock mGestureWakeLock; private Vibrator mVibrator; // Fingerprint (as computed by getLoggingFingerprint() of the last logged state. private int mLastLoggedStateFingerprint; + /** + * If set, the device has started going to sleep but isn't fully non-interactive yet. + */ + protected boolean mStartedGoingToSleep; + private static final int VISIBLE_LOCATIONS = StackViewState.LOCATION_FIRST_CARD | StackViewState.LOCATION_MAIN_AREA; @@ -3558,6 +3567,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // Treat Keyguard exit animation as an app transition to achieve nice transition for status // bar. + mKeyguardGoingAway = true; mIconController.appTransitionPending(); } @@ -3589,6 +3599,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, */ public void finishKeyguardFadingAway() { mKeyguardFadingAway = false; + mKeyguardGoingAway = false; } public void stopWaitingForKeyguardExit() { @@ -3903,15 +3914,32 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, disable(mDisabledUnmodified1, mDisabledUnmodified2, true /* animate */); } + public void onStartedGoingToSleep() { + mStartedGoingToSleep = true; + } + public void onFinishedGoingToSleep() { mNotificationPanel.onAffordanceLaunchEnded(); releaseGestureWakeLock(); mLaunchCameraOnScreenTurningOn = false; + mStartedGoingToSleep = false; mDeviceInteractive = false; mWakeUpComingFromTouch = false; mWakeUpTouchLocation = null; mStackScroller.setAnimationsEnabled(false); updateVisibleToUser(); + if (mLaunchCameraOnFinishedGoingToSleep) { + mLaunchCameraOnFinishedGoingToSleep = false; + + // 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(); + } + }); + } } public void onStartedWakingUp() { @@ -3931,7 +3959,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } private void vibrateForCameraGesture() { - mVibrator.vibrate(750L); + // Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep. + mVibrator.vibrate(new long[] { 0, 750L }, -1 /* repeat */); } public void onScreenTurnedOn() { @@ -4079,9 +4108,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, public void appTransitionStarting(long startTime, long duration) { // Use own timings when Keyguard is going away, see keyguardGoingAway and - // setKeyguardFadingAway. When duration is 0, skip this one because no animation is really - // playing. - if (!mKeyguardFadingAway && duration > 0) { + // setKeyguardFadingAway. + if (!mKeyguardGoingAway) { mIconController.appTransitionStarting(startTime, duration); } if (mIconPolicy != null) { @@ -4091,6 +4119,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, @Override public void onCameraLaunchGestureDetected() { + if (mStartedGoingToSleep) { + mLaunchCameraOnFinishedGoingToSleep = true; + return; + } if (!mNotificationPanel.canCameraGestureBeLaunched( mStatusBarKeyguardViewManager.isShowing() && mExpandedVisible)) { return; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index e26f42301830..394ff3f8245c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -164,6 +164,10 @@ public class StatusBarKeyguardViewManager { } } + public void onStartedGoingToSleep() { + mPhoneStatusBar.onStartedGoingToSleep(); + } + public void onFinishedGoingToSleep() { mDeviceInteractive = false; mPhoneStatusBar.onFinishedGoingToSleep(); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index d1e1683d6478..6190a5ab357e 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -2270,8 +2270,9 @@ public class ConnectivityService extends IConnectivityManager.Stub mNetworkRequestInfoLogs.log("REGISTER " + nri); if (!nri.isRequest) { for (NetworkAgentInfo network : mNetworkAgentInfos.values()) { - if (network.satisfiesImmutableCapabilitiesOf(nri.request)) { - updateSignalStrengthThresholds(network); + if (nri.request.networkCapabilities.hasSignalStrength() && + network.satisfiesImmutableCapabilitiesOf(nri.request)) { + updateSignalStrengthThresholds(network, "REGISTER", nri.request); } } } @@ -2388,8 +2389,9 @@ public class ConnectivityService extends IConnectivityManager.Stub // if this listen request applies and remove it. for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { nai.networkRequests.remove(nri.request.requestId); - if (nai.satisfiesImmutableCapabilitiesOf(nri.request)) { - updateSignalStrengthThresholds(nai); + if (nri.request.networkCapabilities.hasSignalStrength() && + nai.satisfiesImmutableCapabilitiesOf(nri.request)) { + updateSignalStrengthThresholds(nai, "RELEASE", nri.request); } } } @@ -3639,9 +3641,24 @@ public class ConnectivityService extends IConnectivityManager.Stub return new ArrayList<Integer>(thresholds); } - private void updateSignalStrengthThresholds(NetworkAgentInfo nai) { + private void updateSignalStrengthThresholds( + NetworkAgentInfo nai, String reason, NetworkRequest request) { + ArrayList<Integer> thresholdsArray = getSignalStrengthThresholds(nai); Bundle thresholds = new Bundle(); - thresholds.putIntegerArrayList("thresholds", getSignalStrengthThresholds(nai)); + thresholds.putIntegerArrayList("thresholds", thresholdsArray); + + // TODO: Switch to VDBG. + if (DBG) { + String detail; + if (request != null && request.networkCapabilities.hasSignalStrength()) { + detail = reason + " " + request.networkCapabilities.getSignalStrength(); + } else { + detail = reason; + } + log(String.format("updateSignalStrengthThresholds: %s, sending %s to %s", + detail, Arrays.toString(thresholdsArray.toArray()), nai.name())); + } + nai.asyncChannel.sendMessage( android.net.NetworkAgent.CMD_SET_SIGNAL_STRENGTH_THRESHOLDS, 0, 0, thresholds); @@ -4624,7 +4641,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // so we could decide to tear it down immediately afterwards. That's fine though - on // disconnection NetworkAgents should stop any signal strength monitoring they have been // doing. - updateSignalStrengthThresholds(networkAgent); + updateSignalStrengthThresholds(networkAgent, "CONNECT", null); // Consider network even though it is not yet validated. rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP); diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java index 69f0cef6d39e..bd7d4b27e02c 100644 --- a/services/core/java/com/android/server/GestureLauncherService.java +++ b/services/core/java/com/android/server/GestureLauncherService.java @@ -101,8 +101,8 @@ public class GestureLauncherService extends SystemService { * Whether camera double tap power button gesture is currently enabled; */ private boolean mCameraDoubleTapPowerEnabled; - private long mLastPowerDownWhileNonInteractive = 0; - + private long mLastPowerDownWhileNonInteractive; + private long mLastPowerDownWhileInteractive; public GestureLauncherService(Context context) { super(context); @@ -251,23 +251,30 @@ public class GestureLauncherService extends SystemService { public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive) { boolean launched = false; + boolean intercept = false; synchronized (this) { if (!mCameraDoubleTapPowerEnabled) { mLastPowerDownWhileNonInteractive = 0; + mLastPowerDownWhileInteractive = 0; return false; } if (event.getEventTime() - mLastPowerDownWhileNonInteractive < CAMERA_POWER_DOUBLE_TAP_TIME_MS) { launched = true; + intercept = true; + } else if (event.getEventTime() - mLastPowerDownWhileInteractive + < CAMERA_POWER_DOUBLE_TAP_TIME_MS) { + launched = true; } mLastPowerDownWhileNonInteractive = interactive ? 0 : event.getEventTime(); + mLastPowerDownWhileInteractive = interactive ? event.getEventTime() : 0; } if (launched) { Slog.i(TAG, "Power button double tap gesture detected, launching camera."); launched = handleCameraLaunchGesture(false /* useWakelock */, MetricsLogger.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE); } - return launched; + return intercept && launched; } /** diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 30f4dce310ca..c2284224502d 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -59,6 +59,7 @@ public class VibratorService extends IVibratorService.Stub implements InputManager.InputDeviceListener { private static final String TAG = "VibratorService"; private static final boolean DEBUG = false; + private static final String SYSTEM_UI_PACKAGE = "com.android.systemui"; private final LinkedList<Vibration> mVibrations; private final LinkedList<VibrationInfo> mPreviousVibrations; @@ -147,7 +148,8 @@ public class VibratorService extends IVibratorService.Stub } public boolean isSystemHapticFeedback() { - return (mUid == Process.SYSTEM_UID || mUid == 0) && mRepeat < 0; + return (mUid == Process.SYSTEM_UID || mUid == 0 || SYSTEM_UI_PACKAGE.equals(mOpPkg)) + && mRepeat < 0; } } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 335288da005b..62768c3b6475 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -171,12 +171,11 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void publish(Context context) { mContext = context; - ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder()); - mStats.setNumSpeedSteps(new PowerProfile(mContext).getNumSpeedSteps()); mStats.setRadioScanningTimeout(mContext.getResources().getInteger( com.android.internal.R.integer.config_radioScanningTimeout) * 1000L); mStats.setPowerProfile(new PowerProfile(context)); + ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder()); } /** diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java index aca699152e02..5fd39c02a10a 100644 --- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java +++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java @@ -18,6 +18,7 @@ package com.android.server.connectivity; import static android.system.OsConstants.*; +import android.net.LinkAddress; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkUtils; @@ -27,6 +28,7 @@ import android.system.ErrnoException; import android.system.Os; import android.system.StructTimeval; import android.text.TextUtils; +import android.util.Pair; import com.android.internal.util.IndentingPrintWriter; @@ -149,6 +151,8 @@ public class NetworkDiagnostics { } private final Map<InetAddress, Measurement> mIcmpChecks = new HashMap<>(); + private final Map<Pair<InetAddress, InetAddress>, Measurement> mExplicitSourceIcmpChecks = + new HashMap<>(); private final Map<InetAddress, Measurement> mDnsUdpChecks = new HashMap<>(); private final String mDescription; @@ -178,7 +182,11 @@ public class NetworkDiagnostics { for (RouteInfo route : mLinkProperties.getRoutes()) { if (route.hasGateway()) { - prepareIcmpMeasurement(route.getGateway()); + InetAddress gateway = route.getGateway(); + prepareIcmpMeasurement(gateway); + if (route.isIPv6Default()) { + prepareExplicitSourceIcmpMeasurements(gateway); + } } } for (InetAddress nameserver : mLinkProperties.getDnsServers()) { @@ -213,6 +221,20 @@ public class NetworkDiagnostics { } } + private void prepareExplicitSourceIcmpMeasurements(InetAddress target) { + for (LinkAddress l : mLinkProperties.getLinkAddresses()) { + InetAddress source = l.getAddress(); + if (source instanceof Inet6Address && l.isGlobalPreferred()) { + Pair<InetAddress, InetAddress> srcTarget = new Pair<>(source, target); + if (!mExplicitSourceIcmpChecks.containsKey(srcTarget)) { + Measurement measurement = new Measurement(); + measurement.thread = new Thread(new IcmpCheck(source, target, measurement)); + mExplicitSourceIcmpChecks.put(srcTarget, measurement); + } + } + } + } + private void prepareDnsMeasurement(InetAddress target) { if (!mDnsUdpChecks.containsKey(target)) { Measurement measurement = new Measurement(); @@ -222,13 +244,16 @@ public class NetworkDiagnostics { } private int totalMeasurementCount() { - return mIcmpChecks.size() + mDnsUdpChecks.size(); + return mIcmpChecks.size() + mExplicitSourceIcmpChecks.size() + mDnsUdpChecks.size(); } private void startMeasurements() { for (Measurement measurement : mIcmpChecks.values()) { measurement.thread.start(); } + for (Measurement measurement : mExplicitSourceIcmpChecks.values()) { + measurement.thread.start(); + } for (Measurement measurement : mDnsUdpChecks.values()) { measurement.thread.start(); } @@ -261,6 +286,10 @@ public class NetworkDiagnostics { pw.println(entry.getValue().toString()); } } + for (Map.Entry<Pair<InetAddress, InetAddress>, Measurement> entry : + mExplicitSourceIcmpChecks.entrySet()) { + pw.println(entry.getValue().toString()); + } for (Map.Entry<InetAddress, Measurement> entry : mDnsUdpChecks.entrySet()) { if (entry.getKey() instanceof Inet4Address) { pw.println(entry.getValue().toString()); @@ -276,13 +305,15 @@ public class NetworkDiagnostics { private class SimpleSocketCheck implements Closeable { + protected final InetAddress mSource; // Usually null. protected final InetAddress mTarget; protected final int mAddressFamily; protected final Measurement mMeasurement; protected FileDescriptor mFileDescriptor; protected SocketAddress mSocketAddress; - protected SimpleSocketCheck(InetAddress target, Measurement measurement) { + protected SimpleSocketCheck( + InetAddress source, InetAddress target, Measurement measurement) { mMeasurement = measurement; if (target instanceof Inet6Address) { @@ -301,6 +332,14 @@ public class NetworkDiagnostics { mTarget = target; mAddressFamily = AF_INET; } + + // We don't need to check the scope ID here because we currently only do explicit-source + // measurements from global IPv6 addresses. + mSource = source; + } + + protected SimpleSocketCheck(InetAddress target, Measurement measurement) { + this(null, target, measurement); } protected void setupSocket( @@ -314,6 +353,9 @@ public class NetworkDiagnostics { SOL_SOCKET, SO_RCVTIMEO, StructTimeval.fromMillis(readTimeout)); // TODO: Use IP_RECVERR/IPV6_RECVERR, pending OsContants availability. mNetwork.bindSocket(mFileDescriptor); + if (mSource != null) { + Os.bind(mFileDescriptor, mSource, 0); + } Os.connect(mFileDescriptor, mTarget, dstPort); mSocketAddress = Os.getsockname(mFileDescriptor); } @@ -343,8 +385,8 @@ public class NetworkDiagnostics { private final int mProtocol; private final int mIcmpType; - public IcmpCheck(InetAddress target, Measurement measurement) { - super(target, measurement); + public IcmpCheck(InetAddress source, InetAddress target, Measurement measurement) { + super(source, target, measurement); if (mAddressFamily == AF_INET6) { mProtocol = IPPROTO_ICMPV6; @@ -359,6 +401,10 @@ public class NetworkDiagnostics { mMeasurement.description += " dst{" + mTarget.getHostAddress() + "}"; } + public IcmpCheck(InetAddress target, Measurement measurement) { + this(null, target, measurement); + } + @Override public void run() { // Check if this measurement has already failed during setup. diff --git a/services/core/java/com/android/server/display/DisplayAdapter.java b/services/core/java/com/android/server/display/DisplayAdapter.java index 6ba25a53248d..701b9f179f9c 100644 --- a/services/core/java/com/android/server/display/DisplayAdapter.java +++ b/services/core/java/com/android/server/display/DisplayAdapter.java @@ -49,6 +49,13 @@ abstract class DisplayAdapter { */ private static final AtomicInteger NEXT_DISPLAY_MODE_ID = new AtomicInteger(1); // 0 = no mode. + /** + * Used to generate globally unique color transform ids. + * + * Valid IDs start at 1 with 0 as the sentinel value for the default mode. + */ + private static final AtomicInteger NEXT_COLOR_TRANSFORM_ID = new AtomicInteger(1); + // Called with SyncRoot lock held. public DisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener, String name) { @@ -134,6 +141,11 @@ abstract class DisplayAdapter { NEXT_DISPLAY_MODE_ID.getAndIncrement(), width, height, refreshRate); } + public static Display.ColorTransform createColorTransform(int colorTransform) { + return new Display.ColorTransform( + NEXT_COLOR_TRANSFORM_ID.getAndIncrement(), colorTransform); + } + public interface Listener { public void onDisplayDeviceEvent(DisplayDevice device, int event); public void onTraversalRequested(); diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index 93bda46873a1..7af0bdbdc357 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -135,7 +135,7 @@ abstract class DisplayDevice { /** * Sets the mode, if supported. */ - public void requestModeInTransactionLocked(int id) { + public void requestColorTransformAndModeInTransactionLocked(int colorTransformId, int modeId) { } /** diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java index 97ada1542466..55ba3025c516 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java @@ -155,6 +155,15 @@ final class DisplayDeviceInfo { */ public Display.Mode[] supportedModes = Display.Mode.EMPTY_ARRAY; + /** The active color transform of the display */ + public int colorTransformId; + + /** The default color transform of the display */ + public int defaultColorTransformId; + + /** The supported color transforms of the display */ + public Display.ColorTransform[] supportedColorTransforms = Display.ColorTransform.EMPTY_ARRAY; + /** * The nominal apparent density of the display in DPI used for layout calculations. * This density is sensitive to the viewing distance. A big TV and a tablet may have @@ -276,6 +285,9 @@ final class DisplayDeviceInfo { || modeId != other.modeId || defaultModeId != other.defaultModeId || !Arrays.equals(supportedModes, other.supportedModes) + || colorTransformId != other.colorTransformId + || defaultColorTransformId != other.defaultColorTransformId + || !Arrays.equals(supportedColorTransforms, other.supportedColorTransforms) || densityDpi != other.densityDpi || xDpi != other.xDpi || yDpi != other.yDpi @@ -306,6 +318,9 @@ final class DisplayDeviceInfo { modeId = other.modeId; defaultModeId = other.defaultModeId; supportedModes = other.supportedModes; + colorTransformId = other.colorTransformId; + defaultColorTransformId = other.defaultColorTransformId; + supportedColorTransforms = other.supportedColorTransforms; densityDpi = other.densityDpi; xDpi = other.xDpi; yDpi = other.yDpi; @@ -331,6 +346,9 @@ final class DisplayDeviceInfo { sb.append(", modeId ").append(modeId); sb.append(", defaultModeId ").append(defaultModeId); sb.append(", supportedModes ").append(Arrays.toString(supportedModes)); + sb.append(", colorTransformId ").append(colorTransformId); + sb.append(", defaultColorTransformId ").append(defaultColorTransformId); + sb.append(", supportedColorTransforms ").append(Arrays.toString(supportedColorTransforms)); sb.append(", density ").append(densityDpi); sb.append(", ").append(xDpi).append(" x ").append(yDpi).append(" dpi"); sb.append(", appVsyncOff ").append(appVsyncOffsetNanos); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index b2ab797a409e..6a6570b97205 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -540,6 +540,17 @@ public final class DisplayManagerService extends SystemService { } } + private void requestColorTransformInternal(int displayId, int colorTransformId) { + synchronized (mSyncRoot) { + LogicalDisplay display = mLogicalDisplays.get(displayId); + if (display != null && + display.getRequestedColorTransformIdLocked() != colorTransformId) { + display.setRequestedColorTransformIdLocked(colorTransformId); + scheduleTraversalLocked(false); + } + } + } + private int createVirtualDisplayInternal(IVirtualDisplayCallback callback, IMediaProjection projection, int callingUid, String packageName, String name, int width, int height, int densityDpi, Surface surface, int flags) { @@ -1340,6 +1351,19 @@ public final class DisplayManagerService extends SystemService { } @Override // Binder call + public void requestColorTransform(int displayId, int colorTransformId) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.CONFIGURE_DISPLAY_COLOR_TRANSFORM, + "Permission required to change the display color transform"); + final long token = Binder.clearCallingIdentity(); + try { + requestColorTransformInternal(displayId, colorTransformId); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call public int createVirtualDisplay(IVirtualDisplayCallback callback, IMediaProjection projection, String packageName, String name, int width, int height, int densityDpi, Surface surface, int flags) { diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 517a825f6fcb..be37f524eb8d 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -31,6 +31,7 @@ import android.os.SystemProperties; import android.os.Trace; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.view.Display; import android.view.DisplayEventReceiver; import android.view.Surface; @@ -38,6 +39,7 @@ import android.view.SurfaceControl; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; /** * A display adapter for the local displays managed by Surface Flinger. @@ -143,14 +145,22 @@ final class LocalDisplayAdapter extends DisplayAdapter { private final int mBuiltInDisplayId; private final Light mBacklight; private final SparseArray<DisplayModeRecord> mSupportedModes = new SparseArray<>(); + private final SparseArray<Display.ColorTransform> mSupportedColorTransforms = + new SparseArray<>(); private DisplayDeviceInfo mInfo; private boolean mHavePendingChanges; private int mState = Display.STATE_UNKNOWN; private int mBrightness = PowerManager.BRIGHTNESS_DEFAULT; + private int mActivePhysIndex; private int mDefaultModeId; private int mActiveModeId; private boolean mActiveModeInvalid; + private int mDefaultColorTransformId; + private int mActiveColorTransformId; + private boolean mActiveColorTransformInvalid; + + private SurfaceControl.PhysicalDisplayInfo mDisplayInfos[]; public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId, SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) { @@ -167,22 +177,73 @@ final class LocalDisplayAdapter extends DisplayAdapter { public boolean updatePhysicalDisplayInfoLocked( SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) { + mDisplayInfos = Arrays.copyOf(physicalDisplayInfos, physicalDisplayInfos.length); + mActivePhysIndex = activeDisplayInfo; + ArrayList<Display.ColorTransform> colorTransforms = new ArrayList<>(); + + // Build an updated list of all existing color transforms. + boolean colorTransformsAdded = false; + Display.ColorTransform activeColorTransform = null; + for (int i = 0; i < physicalDisplayInfos.length; i++) { + SurfaceControl.PhysicalDisplayInfo info = physicalDisplayInfos[i]; + // First check to see if we've already added this color transform + boolean existingMode = false; + for (int j = 0; j < colorTransforms.size(); j++) { + if (colorTransforms.get(j).getColorTransform() == info.colorTransform) { + existingMode = true; + break; + } + } + if (existingMode) { + continue; + } + Display.ColorTransform colorTransform = findColorTransform(info); + if (colorTransform == null) { + colorTransform = createColorTransform(info.colorTransform); + colorTransformsAdded = true; + } + colorTransforms.add(colorTransform); + if (i == activeDisplayInfo) { + activeColorTransform = colorTransform; + } + } + // Build an updated list of all existing modes. - boolean modesAdded = false; - DisplayModeRecord activeRecord = null; ArrayList<DisplayModeRecord> records = new ArrayList<DisplayModeRecord>(); + boolean modesAdded = false; for (int i = 0; i < physicalDisplayInfos.length; i++) { SurfaceControl.PhysicalDisplayInfo info = physicalDisplayInfos[i]; + // First, check to see if we've already added a matching mode. Since not all + // configuration options are exposed via Display.Mode, it's possible that we have + // multiple PhysicalDisplayInfos that would generate the same Display.Mode. + boolean existingMode = false; + for (int j = 0; j < records.size(); j++) { + if (records.get(j).hasMatchingMode(info)) { + existingMode = true; + break; + } + } + if (existingMode) { + continue; + } + // If we haven't already added a mode for this configuration to the new set of + // supported modes then check to see if we have one in the prior set of supported + // modes to reuse. DisplayModeRecord record = findDisplayModeRecord(info); - if (record != null) { - record.mPhysIndex = i; - } else { - record = new DisplayModeRecord(info, i); + if (record == null) { + record = new DisplayModeRecord(info); modesAdded = true; } records.add(record); - if (i == activeDisplayInfo) { + } + + // Get the currently active mode + DisplayModeRecord activeRecord = null; + for (int i = 0; i < records.size(); i++) { + DisplayModeRecord record = records.get(i); + if (record.hasMatchingMode(physicalDisplayInfos[activeDisplayInfo])){ activeRecord = record; + break; } } // Check whether surface flinger spontaneously changed modes out from under us. Schedule @@ -192,25 +253,48 @@ final class LocalDisplayAdapter extends DisplayAdapter { mActiveModeInvalid = true; sendTraversalRequestLocked(); } - // If no modes were added and we have the same number of modes as before, then nothing - // actually changed except possibly the physical index (which we only care about when - // setting the mode) so we're done. - if (records.size() == mSupportedModes.size() && !modesAdded) { + // Check whether surface flinger spontaneously changed color transforms out from under + // us. + if (mActiveColorTransformId != 0 + && mActiveColorTransformId != activeColorTransform.getId()) { + mActiveColorTransformInvalid = true; + sendTraversalRequestLocked(); + } + + boolean colorTransformsChanged = + colorTransforms.size() != mSupportedColorTransforms.size() + || colorTransformsAdded; + boolean recordsChanged = records.size() != mSupportedModes.size() || modesAdded; + // If neither the records nor the supported color transforms have changed then we're + // done here. + if (!recordsChanged && !colorTransformsChanged) { return false; } // Update the index of modes. mHavePendingChanges = true; + mSupportedModes.clear(); for (DisplayModeRecord record : records) { mSupportedModes.put(record.mMode.getModeId(), record); } - // Update the default mode if needed. - if (mSupportedModes.indexOfKey(mDefaultModeId) < 0) { + mSupportedColorTransforms.clear(); + for (Display.ColorTransform colorTransform : colorTransforms) { + mSupportedColorTransforms.put(colorTransform.getId(), colorTransform); + } + + // Update the default mode and color transform if needed. This needs to be done in + // tandem so we always have a default state to fall back to. + if (findDisplayInfoIndexLocked(mDefaultColorTransformId, mDefaultModeId) < 0) { if (mDefaultModeId != 0) { - Slog.w(TAG, "Default display mode no longer available, using currently active" - + " mode as default."); + Slog.w(TAG, "Default display mode no longer available, using currently" + + " active mode as default."); } mDefaultModeId = activeRecord.mMode.getModeId(); + if (mDefaultColorTransformId != 0) { + Slog.w(TAG, "Default color transform no longer available, using currently" + + " active color transform as default"); + } + mDefaultColorTransformId = activeColorTransform.getId(); } // Determine whether the active mode is still there. if (mSupportedModes.indexOfKey(mActiveModeId) < 0) { @@ -221,6 +305,16 @@ final class LocalDisplayAdapter extends DisplayAdapter { mActiveModeId = mDefaultModeId; mActiveModeInvalid = true; } + + // Determine whether the active color transform is still there. + if (mSupportedColorTransforms.indexOfKey(mActiveColorTransformId) < 0) { + if (mActiveColorTransformId != 0) { + Slog.w(TAG, "Active color transform no longer available, reverting" + + " to default transform."); + } + mActiveColorTransformId = mDefaultColorTransformId; + mActiveColorTransformInvalid = true; + } // Schedule traversals so that we apply pending changes. sendTraversalRequestLocked(); return true; @@ -229,13 +323,23 @@ final class LocalDisplayAdapter extends DisplayAdapter { private DisplayModeRecord findDisplayModeRecord(SurfaceControl.PhysicalDisplayInfo info) { for (int i = 0; i < mSupportedModes.size(); i++) { DisplayModeRecord record = mSupportedModes.valueAt(i); - if (record.mPhys.equals(info)) { + if (record.hasMatchingMode(info)) { return record; } } return null; } + private Display.ColorTransform findColorTransform(SurfaceControl.PhysicalDisplayInfo info) { + for (int i = 0; i < mSupportedColorTransforms.size(); i++) { + Display.ColorTransform transform = mSupportedColorTransforms.valueAt(i); + if (transform.getColorTransform() == info.colorTransform) { + return transform; + } + } + return null; + } + @Override public void applyPendingDisplayDeviceInfoChangesLocked() { if (mHavePendingChanges) { @@ -247,7 +351,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { @Override public DisplayDeviceInfo getDisplayDeviceInfoLocked() { if (mInfo == null) { - SurfaceControl.PhysicalDisplayInfo phys = mSupportedModes.get(mActiveModeId).mPhys; + SurfaceControl.PhysicalDisplayInfo phys = mDisplayInfos[mActivePhysIndex]; mInfo = new DisplayDeviceInfo(); mInfo.width = phys.width; mInfo.height = phys.height; @@ -258,6 +362,13 @@ final class LocalDisplayAdapter extends DisplayAdapter { DisplayModeRecord record = mSupportedModes.valueAt(i); mInfo.supportedModes[i] = record.mMode; } + mInfo.colorTransformId = mActiveColorTransformId; + mInfo.defaultColorTransformId = mDefaultColorTransformId; + mInfo.supportedColorTransforms = + new Display.ColorTransform[mSupportedColorTransforms.size()]; + for (int i = 0; i < mSupportedColorTransforms.size(); i++) { + mInfo.supportedColorTransforms[i] = mSupportedColorTransforms.valueAt(i); + } mInfo.appVsyncOffsetNanos = phys.appVsyncOffsetNanos; mInfo.presentationDeadlineNanos = phys.presentationDeadlineNanos; mInfo.state = mState; @@ -402,7 +513,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { } @Override - public void requestModeInTransactionLocked(int modeId) { + public void requestColorTransformAndModeInTransactionLocked( + int colorTransformId, int modeId) { if (modeId == 0) { modeId = mDefaultModeId; } else if (mSupportedModes.indexOfKey(modeId) < 0) { @@ -410,13 +522,37 @@ final class LocalDisplayAdapter extends DisplayAdapter { + " reverting to default display mode."); modeId = mDefaultModeId; } - if (mActiveModeId == modeId && !mActiveModeInvalid) { + + if (colorTransformId == 0) { + colorTransformId = mDefaultColorTransformId; + } else if (mSupportedColorTransforms.indexOfKey(colorTransformId) < 0) { + Slog.w(TAG, "Requested color transform " + colorTransformId + " is not supported" + + " by this display, reverting to the default color transform"); + colorTransformId = mDefaultColorTransformId; + } + int physIndex = findDisplayInfoIndexLocked(colorTransformId, modeId); + if (physIndex < 0) { + Slog.w(TAG, "Requested color transform, mode ID pair (" + colorTransformId + ", " + + modeId + ") not available, trying color transform with default mode ID"); + modeId = mDefaultModeId; + physIndex = findDisplayInfoIndexLocked(colorTransformId, modeId); + if (physIndex < 0) { + Slog.w(TAG, "Requested color transform with default mode ID still not" + + " available, falling back to default color transform with default" + + " mode."); + colorTransformId = mDefaultColorTransformId; + physIndex = findDisplayInfoIndexLocked(colorTransformId, modeId); + } + } + if (physIndex > 0 && mActivePhysIndex == physIndex) { return; } - DisplayModeRecord record = mSupportedModes.get(modeId); - SurfaceControl.setActiveConfig(getDisplayTokenLocked(), record.mPhysIndex); + SurfaceControl.setActiveConfig(getDisplayTokenLocked(), physIndex); + mActivePhysIndex = physIndex; mActiveModeId = modeId; mActiveModeInvalid = false; + mActiveColorTransformId = colorTransformId; + mActiveColorTransformInvalid = false; updateDeviceInfoLocked(); } @@ -424,10 +560,43 @@ final class LocalDisplayAdapter extends DisplayAdapter { public void dumpLocked(PrintWriter pw) { super.dumpLocked(pw); pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId); + pw.println("mActivePhysIndex=" + mActivePhysIndex); pw.println("mActiveModeId=" + mActiveModeId); + pw.println("mActiveColorTransformId=" + mActiveColorTransformId); pw.println("mState=" + Display.stateToString(mState)); pw.println("mBrightness=" + mBrightness); pw.println("mBacklight=" + mBacklight); + pw.println("mDisplayInfos="); + for (int i = 0; i < mDisplayInfos.length; i++) { + pw.println(" " + mDisplayInfos[i]); + } + pw.println("mSupportedModes="); + for (int i = 0; i < mSupportedModes.size(); i++) { + pw.println(" " + mSupportedModes.valueAt(i)); + } + pw.println("mSupportedColorTransforms=["); + for (int i = 0; i < mSupportedColorTransforms.size(); i++) { + if (i != 0) { + pw.print(", "); + } + pw.print(mSupportedColorTransforms.valueAt(i)); + } + pw.println("]"); + } + + private int findDisplayInfoIndexLocked(int colorTransformId, int modeId) { + DisplayModeRecord record = mSupportedModes.get(modeId); + Display.ColorTransform transform = mSupportedColorTransforms.get(colorTransformId); + if (record != null && transform != null) { + for (int i = 0; i < mDisplayInfos.length; i++) { + SurfaceControl.PhysicalDisplayInfo info = mDisplayInfos[i]; + if (info.colorTransform == transform.getColorTransform() + && record.hasMatchingMode(info)){ + return i; + } + } + } + return -1; } private void updateDeviceInfoLocked() { @@ -441,13 +610,28 @@ final class LocalDisplayAdapter extends DisplayAdapter { */ private static final class DisplayModeRecord { public final Display.Mode mMode; - public final SurfaceControl.PhysicalDisplayInfo mPhys; - public int mPhysIndex; - public DisplayModeRecord(SurfaceControl.PhysicalDisplayInfo phys, int physIndex) { + public DisplayModeRecord(SurfaceControl.PhysicalDisplayInfo phys) { mMode = createMode(phys.width, phys.height, phys.refreshRate); - mPhys = phys; - mPhysIndex = physIndex; + } + + /** + * Returns whether the mode generated by the given PhysicalDisplayInfo matches the mode + * contained by the record modulo mode ID. + * + * Note that this doesn't necessarily mean the the PhysicalDisplayInfos are identical, just + * that they generate identical modes. + */ + public boolean hasMatchingMode(SurfaceControl.PhysicalDisplayInfo info) { + int modeRefreshRate = Float.floatToIntBits(mMode.getRefreshRate()); + int displayInfoRefreshRate = Float.floatToIntBits(info.refreshRate); + return mMode.getPhysicalWidth() == info.width + && mMode.getPhysicalHeight() == info.height + && modeRefreshRate == displayInfoRefreshRate; + } + + public String toString() { + return "DisplayModeRecord{mMode=" + mMode + "}"; } } diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 6efc99a0c342..6dae397cff21 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -74,6 +74,7 @@ final class LogicalDisplay { private boolean mHasContent; private int mRequestedModeId; + private int mRequestedColorTransformId; // The display offsets to apply to the display projection. private int mDisplayOffsetX; @@ -235,6 +236,11 @@ final class LogicalDisplay { mBaseDisplayInfo.defaultModeId = deviceInfo.defaultModeId; mBaseDisplayInfo.supportedModes = Arrays.copyOf( deviceInfo.supportedModes, deviceInfo.supportedModes.length); + mBaseDisplayInfo.colorTransformId = deviceInfo.colorTransformId; + mBaseDisplayInfo.defaultColorTransformId = deviceInfo.defaultColorTransformId; + mBaseDisplayInfo.supportedColorTransforms = Arrays.copyOf( + deviceInfo.supportedColorTransforms, + deviceInfo.supportedColorTransforms.length); mBaseDisplayInfo.logicalDensityDpi = deviceInfo.densityDpi; mBaseDisplayInfo.physicalXDpi = deviceInfo.xDpi; mBaseDisplayInfo.physicalYDpi = deviceInfo.yDpi; @@ -275,11 +281,12 @@ final class LogicalDisplay { // Set the layer stack. device.setLayerStackInTransactionLocked(isBlanked ? BLANK_LAYER_STACK : mLayerStack); - // Set the mode. + // Set the color transform and mode. if (device == mPrimaryDisplayDevice) { - device.requestModeInTransactionLocked(mRequestedModeId); + device.requestColorTransformAndModeInTransactionLocked( + mRequestedColorTransformId, mRequestedModeId); } else { - device.requestModeInTransactionLocked(0); // Revert to default. + device.requestColorTransformAndModeInTransactionLocked(0, 0); // Revert to default. } // Only grab the display info now as it may have been changed based on the requests above. @@ -383,6 +390,18 @@ final class LogicalDisplay { } /** + * Requests the given color transform. + */ + public void setRequestedColorTransformIdLocked(int colorTransformId) { + mRequestedColorTransformId = colorTransformId; + } + + /** Returns the pending requested color transform. */ + public int getRequestedColorTransformIdLocked() { + return mRequestedColorTransformId; + } + + /** * Gets the burn-in offset in X. */ public int getDisplayOffsetXLocked() { @@ -409,6 +428,7 @@ final class LogicalDisplay { pw.println("mLayerStack=" + mLayerStack); pw.println("mHasContent=" + mHasContent); pw.println("mRequestedMode=" + mRequestedModeId); + pw.println("mRequestedColorTransformId=" + mRequestedColorTransformId); pw.println("mDisplayOffset=(" + mDisplayOffsetX + ", " + mDisplayOffsetY + ")"); pw.println("mPrimaryDisplayDevice=" + (mPrimaryDisplayDevice != null ? mPrimaryDisplayDevice.getNameLocked() : "null")); diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java index 0bddff076c18..cf6264ace34f 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java +++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java @@ -310,7 +310,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter { } @Override - public void requestModeInTransactionLocked(int id) { + public void requestColorTransformAndModeInTransactionLocked(int color, int id) { int index = -1; if (id == 0) { // Use the default. diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java index ea7d85e36944..ec7c1c437d92 100644 --- a/services/core/java/com/android/server/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java @@ -337,7 +337,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe return; } stopPendingOperations(true); - mEnrollClient = new ClientMonitor(token, receiver, groupId, restricted); + mEnrollClient = new ClientMonitor(token, receiver, groupId, restricted, token.toString()); final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC); try { final int result = daemon.enroll(cryptoToken, groupId, timeout); @@ -417,14 +417,15 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe } void startAuthentication(IBinder token, long opId, int groupId, - IFingerprintServiceReceiver receiver, int flags, boolean restricted) { + IFingerprintServiceReceiver receiver, int flags, boolean restricted, + String opPackageName) { IFingerprintDaemon daemon = getFingerprintDaemon(); if (daemon == null) { Slog.w(TAG, "startAuthentication: no fingeprintd!"); return; } stopPendingOperations(true); - mAuthClient = new ClientMonitor(token, receiver, groupId, restricted); + mAuthClient = new ClientMonitor(token, receiver, groupId, restricted, opPackageName); if (inLockoutMode()) { Slog.v(TAG, "In lockout mode; disallowing authentication"); if (!mAuthClient.sendError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) { @@ -481,7 +482,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe } stopPendingOperations(true); - mRemoveClient = new ClientMonitor(token, receiver, userId, restricted); + mRemoveClient = new ClientMonitor(token, receiver, userId, restricted, token.toString()); // The fingerprint template ids will be removed when we get confirmation from the HAL try { final int result = daemon.remove(fingerId, userId); @@ -574,11 +575,11 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe } if (mAppOps.noteOp(AppOpsManager.OP_USE_FINGERPRINT, uid, opPackageName) != AppOpsManager.MODE_ALLOWED) { - Slog.v(TAG, "Rejecting " + opPackageName + " ; permission denied"); + Slog.w(TAG, "Rejecting " + opPackageName + " ; permission denied"); return false; } if (foregroundOnly && !isForegroundActivity(uid, pid)) { - Slog.v(TAG, "Rejecting " + opPackageName + " ; not in foreground"); + Slog.w(TAG, "Rejecting " + opPackageName + " ; not in foreground"); return false; } return true; @@ -606,13 +607,15 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe IFingerprintServiceReceiver receiver; int userId; boolean restricted; // True if client does not have MANAGE_FINGERPRINT permission + String owner; public ClientMonitor(IBinder token, IFingerprintServiceReceiver receiver, int userId, - boolean restricted) { + boolean restricted, String owner) { this.token = token; this.receiver = receiver; this.userId = userId; this.restricted = restricted; + this.owner = owner; // name of the client that owns this - for debugging try { token.linkToDeath(this, 0); } catch (RemoteException e) { @@ -695,6 +698,10 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe if (!authenticated) { receiver.onAuthenticationFailed(mHalDeviceId); } else { + if (DEBUG) { + Slog.v(TAG, "onAuthenticated(owner=" + mAuthClient.owner + + ", id=" + fpId + ", gp=" + groupId + ")"); + } Fingerprint fp = !restricted ? new Fingerprint("" /* TODO */, groupId, fpId, mHalDeviceId) : null; receiver.onAuthenticationSucceeded(mHalDeviceId, fp); @@ -915,6 +922,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe final IFingerprintServiceReceiver receiver, final int flags, final String opPackageName) { if (!canUseFingerprint(opPackageName, true /* foregroundOnly */)) { + if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName); return; } @@ -927,7 +935,8 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe @Override public void run() { MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0); - startAuthentication(token, opId, effectiveGroupId, receiver, flags, restricted); + startAuthentication(token, opId, effectiveGroupId, receiver, flags, restricted, + opPackageName); } }); } diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java index 96a5e0057d61..b5046056e913 100644 --- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java @@ -245,6 +245,8 @@ final class DefaultPermissionGrantPolicy { if (verifierPackage != null && doesPackageSupportRuntimePermissions(verifierPackage)) { grantRuntimePermissionsLPw(verifierPackage, STORAGE_PERMISSIONS, true, userId); + grantRuntimePermissionsLPw(verifierPackage, PHONE_PERMISSIONS, false, userId); + grantRuntimePermissionsLPw(verifierPackage, SMS_PERMISSIONS, false, userId); } // SetupWizard diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 7024ec8b13b9..8c23648f0f0f 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -217,8 +217,7 @@ final class PackageDexOptimizer { @Nullable private String createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet) throws IOException { - if ((pkg.isSystemApp() && !pkg.isUpdatedSystemApp()) || pkg.isForwardLocked() - || pkg.applicationInfo.isExternalAsec()) { + if (!pkg.canHaveOatDir()) { return null; } File codePath = new File(pkg.codePath); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 96c546058442..3330a502236e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -13628,7 +13628,21 @@ public class PackageManagerService extends IPackageManager.Stub { // TODO(multiArch): Extend getSizeInfo to look at *all* instruction sets, not // just the primary. String[] dexCodeInstructionSets = getDexCodeInstructionSets(getAppDexInstructionSets(ps)); - int res = mInstaller.getSizeInfo(p.volumeUuid, packageName, userHandle, p.baseCodePath, + + String apkPath; + File packageDir = new File(p.codePath); + + if (packageDir.isDirectory() && p.canHaveOatDir()) { + apkPath = packageDir.getAbsolutePath(); + // If libDirRoot is inside a package dir, set it to null to avoid it being counted twice + if (libDirRoot != null && libDirRoot.startsWith(apkPath)) { + libDirRoot = null; + } + } else { + apkPath = p.baseCodePath; + } + + int res = mInstaller.getSizeInfo(p.volumeUuid, packageName, userHandle, apkPath, libDirRoot, publicSrcDir, asecPath, dexCodeInstructionSets, pStats); if (res < 0) { return false; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index fb461ae3bfb0..80c604fc1b6b 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -266,9 +266,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { WindowManagerFuncs mWindowManagerFuncs; WindowManagerInternal mWindowManagerInternal; PowerManager mPowerManager; - PowerManagerInternal mPowerManagerInternal; ActivityManagerInternal mActivityManagerInternal; DreamManagerInternal mDreamManagerInternal; + PowerManagerInternal mPowerManagerInternal; IStatusBarService mStatusBarService; boolean mPreloadedRecentApps; final Object mServiceAquireLock = new Object(); @@ -1332,6 +1332,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class); + mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); @@ -1509,6 +1510,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } @Override + public void onFling(int duration) { + if (mPowerManagerInternal != null) { + mPowerManagerInternal.powerHint( + PowerManagerInternal.POWER_HINT_INTERACTION, duration); + } + } + @Override public void onDebug() { // no-op } @@ -6086,6 +6094,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mKeyguardDelegate.bindService(mContext); mKeyguardDelegate.onBootCompleted(); } + mSystemGestures.systemReady(); } /** {@inheritDoc} */ @@ -7029,5 +7038,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mBurnInProtectionHelper != null) { mBurnInProtectionHelper.dump(prefix, pw); } + if (mKeyguardDelegate != null) { + mKeyguardDelegate.dump(prefix, pw); + } } } diff --git a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java index 627b3284be18..e4bd21d40233 100644 --- a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java +++ b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java @@ -17,9 +17,14 @@ package com.android.server.policy; import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.os.SystemClock; import android.util.Slog; +import android.view.GestureDetector; import android.view.MotionEvent; import android.view.WindowManagerPolicy.PointerEventListener; +import android.widget.OverScroller; /* * Listens for system-wide input gestures, firing callbacks when detected. @@ -31,12 +36,14 @@ public class SystemGesturesPointerEventListener implements PointerEventListener private static final long SWIPE_TIMEOUT_MS = 500; private static final int MAX_TRACKED_POINTERS = 32; // max per input system private static final int UNTRACKED_POINTER = -1; + private static final int MAX_FLING_TIME_MILLIS = 5000; private static final int SWIPE_NONE = 0; private static final int SWIPE_FROM_TOP = 1; private static final int SWIPE_FROM_BOTTOM = 2; private static final int SWIPE_FROM_RIGHT = 3; + private final Context mContext; private final int mSwipeStartThreshold; private final int mSwipeDistanceThreshold; private final Callbacks mCallbacks; @@ -45,13 +52,18 @@ public class SystemGesturesPointerEventListener implements PointerEventListener private final float[] mDownY = new float[MAX_TRACKED_POINTERS]; private final long[] mDownTime = new long[MAX_TRACKED_POINTERS]; + private GestureDetector mGestureDetector; + private OverScroller mOverscroller; + int screenHeight; int screenWidth; private int mDownPointers; private boolean mSwipeFireable; private boolean mDebugFireable; + private long mLastFlingTime; public SystemGesturesPointerEventListener(Context context, Callbacks callbacks) { + mContext = context; mCallbacks = checkNull("callbacks", callbacks); mSwipeStartThreshold = checkNull("context", context).getResources() .getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); @@ -67,8 +79,17 @@ public class SystemGesturesPointerEventListener implements PointerEventListener return arg; } + public void systemReady() { + Handler h = new Handler(Looper.myLooper()); + mGestureDetector = new GestureDetector(mContext, new FlingGestureDetector(), h); + mOverscroller = new OverScroller(mContext); + } + @Override public void onPointerEvent(MotionEvent event) { + if (mGestureDetector != null) { + mGestureDetector.onTouchEvent(event); + } switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: mSwipeFireable = true; @@ -190,10 +211,40 @@ public class SystemGesturesPointerEventListener implements PointerEventListener return SWIPE_NONE; } + private final class FlingGestureDetector extends GestureDetector.SimpleOnGestureListener { + @Override + public boolean onSingleTapUp(MotionEvent e) { + if (!mOverscroller.isFinished()) { + mOverscroller.forceFinished(true); + } + return true; + } + @Override + public boolean onFling(MotionEvent down, MotionEvent up, + float velocityX, float velocityY) { + mOverscroller.computeScrollOffset(); + long now = SystemClock.uptimeMillis(); + + if (mLastFlingTime != 0 && now > mLastFlingTime + MAX_FLING_TIME_MILLIS) { + mOverscroller.forceFinished(true); + } + mOverscroller.fling(0, 0, (int)velocityX, (int)velocityY, + Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE); + int duration = mOverscroller.getDuration(); + if (duration > MAX_FLING_TIME_MILLIS) { + duration = MAX_FLING_TIME_MILLIS; + } + mLastFlingTime = now; + mCallbacks.onFling(duration); + return true; + } + } + interface Callbacks { void onSwipeFromTop(); void onSwipeFromBottom(); void onSwipeFromRight(); + void onFling(int durationMs); void onDown(); void onUpOrCancel(); void onDebug(); diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java index 6b45941d91ec..7ae3c79e0fe5 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java @@ -23,6 +23,8 @@ import com.android.internal.policy.IKeyguardDrawnCallback; import com.android.internal.policy.IKeyguardExitCallback; import com.android.internal.policy.IKeyguardService; +import java.io.PrintWriter; + /** * A local class that keeps a cache of keyguard state that can be restored in the event * keyguard crashes. It currently also allows runtime-selectable @@ -393,4 +395,26 @@ public class KeyguardServiceDelegate { mKeyguardService.onActivityDrawn(); } } + + public void dump(String prefix, PrintWriter pw) { + pw.println(prefix + TAG); + prefix += " "; + pw.println(prefix + "showing=" + mKeyguardState.showing); + pw.println(prefix + "showingAndNotOccluded=" + mKeyguardState.showingAndNotOccluded); + pw.println(prefix + "inputRestricted=" + mKeyguardState.inputRestricted); + pw.println(prefix + "occluded=" + mKeyguardState.occluded); + pw.println(prefix + "secure=" + mKeyguardState.secure); + pw.println(prefix + "dreaming=" + mKeyguardState.dreaming); + 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 + "currentUser=" + mKeyguardState.currentUser); + pw.println(prefix + "bootCompleted=" + mKeyguardState.bootCompleted); + pw.println(prefix + "screenState=" + mKeyguardState.screenState); + pw.println(prefix + "interactiveState=" + mKeyguardState.interactiveState); + if (mKeyguardService != null) { + mKeyguardService.dump(prefix, pw); + } + } } diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java index cd88b664e2f8..429b18866a61 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java @@ -27,6 +27,8 @@ import com.android.internal.policy.IKeyguardExitCallback; import com.android.internal.policy.IKeyguardService; import com.android.internal.policy.IKeyguardStateCallback; +import java.io.PrintWriter; + /** * A wrapper class for KeyguardService. It implements IKeyguardService to ensure the interface * remains consistent. @@ -239,4 +241,8 @@ public class KeyguardServiceWrapper implements IKeyguardService { public boolean isInputRestricted() { return mKeyguardStateMonitor.isInputRestricted(); } + + public void dump(String prefix, PrintWriter pw) { + mKeyguardStateMonitor.dump(prefix, pw); + } }
\ No newline at end of file diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java index f1f9c5087aa7..30cff039f11a 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java @@ -25,6 +25,8 @@ import com.android.internal.policy.IKeyguardService; import com.android.internal.policy.IKeyguardStateCallback; import com.android.internal.widget.LockPatternUtils; +import java.io.PrintWriter; + /** * Maintains a cached copy of Keyguard's state. * @hide @@ -90,4 +92,13 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub { public void onInputRestrictedStateChanged(boolean inputRestricted) { mInputRestricted = inputRestricted; } + + public void dump(String prefix, PrintWriter pw) { + pw.println(prefix + TAG); + prefix += " "; + pw.println(prefix + "mIsShowing=" + mIsShowing); + pw.println(prefix + "mSimSecure=" + mSimSecure); + pw.println(prefix + "mInputRestricted=" + mInputRestricted); + pw.println(prefix + "mCurrentUserId=" + mCurrentUserId); + } }
\ No newline at end of file diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index a06ea1f1f752..2cb3db2fe596 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -76,6 +76,7 @@ import java.util.Arrays; import libcore.util.Objects; +import static android.os.PowerManagerInternal.POWER_HINT_INTERACTION; import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP; import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE; import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING; @@ -150,7 +151,6 @@ public final class PowerManagerService extends SystemService private static final int SCREEN_BRIGHTNESS_BOOST_TIMEOUT = 5 * 1000; // Power hints defined in hardware/libhardware/include/hardware/power.h. - private static final int POWER_HINT_INTERACTION = 2; private static final int POWER_HINT_LOW_POWER = 5; // Power features defined in hardware/libhardware/include/hardware/power.h. @@ -3565,5 +3565,10 @@ public final class PowerManagerService extends SystemService public void uidGone(int uid) { uidGoneInternal(uid); } + + @Override + public void powerHint(int hintId, int data) { + powerHintInternal(hintId, data); + } } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index b888a78e6156..2166e7e8a843 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -996,7 +996,6 @@ public class WindowManagerService extends IWindowManager.Stub SurfaceControl.closeTransaction(); } - updateCircularDisplayMaskIfNeeded(); showEmulatorDisplayOverlayIfNeeded(); } @@ -5943,7 +5942,7 @@ public class WindowManagerService extends IWindowManager.Stub } } - public void updateCircularDisplayMaskIfNeeded() { + private void updateCircularDisplayMaskIfNeeded() { // we're fullscreen and not hosted in an ActivityView if (mContext.getResources().getConfiguration().isScreenRound() && mContext.getResources().getBoolean( @@ -7659,6 +7658,8 @@ public class WindowManagerService extends IWindowManager.Stub mActivityManager.updateConfiguration(null); } catch (RemoteException e) { } + + updateCircularDisplayMaskIfNeeded(); } private void displayReady(int displayId) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 7dd16d145e32..3effe6371a98 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1047,6 +1047,12 @@ public final class SystemServer { w.getDefaultDisplay().getMetrics(metrics); context.getResources().updateConfiguration(config, metrics); + // The system context's theme may be configuration-dependent. + final Theme systemTheme = context.getTheme(); + if (systemTheme.getChangingConfigurations() != 0) { + systemTheme.rebase(); + } + try { // TODO: use boot phase mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService()); diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 1321281d00ed..49062d068d8a 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -417,10 +417,9 @@ public class UsbDeviceManager { private boolean setUsbConfig(String config) { if (DEBUG) Slog.d(TAG, "setUsbConfig(" + config + ")"); // set the new configuration - String oldConfig = SystemProperties.get(USB_CONFIG_PROPERTY); - if (!config.equals(oldConfig)) { - SystemProperties.set(USB_CONFIG_PROPERTY, config); - } + // we always set it due to b/23631400, where adbd was getting killed + // and not restarted due to property timeouts on some devices + SystemProperties.set(USB_CONFIG_PROPERTY, config); return waitForState(config); } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index e675c6df166e..aa4da4b1b690 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -78,6 +78,15 @@ public class CarrierConfigManager { public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool"; /** + * Flag to require or skip entitlement checks. + * If true, entitlement checks will be executed if device has been configured for it, + * If false, entitlement checks will be skipped. + * @hide + */ + public static final String + KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool"; + + /** * If true, enable vibration (haptic feedback) for key presses in the EmergencyDialer activity. * The pattern is set on a per-platform basis using config_virtualKeyVibePattern. To be * consistent with the regular Dialer, this value should agree with the corresponding values @@ -277,6 +286,17 @@ public class CarrierConfigManager { "carrier_instant_lettering_invalid_chars_string"; /** + * When IMS instant lettering is available for a carrier (see + * {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), determines a list of characters which + * must be escaped with a backslash '\' character. Should be specified as a string containing + * the characters to be escaped. For example to escape quote and backslash the string would be + * a quote and a backslash. + * @hide + */ + public static final String KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING = + "carrier_instant_lettering_escaped_chars_string"; + + /** * If Voice Radio Technology is RIL_RADIO_TECHNOLOGY_LTE:14 or RIL_RADIO_TECHNOLOGY_UNKNOWN:0 * this is the value that should be used instead. A configuration value of * RIL_RADIO_TECHNOLOGY_UNKNOWN:0 means there is no replacement value and that the default @@ -299,6 +319,14 @@ public class CarrierConfigManager { public static final String KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL = "carrier_force_disable_etws_cmas_test_bool"; + /** + * The default flag specifying whether "Turn on Notifications" option will be always shown in + * Settings->More->Emergency broadcasts menu regardless developer options is turned on or not. + * @hide + */ + public static final String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = + "always_show_emergency_alert_onoff_bool"; + /* The following 3 fields are related to carrier visual voicemail. */ /** @@ -463,6 +491,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL, true); sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false); sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING, ""); + sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING, ""); sDefaults.putBoolean(KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL, false); sDefaults.putBoolean(KEY_DTMF_TYPE_ENABLED_BOOL, false); sDefaults.putBoolean(KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL, true); @@ -483,6 +512,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL, false); sDefaults.putBoolean(KEY_VOICE_PRIVACY_DISABLE_UI_BOOL, false); sDefaults.putBoolean(KEY_WORLD_PHONE_BOOL, false); + sDefaults.putBoolean(KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true); sDefaults.putInt(KEY_VOLTE_REPLACEMENT_RAT_INT, 0); sDefaults.putString(KEY_DEFAULT_SIM_CALL_MANAGER_STRING, ""); sDefaults.putString(KEY_VVM_DESTINATION_NUMBER_STRING, ""); @@ -494,6 +524,7 @@ public class CarrierConfigManager { sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING, ""); sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING, ""); sDefaults.putBoolean(KEY_CSP_ENABLED_BOOL, false); + sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false); sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null); sDefaults.putStringArray(KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY, null); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index f6e4bed86b16..d22727d14829 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -3611,11 +3611,12 @@ public class TelephonyManager { * * @hide */ - public boolean setNetworkSelectionModeManual(int subId, OperatorInfo operator) { + public boolean setNetworkSelectionModeManual(int subId, OperatorInfo operator, + boolean persistSelection) { try { ITelephony telephony = getITelephony(); if (telephony != null) - return telephony.setNetworkSelectionModeManual(subId, operator); + return telephony.setNetworkSelectionModeManual(subId, operator, persistSelection); } catch (RemoteException ex) { Rlog.e(TAG, "setNetworkSelectionModeManual RemoteException", ex); } catch (NullPointerException ex) { diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 661f12db3f9c..dcece26d6c27 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -707,9 +707,13 @@ interface ITelephony { * * @param subId the id of the subscription. * @param operatorInfo the operator to attach to. + * @param persistSelection should the selection persist till reboot or its + * turned off? Will also result in notification being not shown to + * the user if the signal is lost. * @return true if the request suceeded. */ - boolean setNetworkSelectionModeManual(int subId, in OperatorInfo operator); + boolean setNetworkSelectionModeManual(int subId, in OperatorInfo operator, + boolean persistSelection); /** * Set the preferred network type. |