Merge "Restore mLazyDexOpt behavior for eng builds" into lmp-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index 840a275..8b577dd 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4798,7 +4798,9 @@
method public int getCustomSizePreset();
method public android.app.PendingIntent getDisplayIntent();
method public int getGravity();
+ method public boolean getHintAvoidBackgroundClipping();
method public boolean getHintHideIcon();
+ method public int getHintScreenTimeout();
method public boolean getHintShowBackgroundOnly();
method public java.util.List<android.app.Notification> getPages();
method public boolean getStartScrollBottom();
@@ -4811,9 +4813,13 @@
method public android.app.Notification.WearableExtender setCustomSizePreset(int);
method public android.app.Notification.WearableExtender setDisplayIntent(android.app.PendingIntent);
method public android.app.Notification.WearableExtender setGravity(int);
+ method public android.app.Notification.WearableExtender setHintAvoidBackgroundClipping(boolean);
method public android.app.Notification.WearableExtender setHintHideIcon(boolean);
+ method public android.app.Notification.WearableExtender setHintScreenTimeout(int);
method public android.app.Notification.WearableExtender setHintShowBackgroundOnly(boolean);
method public android.app.Notification.WearableExtender setStartScrollBottom(boolean);
+ field public static final int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff
+ field public static final int SCREEN_TIMEOUT_SHORT = 0; // 0x0
field public static final int SIZE_DEFAULT = 0; // 0x0
field public static final int SIZE_FULL_SCREEN = 5; // 0x5
field public static final int SIZE_LARGE = 4; // 0x4
@@ -5317,6 +5323,7 @@
method public boolean getScreenCaptureDisabled(android.content.ComponentName);
method public boolean getStorageEncryption(android.content.ComponentName);
method public int getStorageEncryptionStatus();
+ method public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName);
method public boolean hasCaCertInstalled(android.content.ComponentName, byte[]);
method public boolean hasGrantedPolicy(android.content.ComponentName, int);
method public boolean installCaCert(android.content.ComponentName, byte[]);
@@ -5365,6 +5372,7 @@
method public void setScreenCaptureDisabled(android.content.ComponentName, boolean);
method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public int setStorageEncryption(android.content.ComponentName, boolean);
+ method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
method public boolean switchUser(android.content.ComponentName, android.os.UserHandle);
method public void uninstallAllUserCaCerts(android.content.ComponentName);
@@ -16304,6 +16312,7 @@
method public void setPlaybackToRemote(android.media.VolumeProvider);
method public void setQueue(java.util.List<android.media.session.MediaSession.QueueItem>);
method public void setQueueTitle(java.lang.CharSequence);
+ method public void setRatingType(int);
method public void setSessionActivity(android.app.PendingIntent);
field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
@@ -16362,6 +16371,7 @@
method public long getBufferedPosition();
method public java.util.List<android.media.session.PlaybackState.CustomAction> getCustomActions();
method public java.lang.CharSequence getErrorMessage();
+ method public android.os.Bundle getExtras();
method public long getLastPositionUpdateTime();
method public float getPlaybackSpeed();
method public long getPosition();
@@ -16406,6 +16416,7 @@
method public android.media.session.PlaybackState.Builder setActiveQueueItemId(long);
method public android.media.session.PlaybackState.Builder setBufferedPosition(long);
method public android.media.session.PlaybackState.Builder setErrorMessage(java.lang.CharSequence);
+ method public android.media.session.PlaybackState.Builder setExtras(android.os.Bundle);
method public android.media.session.PlaybackState.Builder setState(int, long, float, long);
method public android.media.session.PlaybackState.Builder setState(int, long, float);
}
@@ -16885,9 +16896,11 @@
method public boolean isDefaultNetworkActive();
method public static boolean isNetworkTypeValid(int);
method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
+ method public void releaseNetworkRequest(android.app.PendingIntent);
method public void removeDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
method public void reportBadNetwork(android.net.Network);
method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
+ method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
method public deprecated boolean requestRouteToHost(int, int);
method public deprecated void setNetworkPreference(int);
method public static boolean setProcessDefaultNetwork(android.net.Network);
@@ -16900,6 +16913,8 @@
field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover";
field public static final deprecated java.lang.String EXTRA_NETWORK_INFO = "networkInfo";
+ field public static final java.lang.String EXTRA_NETWORK_REQUEST_NETWORK = "networkRequestNetwork";
+ field public static final java.lang.String EXTRA_NETWORK_REQUEST_NETWORK_REQUEST = "networkRequestNetworkRequest";
field public static final java.lang.String EXTRA_NETWORK_TYPE = "networkType";
field public static final java.lang.String EXTRA_NO_CONNECTIVITY = "noConnectivity";
field public static final java.lang.String EXTRA_OTHER_NETWORK_INFO = "otherNetwork";
@@ -28095,6 +28110,7 @@
method public final int getCapabilities();
method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
method public final java.util.List<android.telecom.Connection> getConnections();
+ method public final android.telecom.DisconnectCause getDisconnectCause();
method public final android.telecom.PhoneAccountHandle getPhoneAccountHandle();
method public android.telecom.Connection getPrimaryConnection();
method public final int getState();
@@ -28214,6 +28230,7 @@
method public void writeToParcel(android.os.Parcel, int);
field public static final int BUSY = 7; // 0x7
field public static final int CANCELED = 4; // 0x4
+ field public static final int CONNECTION_MANAGER_NOT_SUPPORTED = 10; // 0xa
field public static final android.os.Parcelable.Creator<android.telecom.DisconnectCause> CREATOR;
field public static final int ERROR = 1; // 0x1
field public static final int LOCAL = 2; // 0x2
@@ -28393,8 +28410,10 @@
method public void clearAccounts();
method public android.net.Uri getAdnUriForPhoneAccount(android.telecom.PhoneAccountHandle);
method public android.telecom.PhoneAccountHandle getConnectionManager();
+ method public android.telecom.PhoneAccountHandle getDefaultOutgoingPhoneAccount(java.lang.String);
method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
+ method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(java.lang.String);
method public boolean handleMmi(java.lang.String);
method public boolean handleMmi(android.telecom.PhoneAccountHandle, java.lang.String);
method public boolean hasMultipleCallCapableAccounts();
@@ -33394,7 +33413,6 @@
method public int describeContents();
method public boolean isValid();
method public android.graphics.Canvas lockCanvas(android.graphics.Rect) throws java.lang.IllegalArgumentException, android.view.Surface.OutOfResourcesException;
- method public android.graphics.Canvas lockHardwareCanvas();
method public void readFromParcel(android.os.Parcel);
method public void release();
method public deprecated void unlockCanvas(android.graphics.Canvas);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index ba11a81..bc57030 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -417,7 +417,7 @@
} else if (opt.equals("--ei")) {
String key = nextArgRequired();
String value = nextArgRequired();
- intent.putExtra(key, Integer.valueOf(value));
+ intent.putExtra(key, Integer.decode(value));
} else if (opt.equals("--eu")) {
String key = nextArgRequired();
String value = nextArgRequired();
@@ -434,7 +434,7 @@
String[] strings = value.split(",");
int[] list = new int[strings.length];
for (int i = 0; i < strings.length; i++) {
- list[i] = Integer.valueOf(strings[i]);
+ list[i] = Integer.decode(strings[i]);
}
intent.putExtra(key, list);
} else if (opt.equals("--el")) {
@@ -477,8 +477,23 @@
hasIntentInfo = true;
} else if (opt.equals("--ez")) {
String key = nextArgRequired();
- String value = nextArgRequired();
- intent.putExtra(key, Boolean.valueOf(value));
+ String value = nextArgRequired().toLowerCase();
+ // Boolean.valueOf() results in false for anything that is not "true", which is
+ // error-prone in shell commands
+ boolean arg;
+ if ("true".equals(value) || "t".equals(value)) {
+ arg = true;
+ } else if ("false".equals(value) || "f".equals(value)) {
+ arg = false;
+ } else {
+ try {
+ arg = Integer.decode(value) != 0;
+ } catch (NumberFormatException ex) {
+ throw new IllegalArgumentException("Invalid boolean value: " + value);
+ }
+ }
+
+ intent.putExtra(key, arg);
} else if (opt.equals("-n")) {
String str = nextArgRequired();
ComponentName cn = ComponentName.unflattenFromString(str);
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 1e1b33f..a9eaf29 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -24,11 +24,9 @@
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
-import android.view.Display;
import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -620,18 +618,6 @@
}
}
- @Override
- public Object getSystemService(String name) {
- if (Context.WINDOW_SERVICE.equals(name)) {
- if (mWindowManager == null) {
- WindowManager wrapped = (WindowManager) super.getSystemService(name);
- mWindowManager = new LocalWindowManager(wrapped);
- }
- return mWindowManager;
- }
- return super.getSystemService(name);
- }
-
/**
* Implement to return the implementation of the internal accessibility
* service interface.
@@ -658,6 +644,9 @@
public void init(int connectionId, IBinder windowToken) {
mConnectionId = connectionId;
mWindowToken = windowToken;
+
+ // Let the window manager know about our shiny new token.
+ WindowManagerGlobal.getInstance().setDefaultToken(mWindowToken);
}
@Override
@@ -812,53 +801,4 @@
}
}
}
-
- private class LocalWindowManager implements WindowManager {
- private final WindowManager mImpl;
-
- private LocalWindowManager(WindowManager impl) {
- mImpl = impl;
- }
-
- @Override
- public Display getDefaultDisplay() {
- return mImpl.getDefaultDisplay();
- }
-
- @Override
- public void addView(View view, ViewGroup.LayoutParams params) {
- if (!(params instanceof WindowManager.LayoutParams)) {
- throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
- }
- WindowManager.LayoutParams windowParams = (WindowManager.LayoutParams) params;
- if (windowParams.type == LayoutParams.TYPE_ACCESSIBILITY_OVERLAY
- && windowParams.token == null) {
- windowParams.token = mWindowToken;
- }
- mImpl.addView(view, params);
- }
-
- @Override
- public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
- if (!(params instanceof WindowManager.LayoutParams)) {
- throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
- }
- WindowManager.LayoutParams windowParams = (WindowManager.LayoutParams) params;
- if (windowParams.type == LayoutParams.TYPE_ACCESSIBILITY_OVERLAY
- && windowParams.token == null) {
- windowParams.token = mWindowToken;
- }
- mImpl.updateViewLayout(view, params);
- }
-
- @Override
- public void removeViewImmediate(View view) {
- mImpl.removeViewImmediate(view);
- }
-
- @Override
- public void removeView(View view) {
- mImpl.removeView(view);
- }
- }
}
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
index 500634c..59daaab 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -885,7 +885,8 @@
}
/**
- * Sets the target object whose property will be animated by this animation
+ * Sets the target object whose property will be animated by this animation. If the
+ * animator has been started, it will be canceled.
*
* @param target The object being animated
*/
@@ -893,6 +894,9 @@
public void setTarget(@Nullable Object target) {
final Object oldTarget = getTarget();
if (oldTarget != target) {
+ if (isStarted()) {
+ cancel();
+ }
mTarget = target == null ? null : new WeakReference<Object>(target);
// New target should cause re-initialization prior to starting
mInitialized = false;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 37e8aa4..a285932 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1243,26 +1243,16 @@
}
/**
- * If set, the process of the root activity of the task will be killed
- * as part of removing the task.
- * @hide
- */
- public static final int REMOVE_TASK_KILL_PROCESS = 0x0001;
-
- /**
* Completely remove the given task.
*
* @param taskId Identifier of the task to be removed.
- * @param flags Additional operational flags. May be 0 or
- * {@link #REMOVE_TASK_KILL_PROCESS}.
* @return Returns true if the given task was found and removed.
*
* @hide
*/
- public boolean removeTask(int taskId, int flags)
- throws SecurityException {
+ public boolean removeTask(int taskId) throws SecurityException {
try {
- return ActivityManagerNative.getDefault().removeTask(taskId, flags);
+ return ActivityManagerNative.getDefault().removeTask(taskId);
} catch (RemoteException e) {
// System dead, we will be dead too soon!
return false;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 4e2ff0b..bc7114b 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1884,8 +1884,7 @@
{
data.enforceInterface(IActivityManager.descriptor);
int taskId = data.readInt();
- int fl = data.readInt();
- boolean result = removeTask(taskId, fl);
+ boolean result = removeTask(taskId);
reply.writeNoException();
reply.writeInt(result ? 1 : 0);
return true;
@@ -4778,12 +4777,11 @@
return result;
}
- public boolean removeTask(int taskId, int flags) throws RemoteException {
+ public boolean removeTask(int taskId) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(taskId);
- data.writeInt(flags);
mRemote.transact(REMOVE_TASK_TRANSACTION, data, reply, 0);
reply.readException();
boolean result = reply.readInt() != 0;
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index be26f30..efcb197 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -373,7 +373,7 @@
public boolean isUserRunning(int userid, boolean orStopping) throws RemoteException;
public int[] getRunningUserIds() throws RemoteException;
- public boolean removeTask(int taskId, int flags) throws RemoteException;
+ public boolean removeTask(int taskId) throws RemoteException;
public void registerProcessObserver(IProcessObserver observer) throws RemoteException;
public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 9849c51..c65f017 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4340,6 +4340,19 @@
*/
public static final int SIZE_FULL_SCREEN = 5;
+ /**
+ * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on for a
+ * short amount of time when this notification is displayed on the screen. This
+ * is the default value.
+ */
+ public static final int SCREEN_TIMEOUT_SHORT = 0;
+
+ /**
+ * Sentinel value for use with {@link #setHintScreenTimeout} to keep the screen on
+ * for a longer amount of time when this notification is displayed on the screen.
+ */
+ public static final int SCREEN_TIMEOUT_LONG = -1;
+
/** Notification extra which contains wearable extensions */
private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
@@ -4355,12 +4368,14 @@
private static final String KEY_CUSTOM_SIZE_PRESET = "customSizePreset";
private static final String KEY_CUSTOM_CONTENT_HEIGHT = "customContentHeight";
private static final String KEY_GRAVITY = "gravity";
+ private static final String KEY_HINT_SCREEN_TIMEOUT = "hintScreenTimeout";
// Flags bitwise-ored to mFlags
private static final int FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE = 0x1;
private static final int FLAG_HINT_HIDE_ICON = 1 << 1;
private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
+ private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
// Default value for flags integer
private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
@@ -4379,6 +4394,7 @@
private int mCustomSizePreset = SIZE_DEFAULT;
private int mCustomContentHeight;
private int mGravity = DEFAULT_GRAVITY;
+ private int mHintScreenTimeout;
/**
* Create a {@link android.app.Notification.WearableExtender} with default
@@ -4414,6 +4430,7 @@
SIZE_DEFAULT);
mCustomContentHeight = wearableBundle.getInt(KEY_CUSTOM_CONTENT_HEIGHT);
mGravity = wearableBundle.getInt(KEY_GRAVITY, DEFAULT_GRAVITY);
+ mHintScreenTimeout = wearableBundle.getInt(KEY_HINT_SCREEN_TIMEOUT);
}
}
@@ -4461,6 +4478,9 @@
if (mGravity != DEFAULT_GRAVITY) {
wearableBundle.putInt(KEY_GRAVITY, mGravity);
}
+ if (mHintScreenTimeout != 0) {
+ wearableBundle.putInt(KEY_HINT_SCREEN_TIMEOUT, mHintScreenTimeout);
+ }
builder.getExtras().putBundle(EXTRA_WEARABLE_EXTENSIONS, wearableBundle);
return builder;
@@ -4480,6 +4500,7 @@
that.mCustomSizePreset = this.mCustomSizePreset;
that.mCustomContentHeight = this.mCustomContentHeight;
that.mGravity = this.mGravity;
+ that.mHintScreenTimeout = this.mHintScreenTimeout;
return that;
}
@@ -4875,6 +4896,48 @@
return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
}
+ /**
+ * Set a hint that this notification's background should not be clipped if possible.
+ * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
+ * @return this object for method chaining
+ */
+ public WearableExtender setHintAvoidBackgroundClipping(
+ boolean hintAvoidBackgroundClipping) {
+ setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
+ return this;
+ }
+
+ /**
+ * Get a hint that this notification's background should not be clipped if possible.
+ * @return {@code true} if it's ok if the background is clipped on the screen, false
+ * otherwise. The default value is {@code false} if this was never set.
+ */
+ public boolean getHintAvoidBackgroundClipping() {
+ return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
+ }
+
+ /**
+ * Set a hint that the screen should remain on for at least this duration when
+ * this notification is displayed on the screen.
+ * @param timeout The requested screen timeout in milliseconds. Can also be either
+ * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
+ * @return this object for method chaining
+ */
+ public WearableExtender setHintScreenTimeout(int timeout) {
+ mHintScreenTimeout = timeout;
+ return this;
+ }
+
+ /**
+ * Get the duration, in milliseconds, that the screen should remain on for
+ * when this notification is displayed.
+ * @return the duration in milliseconds if > 0, or either one of the sentinel values
+ * {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
+ */
+ public int getHintScreenTimeout() {
+ return mHintScreenTimeout;
+ }
+
private void setFlag(int mask, boolean value) {
if (value) {
mFlags |= mask;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 74502fc..d3ff79d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -31,6 +31,7 @@
import android.net.ProxyInfo;
import android.os.Bundle;
import android.os.Handler;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
@@ -40,6 +41,7 @@
import android.provider.Settings;
import android.security.Credentials;
import android.service.restrictions.RestrictionsReceiver;
+import android.service.trust.TrustAgentService;
import android.util.Log;
import com.android.org.conscrypt.TrustedCertificateStore;
@@ -2604,25 +2606,29 @@
}
/**
- * Sets a list of features to enable for a TrustAgent component. This is meant to be
- * used in conjunction with {@link #KEYGUARD_DISABLE_TRUST_AGENTS}, which will disable all
- * trust agents but those with features enabled by this function call.
+ * Sets a list of configuration features to enable for a TrustAgent component. This is meant
+ * to be used in conjunction with {@link #KEYGUARD_DISABLE_TRUST_AGENTS}, which disables all
+ * trust agents but those enabled by this function call. If flag
+ * {@link #KEYGUARD_DISABLE_TRUST_AGENTS} is not set, then this call has no effect.
*
* <p>The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_DISABLE_KEYGUARD_FEATURES} to be able to call
- * this method; if it has not, a security exception will be thrown.
+ * this method; if not, a security exception will be thrown.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @param agent Which component to enable features for.
- * @param features List of features to enable. Consult specific TrustAgent documentation for
- * the feature list.
- * @hide
+ * @param target Component name of the agent to be enabled.
+ * @param options TrustAgent-specific feature bundle. If null for any admin, agent
+ * will be strictly disabled according to the state of the
+ * {@link #KEYGUARD_DISABLE_TRUST_AGENTS} flag.
+ * <p>If {@link #KEYGUARD_DISABLE_TRUST_AGENTS} is set and options is not null for all admins,
+ * then it's up to the TrustAgent itself to aggregate the values from all device admins.
+ * <p>Consult documentation for the specific TrustAgent to determine legal options parameters.
*/
- public void setTrustAgentFeaturesEnabled(ComponentName admin, ComponentName agent,
- List<String> features) {
+ public void setTrustAgentConfiguration(ComponentName admin, ComponentName target,
+ PersistableBundle options) {
if (mService != null) {
try {
- mService.setTrustAgentFeaturesEnabled(admin, agent, features, UserHandle.myUserId());
+ mService.setTrustAgentConfiguration(admin, target, options, UserHandle.myUserId());
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -2630,24 +2636,30 @@
}
/**
- * Gets list of enabled features for the given TrustAgent component. If admin is
- * null, this will return the intersection of all features enabled for the given agent by all
- * admins.
+ * Gets configuration for the given trust agent based on aggregating all calls to
+ * {@link #setTrustAgentConfiguration(ComponentName, ComponentName, PersistableBundle)} for
+ * all device admins.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param agent Which component to get enabled features for.
- * @return List of enabled features.
- * @hide
+ * @return configuration for the given trust agent.
*/
- public List<String> getTrustAgentFeaturesEnabled(ComponentName admin, ComponentName agent) {
+ public List<PersistableBundle> getTrustAgentConfiguration(ComponentName admin,
+ ComponentName agent) {
+ return getTrustAgentConfiguration(admin, agent, UserHandle.myUserId());
+ }
+
+ /** @hide per-user version */
+ public List<PersistableBundle> getTrustAgentConfiguration(ComponentName admin,
+ ComponentName agent, int userHandle) {
if (mService != null) {
try {
- return mService.getTrustAgentFeaturesEnabled(admin, agent, UserHandle.myUserId());
+ return mService.getTrustAgentConfiguration(admin, agent, userHandle);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
}
- return new ArrayList<String>(); // empty list
+ return new ArrayList<PersistableBundle>(); // empty list
}
/**
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index c8e1780..07aa800 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -22,6 +22,7 @@
import android.content.IntentFilter;
import android.net.ProxyInfo;
import android.os.Bundle;
+import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.UserHandle;
import java.util.List;
@@ -183,8 +184,10 @@
boolean getCrossProfileCallerIdDisabled(in ComponentName who);
boolean getCrossProfileCallerIdDisabledForUser(int userId);
- void setTrustAgentFeaturesEnabled(in ComponentName admin, in ComponentName agent, in List<String> features, int userId);
- List<String> getTrustAgentFeaturesEnabled(in ComponentName admin, in ComponentName agent, int userId);
+ void setTrustAgentConfiguration(in ComponentName admin, in ComponentName agent,
+ in PersistableBundle args, int userId);
+ List<PersistableBundle> getTrustAgentConfiguration(in ComponentName admin,
+ in ComponentName agent, int userId);
boolean addCrossProfileWidgetProvider(in ComponentName admin, String packageName);
boolean removeCrossProfileWidgetProvider(in ComponentName admin, String packageName);
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 2853c58..c8f9b7d 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -515,7 +515,10 @@
}
// last chance, check against any uri grants
- if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ final int callingUserId = UserHandle.getUserId(uid);
+ final Uri userUri = (mSingleUser && !UserHandle.isSameUser(mMyUid, uid))
+ ? maybeAddUserId(uri, callingUserId) : uri;
+ if (context.checkUriPermission(userUri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
== PERMISSION_GRANTED) {
return;
}
diff --git a/core/java/android/hardware/hdmi/HdmiRecordListener.java b/core/java/android/hardware/hdmi/HdmiRecordListener.java
index 29f6cfc..90b7768 100644
--- a/core/java/android/hardware/hdmi/HdmiRecordListener.java
+++ b/core/java/android/hardware/hdmi/HdmiRecordListener.java
@@ -39,6 +39,8 @@
/**
* Called when one touch record is started or failed during initialization.
*
+ * @param recorderAddress An address of recorder that reports result of one touch record
+ * request
* @param result result code. For more details, please look at all constants starting with
* "ONE_TOUCH_RECORD_". Only
* {@link HdmiControlManager#ONE_TOUCH_RECORD_RECORDING_CURRENTLY_SELECTED_SOURCE},
@@ -47,15 +49,17 @@
* {@link HdmiControlManager#ONE_TOUCH_RECORD_RECORDING_EXTERNAL_INPUT} mean normal
* start of recording; otherwise, describes failure.
*/
- public void onOneTouchRecordResult(int result) {
+ public void onOneTouchRecordResult(int recorderAddress, int result) {
}
/**
* Called when timer recording is started or failed during initialization.
*
+ * @param recorderAddress An address of recorder that reports result of timer recording
+ * request
* @param data timer status data. For more details, look at {@link TimerStatusData}.
*/
- public void onTimerRecordingResult(TimerStatusData data) {
+ public void onTimerRecordingResult(int recorderAddress, TimerStatusData data) {
}
/**
@@ -230,6 +234,8 @@
/**
* Called when receiving result for clear timer recording request.
*
+ * @param recorderAddress An address of recorder that reports result of clear timer recording
+ * request
* @param result result of clear timer. It should be one of
* {@link HdmiControlManager#CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_RECORDING}
* {@link HdmiControlManager#CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_NO_MATCHING},
@@ -239,6 +245,6 @@
* {@link HdmiControlManager#CLEAR_TIMER_STATUS_FAIL_TO_CLEAR_SELECTED_SOURCE},
* {@link HdmiControlManager#CLEAR_TIMER_STATUS_CEC_DISABLE}.
*/
- public void onClearTimerRecordingResult(int result) {
+ public void onClearTimerRecordingResult(int recorderAddress, int result) {
}
}
diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java
index dbfb4ef..cef17dd 100644
--- a/core/java/android/hardware/hdmi/HdmiTvClient.java
+++ b/core/java/android/hardware/hdmi/HdmiTvClient.java
@@ -226,19 +226,19 @@
}
@Override
- public void onOneTouchRecordResult(int result) {
- callback.onOneTouchRecordResult(result);
+ public void onOneTouchRecordResult(int recorderAddress, int result) {
+ callback.onOneTouchRecordResult(recorderAddress, result);
}
@Override
- public void onTimerRecordingResult(int result) {
- callback.onTimerRecordingResult(
+ public void onTimerRecordingResult(int recorderAddress, int result) {
+ callback.onTimerRecordingResult(recorderAddress,
HdmiRecordListener.TimerStatusData.parseFrom(result));
}
@Override
- public void onClearTimerRecordingResult(int result) {
- callback.onClearTimerRecordingResult(result);
+ public void onClearTimerRecordingResult(int recorderAddress, int result) {
+ callback.onClearTimerRecordingResult(recorderAddress, result);
}
};
}
diff --git a/core/java/android/hardware/hdmi/IHdmiRecordListener.aidl b/core/java/android/hardware/hdmi/IHdmiRecordListener.aidl
index 44d9065..d2deb38 100644
--- a/core/java/android/hardware/hdmi/IHdmiRecordListener.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiRecordListener.aidl
@@ -31,19 +31,25 @@
/**
* Called when one touch record is started or failed during initialization.
*
+ * @param recorderAddress An address of recorder that reports result of one touch record
+ * request
* @param result result code for one touch record
*/
- void onOneTouchRecordResult(int result);
+ void onOneTouchRecordResult(int recorderAddress, int result);
/**
* Called when timer recording is started or failed during initialization.
-
+ *
+ * @param recorderAddress An address of recorder that reports result of timer recording
+ * request
* @param result result code for timer recording
*/
- void onTimerRecordingResult(int result);
+ void onTimerRecordingResult(int recorderAddress, int result);
/**
* Called when receiving result for clear timer recording request.
*
- * @param result result of clear timer.
+ * @param recorderAddress An address of recorder that reports result of clear timer recording
+ * request
+ * @param result result of clear timer
*/
- void onClearTimerRecordingResult(int result);
+ void onClearTimerRecordingResult(int recorderAddress, int result);
}
\ No newline at end of file
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 9194ca8..1c9f4c68 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1012,60 +1012,57 @@
return null;
}
+ /**
+ * Guess what the network request was trying to say so that the resulting
+ * network is accessible via the legacy (deprecated) API such as
+ * requestRouteToHost.
+ * This means we should try to be fairly preceise about transport and
+ * capability but ignore things such as networkSpecifier.
+ * If the request has more than one transport or capability it doesn't
+ * match the old legacy requests (they selected only single transport/capability)
+ * so this function cannot map the request to a single legacy type and
+ * the resulting network will not be available to the legacy APIs.
+ *
+ * TODO - This should be removed when the legacy APIs are removed.
+ */
private int inferLegacyTypeForNetworkCapabilities(NetworkCapabilities netCap) {
if (netCap == null) {
return TYPE_NONE;
}
+
if (!netCap.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
return TYPE_NONE;
}
+
+ String type = null;
+ int result = TYPE_NONE;
+
if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
- if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableCBS"))) {
- return TYPE_MOBILE_CBS;
- } else {
- return TYPE_NONE;
- }
+ type = "enableCBS";
+ result = TYPE_MOBILE_CBS;
+ } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
+ type = "enableIMS";
+ result = TYPE_MOBILE_IMS;
+ } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
+ type = "enableFOTA";
+ result = TYPE_MOBILE_FOTA;
+ } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
+ type = "enableDUN";
+ result = TYPE_MOBILE_DUN;
+ } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
+ type = "enableSUPL";
+ result = TYPE_MOBILE_SUPL;
+ } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
+ type = "enableMMS";
+ result = TYPE_MOBILE_MMS;
+ } else if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+ type = "enableHIPRI";
+ result = TYPE_MOBILE_HIPRI;
}
- if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_IMS)) {
- if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableIMS"))) {
- return TYPE_MOBILE_IMS;
- } else {
- return TYPE_NONE;
- }
- }
- if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOTA)) {
- if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableFOTA"))) {
- return TYPE_MOBILE_FOTA;
- } else {
- return TYPE_NONE;
- }
- }
- if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_DUN)) {
- if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableDUN"))) {
- return TYPE_MOBILE_DUN;
- } else {
- return TYPE_NONE;
- }
- }
- if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_SUPL)) {
- if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableSUPL"))) {
- return TYPE_MOBILE_SUPL;
- } else {
- return TYPE_NONE;
- }
- }
- if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
- if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableMMS"))) {
- return TYPE_MOBILE_MMS;
- } else {
- return TYPE_NONE;
- }
- }
- if (netCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
- if (netCap.equals(networkCapabilitiesForFeature(TYPE_MOBILE, "enableHIPRI"))) {
- return TYPE_MOBILE_HIPRI;
- } else {
- return TYPE_NONE;
+ if (type != null) {
+ NetworkCapabilities testCap = networkCapabilitiesForFeature(TYPE_MOBILE, type);
+ if (testCap.equalsNetCapabilities(netCap) && testCap.equalsTransportTypes(netCap)) {
+ return result;
}
}
return TYPE_NONE;
@@ -2381,17 +2378,15 @@
/**
* The lookup key for a {@link Network} object included with the intent after
- * succesfully finding a network for the applications request. Retrieve it with
+ * successfully finding a network for the applications request. Retrieve it with
* {@link android.content.Intent#getParcelableExtra(String)}.
- * @hide
*/
public static final String EXTRA_NETWORK_REQUEST_NETWORK = "networkRequestNetwork";
/**
* The lookup key for a {@link NetworkRequest} object included with the intent after
- * succesfully finding a network for the applications request. Retrieve it with
+ * successfully finding a network for the applications request. Retrieve it with
* {@link android.content.Intent#getParcelableExtra(String)}.
- * @hide
*/
public static final String EXTRA_NETWORK_REQUEST_NETWORK_REQUEST =
"networkRequestNetworkRequest";
@@ -2400,7 +2395,7 @@
/**
* Request a network to satisfy a set of {@link NetworkCapabilities}.
*
- * This function behavies identically to the version that takes a NetworkCallback, but instead
+ * This function behaves identically to the version that takes a NetworkCallback, but instead
* of {@link NetworkCallback} a {@link PendingIntent} is used. This means
* the request may outlive the calling application and get called back when a suitable
* network is found.
@@ -2421,21 +2416,46 @@
* two Intents defined by {@link Intent#filterEquals}), then it will be removed and
* replaced by this one, effectively releasing the previous {@link NetworkRequest}.
* <p>
- * The request may be released normally by calling {@link #unregisterNetworkCallback}.
+ * The request may be released normally by calling
+ * {@link #releaseNetworkRequest(android.app.PendingIntent)}.
*
* @param request {@link NetworkRequest} describing this request.
* @param operation Action to perform when the network is available (corresponds
* to the {@link NetworkCallback#onAvailable} call. Typically
- * comes from {@link PendingIntent#getBroadcast}.
- * @hide
+ * comes from {@link PendingIntent#getBroadcast}. Cannot be null.
*/
public void requestNetwork(NetworkRequest request, PendingIntent operation) {
+ checkPendingIntent(operation);
try {
mService.pendingRequestForNetwork(request.networkCapabilities, operation);
} catch (RemoteException e) {}
}
/**
+ * Removes a request made via {@link #requestNetwork(NetworkRequest, android.app.PendingIntent)}
+ * <p>
+ * This method has the same behavior as {@link #unregisterNetworkCallback} with respect to
+ * releasing network resources and disconnecting.
+ *
+ * @param operation A PendingIntent equal (as defined by {@link Intent#filterEquals}) to the
+ * PendingIntent passed to
+ * {@link #requestNetwork(NetworkRequest, android.app.PendingIntent)} with the
+ * corresponding NetworkRequest you'd like to remove. Cannot be null.
+ */
+ public void releaseNetworkRequest(PendingIntent operation) {
+ checkPendingIntent(operation);
+ try {
+ mService.releasePendingNetworkRequest(operation);
+ } catch (RemoteException e) {}
+ }
+
+ private void checkPendingIntent(PendingIntent intent) {
+ if (intent == null) {
+ throw new IllegalArgumentException("PendingIntent cannot be null.");
+ }
+ }
+
+ /**
* Registers to receive notifications about all networks which satisfy the given
* {@link NetworkRequest}. The callbacks will continue to be called until
* either the application exits or {@link #unregisterNetworkCallback} is called
@@ -2451,7 +2471,7 @@
/**
* Unregisters callbacks about and possibly releases networks originating from
* {@link #requestNetwork} and {@link #registerNetworkCallback} calls. If the
- * given {@code NetworkCallback} had previosuly been used with {@code #requestNetwork},
+ * given {@code NetworkCallback} had previously been used with {@code #requestNetwork},
* any networks that had been connected to only to satisfy that request will be
* disconnected.
*
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
index 71df60a..6159e1e 100644
--- a/core/java/android/net/DhcpResults.java
+++ b/core/java/android/net/DhcpResults.java
@@ -200,7 +200,7 @@
vendorInfo = info;
}
- public void setDomains(String domains) {
- domains = domains;
+ public void setDomains(String newDomains) {
+ domains = newDomains;
}
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index a983d88..a7bbc53 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -156,6 +156,8 @@
NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities,
in PendingIntent operation);
+ void releasePendingNetworkRequest(in PendingIntent operation);
+
NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities,
in Messenger messenger, in IBinder binder);
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 1efe478..ce7ad65 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -235,7 +235,8 @@
return ((nc.mNetworkCapabilities & this.mNetworkCapabilities) == this.mNetworkCapabilities);
}
- private boolean equalsNetCapabilities(NetworkCapabilities nc) {
+ /** @hide */
+ public boolean equalsNetCapabilities(NetworkCapabilities nc) {
return (nc.mNetworkCapabilities == this.mNetworkCapabilities);
}
@@ -344,7 +345,8 @@
return ((this.mTransportTypes == 0) ||
((this.mTransportTypes & nc.mTransportTypes) != 0));
}
- private boolean equalsTransportTypes(NetworkCapabilities nc) {
+ /** @hide */
+ public boolean equalsTransportTypes(NetworkCapabilities nc) {
return (nc.mTransportTypes == this.mTransportTypes);
}
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 3f68a44..a939cce 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -41,10 +41,10 @@
* <ul>
* <li>Declares the {@link android.Manifest.permission#SCORE_NETWORKS} permission.
* <li>Includes a receiver for {@link #ACTION_SCORE_NETWORKS} guarded by the
- * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission which scores networks
- * and (eventually) calls {@link #updateScores} with the results. If this receiver specifies an
- * android:label attribute, this label will be used when referring to the application throughout
- * system settings; otherwise, the application label will be used.
+ * {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission which scores
+ * networks and (eventually) calls {@link #updateScores} with the results. If this receiver
+ * specifies an android:label attribute, this label will be used when referring to the
+ * application throughout system settings; otherwise, the application label will be used.
* </ul>
*
* <p>The system keeps track of an active scorer application; at any time, only this application
@@ -192,12 +192,15 @@
/**
* Set the active scorer to a new package and clear existing scores.
*
+ * <p>Should never be called directly without obtaining user consent. This can be done by using
+ * the {@link #ACTION_CHANGE_ACTIVE} broadcast, or using a custom configuration activity.
+ *
* @return true if the operation succeeded, or false if the new package is not a valid scorer.
* @throws SecurityException if the caller does not hold the
- * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission indicating
- * that it can manage scorer applications.
+ * {@link android.Manifest.permission#SCORE_NETWORKS} permission.
* @hide
*/
+ @SystemApi
public boolean setActiveScorer(String packageName) throws SecurityException {
try {
return mService.setActiveScorer(packageName);
@@ -228,7 +231,7 @@
*
* @return true if the broadcast was sent, or false if there is no active scorer.
* @throws SecurityException if the caller does not hold the
- * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission.
+ * {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission.
* @hide
*/
public boolean requestScores(NetworkKey[] networks) throws SecurityException {
@@ -252,7 +255,7 @@
* @param networkType the type of network this cache can handle. See {@link NetworkKey#type}.
* @param scoreCache implementation of {@link INetworkScoreCache} to store the scores.
* @throws SecurityException if the caller does not hold the
- * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission.
+ * {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission.
* @throws IllegalArgumentException if a score cache is already registered for this type.
* @hide
*/
diff --git a/core/java/android/net/NetworkScorerAppManager.java b/core/java/android/net/NetworkScorerAppManager.java
index c33f5ec..46f7194 100644
--- a/core/java/android/net/NetworkScorerAppManager.java
+++ b/core/java/android/net/NetworkScorerAppManager.java
@@ -79,7 +79,7 @@
* <ul>
* <li>Declares the {@link android.Manifest.permission#SCORE_NETWORKS} permission.
* <li>Includes a receiver for {@link NetworkScoreManager#ACTION_SCORE_NETWORKS} guarded by the
- * {@link android.Manifest.permission#BROADCAST_SCORE_NETWORKS} permission.
+ * {@link android.Manifest.permission#BROADCAST_NETWORK_PRIVILEGED} permission.
* </ul>
*
* @return the list of scorers, or the empty list if there are no valid scorers.
@@ -98,8 +98,8 @@
// Should never happen with queryBroadcastReceivers, but invalid nonetheless.
continue;
}
- if (!permission.BROADCAST_SCORE_NETWORKS.equals(receiverInfo.permission)) {
- // Receiver doesn't require the BROADCAST_SCORE_NETWORKS permission, which means
+ if (!permission.BROADCAST_NETWORK_PRIVILEGED.equals(receiverInfo.permission)) {
+ // Receiver doesn't require the BROADCAST_NETWORK_PRIVILEGED permission, which means
// anyone could trigger network scoring and flood the framework with score requests.
continue;
}
diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java
index 5a273cf..598a503 100644
--- a/core/java/android/net/StaticIpConfiguration.java
+++ b/core/java/android/net/StaticIpConfiguration.java
@@ -107,6 +107,7 @@
for (InetAddress dns : dnsServers) {
lp.addDnsServer(dns);
}
+ lp.setDomains(domains);
return lp;
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 9a84a1e..36401eb 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -78,6 +78,7 @@
private static final String ALLOW_ATT_EVENTS = "events";
private static final String SLEEP_TAG = "sleep";
private static final String SLEEP_ATT_MODE = "mode";
+ private static final String SLEEP_ATT_NONE = "none";
private static final String SLEEP_ATT_START_HR = "startHour";
private static final String SLEEP_ATT_START_MIN = "startMin";
@@ -107,6 +108,7 @@
public int sleepStartMinute; // 0-59
public int sleepEndHour;
public int sleepEndMinute;
+ public boolean sleepNone; // false = priority, true = none
public ComponentName[] conditionComponents;
public Uri[] conditionIds;
public Condition exitCondition;
@@ -125,6 +127,7 @@
sleepStartMinute = source.readInt();
sleepEndHour = source.readInt();
sleepEndMinute = source.readInt();
+ sleepNone = source.readInt() == 1;
int len = source.readInt();
if (len > 0) {
conditionComponents = new ComponentName[len];
@@ -155,6 +158,7 @@
dest.writeInt(sleepStartMinute);
dest.writeInt(sleepEndHour);
dest.writeInt(sleepEndMinute);
+ dest.writeInt(sleepNone ? 1 : 0);
if (conditionComponents != null && conditionComponents.length > 0) {
dest.writeInt(conditionComponents.length);
dest.writeTypedArray(conditionComponents, 0);
@@ -182,6 +186,7 @@
.append(",sleepMode=").append(sleepMode)
.append(",sleepStart=").append(sleepStartHour).append('.').append(sleepStartMinute)
.append(",sleepEnd=").append(sleepEndHour).append('.').append(sleepEndMinute)
+ .append(",sleepNone=").append(sleepNone)
.append(",conditionComponents=")
.append(conditionComponents == null ? null : TextUtils.join(",", conditionComponents))
.append(",conditionIds=")
@@ -214,6 +219,7 @@
&& other.allowFrom == allowFrom
&& other.allowEvents == allowEvents
&& Objects.equals(other.sleepMode, sleepMode)
+ && other.sleepNone == sleepNone
&& other.sleepStartHour == sleepStartHour
&& other.sleepStartMinute == sleepStartMinute
&& other.sleepEndHour == sleepEndHour
@@ -226,7 +232,7 @@
@Override
public int hashCode() {
- return Objects.hash(allowCalls, allowMessages, allowFrom, allowEvents, sleepMode,
+ return Objects.hash(allowCalls, allowMessages, allowFrom, allowEvents, sleepMode, sleepNone,
sleepStartHour, sleepStartMinute, sleepEndHour, sleepEndMinute,
Arrays.hashCode(conditionComponents), Arrays.hashCode(conditionIds),
exitCondition, exitConditionComponent);
@@ -302,6 +308,7 @@
} else if (SLEEP_TAG.equals(tag)) {
final String mode = parser.getAttributeValue(null, SLEEP_ATT_MODE);
rt.sleepMode = isValidSleepMode(mode)? mode : null;
+ rt.sleepNone = safeBoolean(parser, SLEEP_ATT_NONE, false);
final int startHour = safeInt(parser, SLEEP_ATT_START_HR, 0);
final int startMinute = safeInt(parser, SLEEP_ATT_START_MIN, 0);
final int endHour = safeInt(parser, SLEEP_ATT_END_HR, 0);
@@ -345,6 +352,7 @@
if (sleepMode != null) {
out.attribute(null, SLEEP_ATT_MODE, sleepMode);
}
+ out.attribute(null, SLEEP_ATT_NONE, Boolean.toString(sleepNone));
out.attribute(null, SLEEP_ATT_START_HR, Integer.toString(sleepStartHour));
out.attribute(null, SLEEP_ATT_START_MIN, Integer.toString(sleepStartMinute));
out.attribute(null, SLEEP_ATT_END_HR, Integer.toString(sleepEndHour));
@@ -498,7 +506,7 @@
}
// For built-in conditions
- private static final String SYSTEM_AUTHORITY = "android";
+ public static final String SYSTEM_AUTHORITY = "android";
// Built-in countdown conditions, e.g. condition://android/countdown/1399917958951
private static final String COUNTDOWN_PATH = "countdown";
diff --git a/core/java/android/service/trust/ITrustAgentService.aidl b/core/java/android/service/trust/ITrustAgentService.aidl
index bd80a3f..bb0c2b2 100644
--- a/core/java/android/service/trust/ITrustAgentService.aidl
+++ b/core/java/android/service/trust/ITrustAgentService.aidl
@@ -15,7 +15,7 @@
*/
package android.service.trust;
-import android.os.Bundle;
+import android.os.PersistableBundle;
import android.service.trust.ITrustAgentServiceCallback;
/**
@@ -25,6 +25,6 @@
interface ITrustAgentService {
oneway void onUnlockAttempt(boolean successful);
oneway void onTrustTimeout();
+ oneway void onConfigure(in List<PersistableBundle> options, IBinder token);
oneway void setCallback(ITrustAgentServiceCallback callback);
- oneway void setTrustAgentFeaturesEnabled(in Bundle options, IBinder token);
}
diff --git a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
index b107bcc..76b2be0 100644
--- a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
+++ b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
@@ -27,5 +27,5 @@
void grantTrust(CharSequence message, long durationMs, boolean initiatedByUser);
void revokeTrust();
void setManagingTrust(boolean managingTrust);
- void onSetTrustAgentFeaturesEnabledCompleted(boolean result, IBinder token);
+ void onConfigureCompleted(boolean result, IBinder token);
}
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 3ef5b37..00d60c0 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -29,11 +29,14 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import android.util.Slog;
+import java.util.List;
+
/**
* A service that notifies the system about whether it believes the environment of the device
* to be trusted.
@@ -86,17 +89,47 @@
*/
public static final String TRUST_AGENT_META_DATA = "android.service.trust.trustagent";
- /**
- * A white list of features that the given trust agent should support when otherwise disabled
- * by device policy.
- * @hide
- */
- public static final String KEY_FEATURES = "trust_agent_features";
-
private static final int MSG_UNLOCK_ATTEMPT = 1;
- private static final int MSG_SET_TRUST_AGENT_FEATURES_ENABLED = 2;
+ private static final int MSG_CONFIGURE = 2;
private static final int MSG_TRUST_TIMEOUT = 3;
+ /**
+ * Container class for a list of configuration options and helper methods
+ */
+ public static final class Configuration {
+ public final List<PersistableBundle> options;
+ public Configuration(List<PersistableBundle> opts) {
+ options = opts;
+ }
+
+ /**
+ * Very basic method to determine if all bundles have the given feature, regardless
+ * of type.
+ * @param option String to search for.
+ * @return true if found in all bundles.
+ */
+ public boolean hasOption(String option) {
+ if (options == null || options.size() == 0) return false;
+ final int N = options.size();
+ for (int i = 0; i < N; i++) {
+ if (!options.get(i).containsKey(option)) return false;
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Class containing raw data for a given configuration request.
+ */
+ private static final class ConfigurationData {
+ final IBinder token;
+ final List<PersistableBundle> options;
+ ConfigurationData(List<PersistableBundle> opts, IBinder t) {
+ options = opts;
+ token = t;
+ }
+ }
+
private ITrustAgentServiceCallback mCallback;
private Runnable mPendingGrantTrustTask;
@@ -112,13 +145,12 @@
case MSG_UNLOCK_ATTEMPT:
onUnlockAttempt(msg.arg1 != 0);
break;
- case MSG_SET_TRUST_AGENT_FEATURES_ENABLED:
- Bundle features = msg.peekData();
- IBinder token = (IBinder) msg.obj;
- boolean result = onSetTrustAgentFeaturesEnabled(features);
+ case MSG_CONFIGURE:
+ ConfigurationData data = (ConfigurationData) msg.obj;
+ boolean result = onConfigure(new Configuration(data.options));
try {
synchronized (mLock) {
- mCallback.onSetTrustAgentFeaturesEnabledCompleted(result, token);
+ mCallback.onConfigureCompleted(result, data.token);
}
} catch (RemoteException e) {
onError("calling onSetTrustAgentFeaturesEnabledCompleted()");
@@ -171,23 +203,16 @@
}
/**
- * Called when device policy wants to restrict features in the agent in response to
- * {@link DevicePolicyManager#setTrustAgentFeaturesEnabled(ComponentName, ComponentName, java.util.List) }.
- * Agents that support this feature should overload this method and return 'true'.
+ * Called when device policy admin wants to enable specific options for agent in response to
+ * {@link DevicePolicyManager#setKeyguardDisabledFeatures(ComponentName, int)} and
+ * {@link DevicePolicyManager#setTrustAgentConfiguration(ComponentName, ComponentName,
+ * PersistableBundle)}.
+ * <p>Agents that support configuration options should overload this method and return 'true'.
*
- * The list of options can be obtained by calling
- * options.getStringArrayList({@link #KEY_FEATURES}). Presence of a feature string in the list
- * means it should be enabled ("white-listed"). Absence of the feature means it should be
- * disabled. An empty list means all features should be disabled.
- *
- * This function is only called if {@link DevicePolicyManager#KEYGUARD_DISABLE_TRUST_AGENTS} is
- * set.
- *
- * @param options Option feature bundle.
- * @return true if the {@link TrustAgentService} supports this feature.
- * @hide
+ * @param options bundle containing all options or null if none.
+ * @return true if the {@link TrustAgentService} supports configuration options.
*/
- public boolean onSetTrustAgentFeaturesEnabled(Bundle options) {
+ public boolean onConfigure(Configuration options) {
return false;
}
@@ -295,6 +320,12 @@
}
@Override /* Binder API */
+ public void onConfigure(List<PersistableBundle> args, IBinder token) {
+ mHandler.obtainMessage(MSG_CONFIGURE, new ConfigurationData(args, token))
+ .sendToTarget();
+ }
+
+ @Override /* Binder API */
public void setCallback(ITrustAgentServiceCallback callback) {
synchronized (mLock) {
mCallback = callback;
@@ -313,13 +344,6 @@
}
}
}
-
- @Override /* Binder API */
- public void setTrustAgentFeaturesEnabled(Bundle features, IBinder token) {
- Message msg = mHandler.obtainMessage(MSG_SET_TRUST_AGENT_FEATURES_ENABLED, token);
- msg.setData(features);
- msg.sendToTarget();
- }
}
}
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index b0cbcd2..b467f5a 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -110,6 +110,7 @@
private static final int SECONDS_PER_MINUTE = 60;
private static final int SECONDS_PER_HOUR = 60 * 60;
private static final int SECONDS_PER_DAY = 24 * 60 * 60;
+ private static final int MILLIS_PER_MINUTE = 1000 * 60;
/**
* Returns elapsed time for the given millis, in the following format:
@@ -171,4 +172,24 @@
return context.getString(com.android.internal.R.string.durationSeconds, seconds);
}
}
+
+ /**
+ * Returns elapsed time for the given millis, in the following format:
+ * 1 day 5 hrs; will include at most two units, can go down to minutes precision.
+ * @param context the application context
+ * @param millis the elapsed time in milli seconds
+ * @return the formatted elapsed time
+ * @hide
+ */
+ public static String formatShortElapsedTimeRoundingUpToMinutes(Context context, long millis) {
+ long minutesRoundedUp = (millis + MILLIS_PER_MINUTE - 1) / MILLIS_PER_MINUTE;
+
+ if (minutesRoundedUp == 0) {
+ return context.getString(com.android.internal.R.string.durationMinutes, 0);
+ } else if (minutesRoundedUp == 1) {
+ return context.getString(com.android.internal.R.string.durationMinute, 1);
+ }
+
+ return formatShortElapsedTime(context, minutesRoundedUp * MILLIS_PER_MINUTE);
+ }
}
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index e99c2cf..2705bcf 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -84,8 +84,8 @@
*
* <p>Custom transition classes may be instantiated with a <code>transition</code> tag:</p>
* <pre><transition class="my.app.transition.CustomTransition"/></pre>
- * <p>Custom transition classes loaded from XML must have a public nullary (no argument)
- * constructor.</p>
+ * <p>Custom transition classes loaded from XML should have a public constructor taking
+ * a {@link android.content.Context} and {@link android.util.AttributeSet}.</p>
*
* <p>Note that attributes for the transition are not required, just as they are
* optional when declared in code; Transitions created from XML resources will use
@@ -955,7 +955,7 @@
* Views with different IDs, or no IDs whatsoever, will be ignored.
*
* <p>Note that using ids to specify targets implies that ids should be unique
- * within the view hierarchy underneat the scene root.</p>
+ * within the view hierarchy underneath the scene root.</p>
*
* @see View#getId()
* @param targetId The id of a target view, must be a positive number.
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 1cadf69..5e05683 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -1109,15 +1109,17 @@
|| accessibilityViewId == providerHost.getAccessibilityViewId()) {
final AccessibilityNodeInfo parent;
if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
- parent = provider.createAccessibilityNodeInfo(
- virtualDescendantId);
+ parent = provider.createAccessibilityNodeInfo(virtualDescendantId);
} else {
- parent= provider.createAccessibilityNodeInfo(
+ parent = provider.createAccessibilityNodeInfo(
AccessibilityNodeProvider.HOST_VIEW_ID);
}
- if (parent != null) {
- outInfos.add(parent);
+ if (parent == null) {
+ // Couldn't obtain the parent, which means we have a
+ // disconnected sub-tree. Abort prefetch immediately.
+ return;
}
+ outInfos.add(parent);
parentNodeId = parent.getParentNodeId();
accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(
parentNodeId);
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 562d138..90fdbe24 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -319,6 +319,7 @@
* @return A canvas for drawing into the surface.
*
* @throws IllegalStateException If the canvas cannot be locked.
+ * @hide
*/
public Canvas lockHardwareCanvas() {
synchronized (mLock) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 6450146..ea0a077 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -728,8 +728,8 @@
}
final Rect insets = attrs.surfaceInsets;
- final boolean hasSurfaceInsets = insets.left == 0 || insets.right == 0
- || insets.top == 0 || insets.bottom == 0;
+ final boolean hasSurfaceInsets = insets.left != 0 || insets.right != 0
+ || insets.top != 0 || insets.bottom != 0;
final boolean translucent = attrs.format != PixelFormat.OPAQUE || hasSurfaceInsets;
mAttachInfo.mHardwareRenderer = HardwareRenderer.create(mContext, translucent);
if (mAttachInfo.mHardwareRenderer != null) {
@@ -1746,8 +1746,8 @@
if (hwInitialized ||
mWidth != mAttachInfo.mHardwareRenderer.getWidth() ||
mHeight != mAttachInfo.mHardwareRenderer.getHeight()) {
- final Rect surfaceInsets = params != null ? params.surfaceInsets : null;
- mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight, surfaceInsets);
+ mAttachInfo.mHardwareRenderer.setup(
+ mWidth, mHeight, mWindowAttributes.surfaceInsets);
if (!hwInitialized) {
mAttachInfo.mHardwareRenderer.invalidate(mSurface);
mFullRedrawNeeded = true;
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 5926d5f..82b1073 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -118,6 +118,9 @@
private Runnable mSystemPropertyUpdater;
+ /** Default token to apply to added views. */
+ private IBinder mDefaultToken;
+
private WindowManagerGlobal() {
}
@@ -169,6 +172,17 @@
}
}
+ /**
+ * Sets the default token to use in {@link #addView} when no parent window
+ * token is available and no token has been explicitly set in the view's
+ * layout params.
+ *
+ * @param token Default window token to apply to added views.
+ */
+ public void setDefaultToken(IBinder token) {
+ mDefaultToken = token;
+ }
+
public String[] getViewRootNames() {
synchronized (mLock) {
final int numRoots = mRoots.size();
@@ -216,6 +230,10 @@
}
}
+ if (wparams.token == null && mDefaultToken != null) {
+ wparams.token = mDefaultToken;
+ }
+
ViewRootImpl root;
View panelParentView = null;
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index fcf66f6..f9544d0 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -107,9 +107,9 @@
mAdapter.setRange(mMinDate, mMaxDate);
- if (constrainCalendar(mSelectedDay, mMinDate, mMaxDate)) {
- goTo(mSelectedDay, false, true, true);
- }
+ // Changing the min/max date changes the selection position since we
+ // don't really have stable IDs.
+ goTo(mSelectedDay, false, true, true);
}
/**
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index ac915d1..183527cb 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -16,6 +16,8 @@
package com.android.internal.inputmethod;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.content.ContentResolver;
import android.content.Context;
@@ -34,7 +36,9 @@
import android.view.textservice.TextServicesManager;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
@@ -115,8 +119,8 @@
}
/**
- * @deprecated Use {@link Locale} returned from
- * {@link #getFallbackLocaleForDefaultIme(ArrayList)} instead.
+ * @deprecated Use {@link #isSystemImeThatHasSubtypeOf(InputMethodInfo, Context, boolean,
+ * Locale, boolean, String)} instead.
*/
@Deprecated
public static boolean isSystemImeThatHasEnglishKeyboardSubtype(InputMethodInfo imi) {
@@ -126,25 +130,60 @@
return containsSubtypeOf(imi, ENGLISH_LOCALE.getLanguage(), SUBTYPE_MODE_KEYBOARD);
}
+ private static boolean isSystemImeThatHasSubtypeOf(final InputMethodInfo imi,
+ final Context context, final boolean checkDefaultAttribute,
+ @Nullable final Locale requiredLocale, final boolean checkCountry,
+ final String requiredSubtypeMode) {
+ if (!isSystemIme(imi)) {
+ return false;
+ }
+ if (checkDefaultAttribute && !imi.isDefault(context)) {
+ return false;
+ }
+ if (!containsSubtypeOf(imi, requiredLocale, checkCountry, requiredSubtypeMode)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Nullable
public static Locale getFallbackLocaleForDefaultIme(final ArrayList<InputMethodInfo> imis,
final Context context) {
+ // At first, find the fallback locale from the IMEs that are declared as "default" in the
+ // current locale. Note that IME developers can declare an IME as "default" only for
+ // some particular locales but "not default" for other locales.
for (final Locale fallbackLocale : SEARCH_ORDER_OF_FALLBACK_LOCALES) {
for (int i = 0; i < imis.size(); ++i) {
- final InputMethodInfo imi = imis.get(i);
- if (isSystemIme(imi) && imi.isDefault(context) &&
- containsSubtypeOf(imi, fallbackLocale, false /* ignoreCountry */,
- SUBTYPE_MODE_KEYBOARD)) {
+ if (isSystemImeThatHasSubtypeOf(imis.get(i), context,
+ true /* checkDefaultAttribute */, fallbackLocale,
+ true /* checkCountry */, SUBTYPE_MODE_KEYBOARD)) {
return fallbackLocale;
}
}
}
+ // If no fallback locale is found in the above condition, find fallback locales regardless
+ // of the "default" attribute as a last resort.
+ for (final Locale fallbackLocale : SEARCH_ORDER_OF_FALLBACK_LOCALES) {
+ for (int i = 0; i < imis.size(); ++i) {
+ if (isSystemImeThatHasSubtypeOf(imis.get(i), context,
+ false /* checkDefaultAttribute */, fallbackLocale,
+ true /* checkCountry */, SUBTYPE_MODE_KEYBOARD)) {
+ return fallbackLocale;
+ }
+ }
+ }
+ Slog.w(TAG, "Found no fallback locale. imis=" + Arrays.toString(imis.toArray()));
return null;
}
- private static boolean isSystemAuxilialyImeThatHasAutomaticSubtype(InputMethodInfo imi) {
+ private static boolean isSystemAuxilialyImeThatHasAutomaticSubtype(final InputMethodInfo imi,
+ final Context context, final boolean checkDefaultAttribute) {
if (!isSystemIme(imi)) {
return false;
}
+ if (checkDefaultAttribute && !imi.isDefault(context)) {
+ return false;
+ }
if (!imi.isAuxiliaryIme()) {
return false;
}
@@ -166,98 +205,184 @@
}
}
- public static ArrayList<InputMethodInfo> getDefaultEnabledImes(
- Context context, boolean isSystemReady, ArrayList<InputMethodInfo> imis) {
- // OK to store null in fallbackLocale because isImeThatHasSubtypeOf() is null-tolerant.
- final Locale fallbackLocale = getFallbackLocaleForDefaultIme(imis, context);
+ private static final class InputMethodListBuilder {
+ // Note: We use LinkedHashSet instead of android.util.ArraySet because the enumeration
+ // order can have non-trivial effect in the call sites.
+ @NonNull
+ private final LinkedHashSet<InputMethodInfo> mInputMethodSet = new LinkedHashSet<>();
- if (!isSystemReady) {
- final ArrayList<InputMethodInfo> retval = new ArrayList<>();
+ public InputMethodListBuilder fillImes(final ArrayList<InputMethodInfo> imis,
+ final Context context, final boolean checkDefaultAttribute,
+ @Nullable final Locale locale, final boolean checkCountry,
+ final String requiredSubtypeMode) {
for (int i = 0; i < imis.size(); ++i) {
final InputMethodInfo imi = imis.get(i);
- // TODO: We should check isAsciiCapable instead of relying on fallbackLocale.
- if (isSystemIme(imi) && imi.isDefault(context) &&
- isImeThatHasSubtypeOf(imi, fallbackLocale, false /* ignoreCountry */,
- SUBTYPE_MODE_KEYBOARD)) {
- retval.add(imi);
+ if (isSystemImeThatHasSubtypeOf(imi, context, checkDefaultAttribute, locale,
+ checkCountry, requiredSubtypeMode)) {
+ mInputMethodSet.add(imi);
}
}
- return retval;
+ return this;
}
- // OK to store null in fallbackLocale because isImeThatHasSubtypeOf() is null-tolerant.
- final Locale systemLocale = getSystemLocaleFromContext(context);
- // TODO: Use LinkedHashSet to simplify the code.
- final ArrayList<InputMethodInfo> retval = new ArrayList<>();
- boolean systemLocaleKeyboardImeFound = false;
-
- // First, try to find IMEs with taking the system locale country into consideration.
- for (int i = 0; i < imis.size(); ++i) {
- final InputMethodInfo imi = imis.get(i);
- if (!isSystemIme(imi) || !imi.isDefault(context)) {
- continue;
+ // TODO: The behavior of InputMethodSubtype#overridesImplicitlyEnabledSubtype() should be
+ // documented more clearly.
+ public InputMethodListBuilder fillAuxiliaryImes(final ArrayList<InputMethodInfo> imis,
+ final Context context) {
+ // If one or more auxiliary input methods are available, OK to stop populating the list.
+ for (final InputMethodInfo imi : mInputMethodSet) {
+ if (imi.isAuxiliaryIme()) {
+ return this;
+ }
}
- final boolean isSystemLocaleKeyboardIme = isImeThatHasSubtypeOf(imi, systemLocale,
- false /* ignoreCountry */, SUBTYPE_MODE_KEYBOARD);
- // TODO: We should check isAsciiCapable instead of relying on fallbackLocale.
- // TODO: Use LinkedHashSet to simplify the code.
- if (isSystemLocaleKeyboardIme ||
- isImeThatHasSubtypeOf(imi, fallbackLocale, false /* ignoreCountry */,
- SUBTYPE_MODE_ANY)) {
- retval.add(imi);
- }
- systemLocaleKeyboardImeFound |= isSystemLocaleKeyboardIme;
- }
-
- // System locale country doesn't match any IMEs, try to find IMEs in a country-agnostic
- // way.
- if (!systemLocaleKeyboardImeFound) {
+ boolean added = false;
for (int i = 0; i < imis.size(); ++i) {
final InputMethodInfo imi = imis.get(i);
- if (!isSystemIme(imi) || !imi.isDefault(context)) {
- continue;
- }
- if (isImeThatHasSubtypeOf(imi, fallbackLocale, false /* ignoreCountry */,
- SUBTYPE_MODE_KEYBOARD)) {
- // IMEs that have fallback locale are already added in the previous loop. We
- // don't need to add them again here.
- // TODO: Use LinkedHashSet to simplify the code.
- continue;
- }
- if (isImeThatHasSubtypeOf(imi, systemLocale, true /* ignoreCountry */,
- SUBTYPE_MODE_ANY)) {
- retval.add(imi);
+ if (isSystemAuxilialyImeThatHasAutomaticSubtype(imi, context,
+ true /* checkDefaultAttribute */)) {
+ mInputMethodSet.add(imi);
+ added = true;
}
}
+ if (added) {
+ return this;
+ }
+ for (int i = 0; i < imis.size(); ++i) {
+ final InputMethodInfo imi = imis.get(i);
+ if (isSystemAuxilialyImeThatHasAutomaticSubtype(imi, context,
+ false /* checkDefaultAttribute */)) {
+ mInputMethodSet.add(imi);
+ }
+ }
+ return this;
}
- // If one or more auxiliary input methods are available, OK to stop populating the list.
- for (int i = 0; i < retval.size(); ++i) {
- if (retval.get(i).isAuxiliaryIme()) {
- return retval;
- }
+ public boolean isEmpty() {
+ return mInputMethodSet.isEmpty();
}
- for (int i = 0; i < imis.size(); ++i) {
- final InputMethodInfo imi = imis.get(i);
- if (isSystemAuxilialyImeThatHasAutomaticSubtype(imi)) {
- retval.add(imi);
- }
+
+ @NonNull
+ public ArrayList<InputMethodInfo> build() {
+ return new ArrayList<>(mInputMethodSet);
}
- return retval;
}
- public static boolean isImeThatHasSubtypeOf(final InputMethodInfo imi,
- final Locale locale, final boolean ignoreCountry, final String mode) {
- if (locale == null) {
- return false;
+ private static InputMethodListBuilder getMinimumKeyboardSetWithoutSystemLocale(
+ final ArrayList<InputMethodInfo> imis, final Context context,
+ @Nullable final Locale fallbackLocale) {
+ // Before the system becomes ready, we pick up at least one keyboard in the following order.
+ // The first user (device owner) falls into this category.
+ // 1. checkDefaultAttribute: true, locale: fallbackLocale, checkCountry: true
+ // 2. checkDefaultAttribute: false, locale: fallbackLocale, checkCountry: true
+ // 3. checkDefaultAttribute: true, locale: fallbackLocale, checkCountry: false
+ // 4. checkDefaultAttribute: false, locale: fallbackLocale, checkCountry: false
+ // TODO: We should check isAsciiCapable instead of relying on fallbackLocale.
+
+ final InputMethodListBuilder builder = new InputMethodListBuilder();
+ builder.fillImes(imis, context, true /* checkDefaultAttribute */, fallbackLocale,
+ true /* checkCountry */, SUBTYPE_MODE_KEYBOARD);
+ if (!builder.isEmpty()) {
+ return builder;
}
- return containsSubtypeOf(imi, locale, ignoreCountry, mode);
+ builder.fillImes(imis, context, false /* checkDefaultAttribute */, fallbackLocale,
+ true /* checkCountry */, SUBTYPE_MODE_KEYBOARD);
+ if (!builder.isEmpty()) {
+ return builder;
+ }
+ builder.fillImes(imis, context, true /* checkDefaultAttribute */, fallbackLocale,
+ false /* checkCountry */, SUBTYPE_MODE_KEYBOARD);
+ if (!builder.isEmpty()) {
+ return builder;
+ }
+ builder.fillImes(imis, context, false /* checkDefaultAttribute */, fallbackLocale,
+ false /* checkCountry */, SUBTYPE_MODE_KEYBOARD);
+ if (!builder.isEmpty()) {
+ return builder;
+ }
+ Slog.w(TAG, "No software keyboard is found. imis=" + Arrays.toString(imis.toArray())
+ + " fallbackLocale=" + fallbackLocale);
+ return builder;
+ }
+
+ private static InputMethodListBuilder getMinimumKeyboardSetWithSystemLocale(
+ final ArrayList<InputMethodInfo> imis, final Context context,
+ @Nullable final Locale systemLocale, @Nullable final Locale fallbackLocale) {
+ // Once the system becomes ready, we pick up at least one keyboard in the following order.
+ // Secondary users fall into this category in general.
+ // 1. checkDefaultAttribute: true, locale: systemLocale, checkCountry: true
+ // 2. checkDefaultAttribute: true, locale: systemLocale, checkCountry: false
+ // 3. checkDefaultAttribute: true, locale: fallbackLocale, checkCountry: true
+ // 4. checkDefaultAttribute: true, locale: fallbackLocale, checkCountry: false
+ // 5. checkDefaultAttribute: false, locale: fallbackLocale, checkCountry: true
+ // 6. checkDefaultAttribute: false, locale: fallbackLocale, checkCountry: false
+ // TODO: We should check isAsciiCapable instead of relying on fallbackLocale.
+
+ final InputMethodListBuilder builder = new InputMethodListBuilder();
+ builder.fillImes(imis, context, true /* checkDefaultAttribute */, systemLocale,
+ true /* checkCountry */, SUBTYPE_MODE_KEYBOARD);
+ if (!builder.isEmpty()) {
+ return builder;
+ }
+ builder.fillImes(imis, context, true /* checkDefaultAttribute */, systemLocale,
+ false /* checkCountry */, SUBTYPE_MODE_KEYBOARD);
+ if (!builder.isEmpty()) {
+ return builder;
+ }
+ builder.fillImes(imis, context, true /* checkDefaultAttribute */, fallbackLocale,
+ true /* checkCountry */, SUBTYPE_MODE_KEYBOARD);
+ if (!builder.isEmpty()) {
+ return builder;
+ }
+ builder.fillImes(imis, context, true /* checkDefaultAttribute */, fallbackLocale,
+ false /* checkCountry */, SUBTYPE_MODE_KEYBOARD);
+ if (!builder.isEmpty()) {
+ return builder;
+ }
+ builder.fillImes(imis, context, false /* checkDefaultAttribute */, fallbackLocale,
+ true /* checkCountry */, SUBTYPE_MODE_KEYBOARD);
+ if (!builder.isEmpty()) {
+ return builder;
+ }
+ builder.fillImes(imis, context, false /* checkDefaultAttribute */, fallbackLocale,
+ false /* checkCountry */, SUBTYPE_MODE_KEYBOARD);
+ if (!builder.isEmpty()) {
+ return builder;
+ }
+ Slog.w(TAG, "No software keyboard is found. imis=" + Arrays.toString(imis.toArray())
+ + " systemLocale=" + systemLocale + " fallbackLocale=" + fallbackLocale);
+ return builder;
+ }
+
+ public static ArrayList<InputMethodInfo> getDefaultEnabledImes(final Context context,
+ final boolean isSystemReady, final ArrayList<InputMethodInfo> imis) {
+ final Locale fallbackLocale = getFallbackLocaleForDefaultIme(imis, context);
+ if (!isSystemReady) {
+ // When the system is not ready, the system locale is not stable and reliable. Hence
+ // we will pick up IMEs that support software keyboard based on the fallback locale.
+ // Also pick up suitable IMEs regardless of the software keyboard support.
+ // (e.g. Voice IMEs)
+ return getMinimumKeyboardSetWithoutSystemLocale(imis, context, fallbackLocale)
+ .fillImes(imis, context, true /* checkDefaultAttribute */, fallbackLocale,
+ true /* checkCountry */, SUBTYPE_MODE_ANY)
+ .build();
+ }
+
+ // When the system is ready, we will primarily rely on the system locale, but also keep
+ // relying on the fallback locale as a last resort.
+ // Also pick up suitable IMEs regardless of the software keyboard support (e.g. Voice IMEs),
+ // then pick up suitable auxiliary IMEs when necessary (e.g. Voice IMEs with "automatic"
+ // subtype)
+ final Locale systemLocale = getSystemLocaleFromContext(context);
+ return getMinimumKeyboardSetWithSystemLocale(imis, context, systemLocale, fallbackLocale)
+ .fillImes(imis, context, true /* checkDefaultAttribute */, systemLocale,
+ true /* checkCountry */, SUBTYPE_MODE_ANY)
+ .fillAuxiliaryImes(imis, context)
+ .build();
}
/**
- * @deprecated Use {@link #isSystemIme(InputMethodInfo)} and
- * {@link InputMethodInfo#isDefault(Context)} and
- * {@link #isImeThatHasSubtypeOf(InputMethodInfo, Locale, boolean, String))} instead.
+ * @deprecated Use {@link #isSystemImeThatHasSubtypeOf(InputMethodInfo, Context, boolean,
+ * Locale, boolean, String)} instead.
*/
@Deprecated
public static boolean isValidSystemDefaultIme(
@@ -285,22 +410,25 @@
}
public static boolean containsSubtypeOf(final InputMethodInfo imi,
- final Locale locale, final boolean ignoreCountry, final String mode) {
+ @Nullable final Locale locale, final boolean checkCountry, final String mode) {
+ if (locale == null) {
+ return false;
+ }
final int N = imi.getSubtypeCount();
for (int i = 0; i < N; ++i) {
final InputMethodSubtype subtype = imi.getSubtypeAt(i);
- if (ignoreCountry) {
- final Locale subtypeLocale = new Locale(getLanguageFromLocaleString(
- subtype.getLocale()));
- if (!subtypeLocale.getLanguage().equals(locale.getLanguage())) {
- continue;
- }
- } else {
+ if (checkCountry) {
// TODO: Use {@link Locale#toLanguageTag()} and
// {@link Locale#forLanguageTag(languageTag)} instead.
if (!TextUtils.equals(subtype.getLocale(), locale.toString())) {
continue;
}
+ } else {
+ final Locale subtypeLocale = new Locale(getLanguageFromLocaleString(
+ subtype.getLocale()));
+ if (!subtypeLocale.getLanguage().equals(locale.getLanguage())) {
+ continue;
+ }
}
if (mode == SUBTYPE_MODE_ANY || TextUtils.isEmpty(mode) ||
mode.equalsIgnoreCase(subtype.getMode())) {
@@ -465,19 +593,9 @@
return applicableSubtypes;
}
- private static List<InputMethodSubtype> getEnabledInputMethodSubtypeList(
- Context context, InputMethodInfo imi, List<InputMethodSubtype> enabledSubtypes,
- boolean allowsImplicitlySelectedSubtypes) {
- if (allowsImplicitlySelectedSubtypes && enabledSubtypes.isEmpty()) {
- enabledSubtypes = InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
- context.getResources(), imi);
- }
- return InputMethodSubtype.sort(context, 0, imi, enabledSubtypes);
- }
-
/**
* Returns the language component of a given locale string.
- * TODO: Use {@link Locale#toLanguageTag()} and {@link Locale#forLanguageTag(languageTag)}
+ * TODO: Use {@link Locale#toLanguageTag()} and {@link Locale#forLanguageTag(String)}
*/
public static String getLanguageFromLocaleString(String locale) {
final int idx = locale.indexOf('_');
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 70cf9a8..d7eef6e 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -17,7 +17,7 @@
#include <jni.h>
-#include <Caches.h>
+#include <ResourceCache.h>
#if 0
#define TRACE_BITMAP(code) code
@@ -365,8 +365,8 @@
static void Bitmap_destructor(JNIEnv* env, jobject, jlong bitmapHandle) {
SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
#ifdef USE_OPENGL_RENDERER
- if (android::uirenderer::Caches::hasInstance()) {
- android::uirenderer::Caches::getInstance().resourceCache.destructor(bitmap);
+ if (android::uirenderer::ResourceCache::hasInstance()) {
+ android::uirenderer::ResourceCache::getInstance().destructor(bitmap);
return;
}
#endif // USE_OPENGL_RENDERER
@@ -376,9 +376,9 @@
static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) {
SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
#ifdef USE_OPENGL_RENDERER
- if (android::uirenderer::Caches::hasInstance()) {
+ if (android::uirenderer::ResourceCache::hasInstance()) {
bool result;
- result = android::uirenderer::Caches::getInstance().resourceCache.recycle(bitmap);
+ result = android::uirenderer::ResourceCache::getInstance().recycle(bitmap);
return result ? JNI_TRUE : JNI_FALSE;
}
#endif // USE_OPENGL_RENDERER
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index cf23771..be62fdd 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -21,7 +21,7 @@
#include <androidfw/ResourceTypes.h>
#include <utils/Log.h>
-#include <Caches.h>
+#include <ResourceCache.h>
#include "Paint.h"
#include "Canvas.h"
@@ -80,9 +80,9 @@
static void finalize(JNIEnv* env, jobject, jlong patchHandle) {
int8_t* patch = reinterpret_cast<int8_t*>(patchHandle);
#ifdef USE_OPENGL_RENDERER
- if (android::uirenderer::Caches::hasInstance()) {
+ if (android::uirenderer::ResourceCache::hasInstance()) {
Res_png_9patch* p = (Res_png_9patch*) patch;
- android::uirenderer::Caches::getInstance().resourceCache.destructor(p);
+ android::uirenderer::ResourceCache::getInstance().destructor(p);
return;
}
#endif // USE_OPENGL_RENDERER
diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp
index 9d3e74b..30ce58d 100644
--- a/core/jni/android/graphics/Path.cpp
+++ b/core/jni/android/graphics/Path.cpp
@@ -27,7 +27,7 @@
#include "SkPath.h"
#include "SkPathOps.h"
-#include <Caches.h>
+#include <ResourceCache.h>
#include <vector>
#include <map>
@@ -39,8 +39,8 @@
static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) {
SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
#ifdef USE_OPENGL_RENDERER
- if (android::uirenderer::Caches::hasInstance()) {
- android::uirenderer::Caches::getInstance().resourceCache.destructor(obj);
+ if (android::uirenderer::ResourceCache::hasInstance()) {
+ android::uirenderer::ResourceCache::getInstance().destructor(obj);
return;
}
#endif
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6f2a06b..ffe2b60 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2511,11 +2511,12 @@
android:description="@string/permdesc_broadcastWapPush"
android:protectionLevel="signature" />
- <!-- @SystemApi Allows an application to broadcast a SCORE_NETWORKS request.
+ <!-- @SystemApi Allows an application to broadcast privileged networking requests.
<p>Not for use by third-party applications. @hide -->
- <permission android:name="android.permission.BROADCAST_SCORE_NETWORKS"
- android:label="@string/permlab_broadcastScoreNetworks"
- android:description="@string/permdesc_broadcastScoreNetworks"
+ <permission android:name="android.permission.BROADCAST_NETWORK_PRIVILEGED"
+ android:permissionGroup="android.permission-group.NETWORK"
+ android:label="@string/permlab_broadcastNetworkPrivileged"
+ android:description="@string/permdesc_broadcastNetworkPrivileged"
android:protectionLevel="signature|system" />
<!-- @SystemApi Not for use by third-party applications. -->
diff --git a/core/res/res/drawable-hdpi/sym_def_app_icon.png b/core/res/res/drawable-hdpi/sym_def_app_icon.png
index 96a442e..cde69bc 100644
--- a/core/res/res/drawable-hdpi/sym_def_app_icon.png
+++ b/core/res/res/drawable-hdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_def_app_icon.png b/core/res/res/drawable-mdpi/sym_def_app_icon.png
index 359047d..c133a0c 100644
--- a/core/res/res/drawable-mdpi/sym_def_app_icon.png
+++ b/core/res/res/drawable-mdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/sym_def_app_icon.png b/core/res/res/drawable-xhdpi/sym_def_app_icon.png
index 71c6d76..bfa42f0 100644
--- a/core/res/res/drawable-xhdpi/sym_def_app_icon.png
+++ b/core/res/res/drawable-xhdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/sym_def_app_icon.png b/core/res/res/drawable-xxhdpi/sym_def_app_icon.png
index 20a47a0..324e72c 100644
--- a/core/res/res/drawable-xxhdpi/sym_def_app_icon.png
+++ b/core/res/res/drawable-xxhdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/sym_def_app_icon.png b/core/res/res/drawable-xxxhdpi/sym_def_app_icon.png
new file mode 100644
index 0000000..aee44e1
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/mipmap-hdpi/sym_def_app_icon.png b/core/res/res/mipmap-hdpi/sym_def_app_icon.png
index c8a38ed..cde69bc 100644
--- a/core/res/res/mipmap-hdpi/sym_def_app_icon.png
+++ b/core/res/res/mipmap-hdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/mipmap-mdpi/sym_def_app_icon.png b/core/res/res/mipmap-mdpi/sym_def_app_icon.png
index b3e10f6..c133a0c 100644
--- a/core/res/res/mipmap-mdpi/sym_def_app_icon.png
+++ b/core/res/res/mipmap-mdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/mipmap-xhdpi/sym_def_app_icon.png b/core/res/res/mipmap-xhdpi/sym_def_app_icon.png
index f381f86..bfa42f0 100644
--- a/core/res/res/mipmap-xhdpi/sym_def_app_icon.png
+++ b/core/res/res/mipmap-xhdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/mipmap-xxhdpi/sym_def_app_icon.png b/core/res/res/mipmap-xxhdpi/sym_def_app_icon.png
index e3f3144..324e72c 100644
--- a/core/res/res/mipmap-xxhdpi/sym_def_app_icon.png
+++ b/core/res/res/mipmap-xxhdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/mipmap-xxxhdpi/sym_def_app_icon.png b/core/res/res/mipmap-xxxhdpi/sym_def_app_icon.png
new file mode 100644
index 0000000..aee44e1
--- /dev/null
+++ b/core/res/res/mipmap-xxxhdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/values-mcc204-mnc04/config.xml b/core/res/res/values-mcc204-mnc04/config.xml
index d7484e1..0e67669 100644
--- a/core/res/res/values-mcc204-mnc04/config.xml
+++ b/core/res/res/values-mcc204-mnc04/config.xml
@@ -38,4 +38,9 @@
1. Lenient threshold
-->
<integer name="config_LTE_RSRP_threshold_type">0</integer>
+
+ <string-array translatable="false" name="config_sms_convert_destination_number_support">
+ <item>true;BAE0000000000000</item>
+ <item>false</item>
+ </string-array>
</resources>
diff --git a/core/res/res/values-mcc310-mnc004/config.xml b/core/res/res/values-mcc310-mnc004/config.xml
index 423e250..6a34a3d 100644
--- a/core/res/res/values-mcc310-mnc004/config.xml
+++ b/core/res/res/values-mcc310-mnc004/config.xml
@@ -34,4 +34,9 @@
</string-array>
<bool name="config_auto_attach_data_on_creation">false</bool>
+
+ <string-array translatable="false" name="config_sms_convert_destination_number_support">
+ <item>true</item>
+ </string-array>
+
</resources>
diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml
index 8cb2928..2022d12 100644
--- a/core/res/res/values-mcc311-mnc480/config.xml
+++ b/core/res/res/values-mcc311-mnc480/config.xml
@@ -56,4 +56,8 @@
1. Lenient threshold
-->
<integer name="config_LTE_RSRP_threshold_type">0</integer>
+
+ <string-array translatable="false" name="config_sms_convert_destination_number_support">
+ <item>true</item>
+ </string-array>
</resources>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index bb9885c..f6a5787 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -441,4 +441,13 @@
<item>中文 (繁體)</item>
</string-array>
+ <array name="sim_colors">
+ <item>@color/Teal_700</item>
+ <item>@color/Blue_700</item>
+ <item>@color/Indigo_700</item>
+ <item>@color/Purple_700</item>
+ <item>@color/Pink_700</item>
+ <item>@color/Red_700</item>
+ </array>
+
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 91a8598..67ba27da 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -6388,7 +6388,7 @@
<!-- The summary for the Preference in a PreferenceActivity screen. -->
<attr name="summary" />
<!-- The order for the Preference (lower values are to be ordered first). If this is not
- specified, the default orderin will be alphabetic. -->
+ specified, the default ordering will be alphabetic. -->
<attr name="order" format="integer" />
<!-- When used inside of a modern PreferenceActivity, this declares
a new PreferenceFragment to be shown when the user selects this item. -->
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 9f83db4..b9825c5 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -160,5 +160,18 @@
<color name="user_icon_8">#ffff5722</color><!-- deep orange 500 -->
<color name="user_icon_default_gray">#ff9e9e9e</color><!-- gray 500 -->
<color name="user_icon_default_white">#ffffffff</color><!-- white -->
-</resources>
+ <!-- Multi-sim sim colors -->
+ <color name="Teal_700">#ff00796b</color>
+ <color name="Teal_800">#ff00695c</color>
+ <color name="Blue_700">#ff3367d6</color>
+ <color name="Blue_800">#ff2a56c6</color>
+ <color name="Indigo_700">#ff303f9f</color>
+ <color name="Indigo_800">#ff283593</color>
+ <color name="Purple_700">#ff7b1fa2</color>
+ <color name="Purple_800">#ff6a1b9a</color>
+ <color name="Pink_700">#ffc2185b</color>
+ <color name="Pink_800">#ffad1457</color>
+ <color name="Red_700">#ffc53929</color>
+ <color name="Red_800">#ffb93221</color>
+</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1534b49..6713944 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -473,6 +473,8 @@
<bool name="config_allowTheaterModeWakeFromDock">false</bool>
<!-- If this is true, allow wake from theater mode from window layout flag. -->
<bool name="config_allowTheaterModeWakeFromWindowLayout">false</bool>
+ <!-- If this is true, go to sleep when theater mode is enabled from button press -->
+ <bool name="config_goToSleepOnButtonPressTheaterMode">true</bool>
<!-- Auto-rotation behavior -->
@@ -801,6 +803,13 @@
that can be set by the user. -->
<integer name="config_screenBrightnessDoze">1</integer>
+ <!-- Allow automatic adjusting of the screen brightness while dozing in low power state. -->
+ <bool name="config_allowAutoBrightnessWhileDozing">false</bool>
+
+ <!-- If we allow automatic adjustment of screen brightness while dozing, how many times we want
+ to reduce it to preserve the battery. Value of 100% means no scaling. -->
+ <fraction name="config_screenAutoBrightnessDozeScaleFactor">100%</fraction>
+
<!-- Screen brightness used to dim the screen when the user activity
timeout expires. May be less than the minimum allowed brightness setting
that can be set by the user. -->
@@ -1907,4 +1916,23 @@
1. Lenient threshold
-->
<integer name="config_LTE_RSRP_threshold_type">1</integer>
+
+ <!-- Show the next-alarm as a zen exit condition if it occurs in the next n hours. -->
+ <integer name="config_next_alarm_condition_lookahead_threshold_hrs">12</integer>
+
+ <!-- This config is used to check if the carrier requires converting destination
+ number before sending out a SMS.
+ Formats for this configuration as below:
+ [true or false][;optional gid]
+ The logic to pick up the configuration:
+ (1) If the "config_sms_convert_destination_number_support" array has no gid
+ special item, the last one will be picked
+ (2) If the "config_sms_convert_destination_number_support" array has gid special
+ item and it matches the current sim's gid, it will be picked.
+ (3) If the "config_sms_convert_destination_number_support" array has gid special
+ item but it doesn't match the current sim's gid, the last one without gid
+ will be picked -->
+ <string-array translatable="false" name="config_sms_convert_destination_number_support">
+ <item>false</item>
+ </string-array>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 61fc161..9e25ee2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -61,6 +61,9 @@
<!-- [CHAR_LIMIT=10] Suffix added to signify duration in minutes -->
<string name="durationMinutes"><xliff:g id="minutes">%1$d</xliff:g> mins</string>
+ <!-- [CHAR_LIMIT=10] Suffix added to signify duration of one minute -->
+ <string name="durationMinute"><xliff:g id="minutes">%1$d</xliff:g> min</string>
+
<!-- [CHAR_LIMIT=10] Suffix added to signify duration of one minute with seconds -->
<string name="durationMinuteSeconds"><xliff:g id="minutes">%1$d</xliff:g> min
<xliff:g id="seconds">%2$d</xliff:g> secs</string>
@@ -977,10 +980,10 @@
<!-- TODO: Mark these as translatable when API is finalized. -->
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_broadcastScoreNetworks" translatable="false">send score networks broadcast</string>
+ <string name="permlab_broadcastNetworkPrivileged" translatable="false">send privileged network broadcasts</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permdesc_broadcastScoreNetworks" translatable="false">Allows the app
- to broadcast a notification that networks need to be scored.
+ <string name="permdesc_broadcastNetworkPrivileged" translatable="false">Allows the app
+ to send privileged network broadcasts.
Never needed for normal apps.
</string>
@@ -4879,7 +4882,10 @@
<string name="battery_saver_description">To help improve battery life, battery saver reduces your device’s performance and limits vibration and most background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery saver turns off automatically when your device is charging.</string>
<!-- [CHAR_LIMIT=NONE] Zen mode: Condition summary for built-in downtime condition, if active -->
- <string name="downtime_condition_summary">Until your downtime ends at <xliff:g id="formattedTime" example="10.00 PM">%1$s</xliff:g></string>
+ <string name="downtime_condition_summary">Until your downtime ends at <xliff:g id="formattedTime" example="10:00 PM">%1$s</xliff:g></string>
+
+ <!-- [CHAR_LIMIT=NONE] Zen mode: Condition line one for built-in downtime condition, if active -->
+ <string name="downtime_condition_line_one">Until your downtime ends</string>
<!-- Zen mode condition - summary: time duration in minutes. [CHAR LIMIT=NONE] -->
<plurals name="zen_mode_duration_minutes_summary">
@@ -4913,4 +4919,10 @@
<!-- Content description for the Toolbar icon used to collapse an expanded action mode. [CHAR LIMIT=NONE] -->
<string name="toolbar_collapse_description">Collapse</string>
+
+ <!-- Zen mode condition - summary: until next alarm. [CHAR LIMIT=NONE] -->
+ <string name="zen_mode_next_alarm_summary">Until next alarm at <xliff:g id="formattedTime" example="7:30 AM">%1$s</xliff:g></string>
+
+ <!-- Zen mode condition - line one: until next alarm. [CHAR LIMIT=NONE] -->
+ <string name="zen_mode_next_alarm_line_one">Until next alarm</string>
</resources>
diff --git a/core/res/res/values/styles_micro.xml b/core/res/res/values/styles_micro.xml
index 0c854d3..cf90b39 100644
--- a/core/res/res/values/styles_micro.xml
+++ b/core/res/res/values/styles_micro.xml
@@ -25,6 +25,14 @@
<item name="taskOpenExitAnimation">@null</item>
<item name="taskCloseEnterAnimation">@null</item>
<item name="taskCloseExitAnimation">@anim/slide_out_micro</item>
+ <item name="wallpaperOpenEnterAnimation">@null</item>
+ <item name="wallpaperOpenExitAnimation">@anim/slide_out_micro</item>
+ <item name="wallpaperCloseEnterAnimation">@anim/slide_in_micro</item>
+ <item name="wallpaperCloseExitAnimation">@null</item>
+ <item name="wallpaperIntraOpenEnterAnimation">@null</item>
+ <item name="wallpaperIntraOpenExitAnimation">@anim/slide_out_micro</item>
+ <item name="wallpaperIntraCloseEnterAnimation">@anim/slide_in_micro</item>
+ <item name="wallpaperIntraCloseExitAnimation">@null</item>
</style>
<style name="AlertDialog.Micro" parent="AlertDialog.Holo.Light">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3835d8b..0ee8b7c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -574,6 +574,7 @@
<java-symbol type="string" name="durationHourMinutes" />
<java-symbol type="string" name="durationHourMinute" />
<java-symbol type="string" name="durationMinutes" />
+ <java-symbol type="string" name="durationMinute" />
<java-symbol type="string" name="durationMinuteSeconds" />
<java-symbol type="string" name="durationMinuteSecond" />
<java-symbol type="string" name="durationSeconds" />
@@ -1073,6 +1074,7 @@
<java-symbol type="array" name="networkAttributes" />
<java-symbol type="array" name="preloaded_color_state_lists" />
<java-symbol type="array" name="preloaded_drawables" />
+ <java-symbol type="array" name="sim_colors" />
<java-symbol type="array" name="special_locale_codes" />
<java-symbol type="array" name="special_locale_names" />
<java-symbol type="array" name="config_masterVolumeRamp" />
@@ -1574,6 +1576,7 @@
<java-symbol type="bool" name="config_enableNetworkLocationOverlay" />
<java-symbol type="bool" name="config_sf_limitedAlpha" />
<java-symbol type="bool" name="config_unplugTurnsOnScreen" />
+ <java-symbol type="bool" name="config_allowAutoBrightnessWhileDozing" />
<java-symbol type="bool" name="config_allowTheaterModeWakeFromUnplug" />
<java-symbol type="bool" name="config_allowTheaterModeWakeFromGesture" />
<java-symbol type="bool" name="config_allowTheaterModeWakeFromCameraLens" />
@@ -1583,6 +1586,7 @@
<java-symbol type="bool" name="config_allowTheaterModeWakeFromLidSwitch" />
<java-symbol type="bool" name="config_allowTheaterModeWakeFromDock" />
<java-symbol type="bool" name="config_allowTheaterModeWakeFromWindowLayout" />
+ <java-symbol type="bool" name="config_goToSleepOnButtonPressTheaterMode" />
<java-symbol type="bool" name="config_wifi_background_scan_support" />
<java-symbol type="bool" name="config_wifi_dual_band_support" />
<java-symbol type="bool" name="config_wimaxEnabled" />
@@ -1615,6 +1619,7 @@
<java-symbol type="id" name="replace_app_icon" />
<java-symbol type="id" name="replace_message" />
<java-symbol type="fraction" name="config_dimBehindFadeDuration" />
+ <java-symbol type="fraction" name="config_screenAutoBrightnessDozeScaleFactor" />
<java-symbol type="integer" name="config_carDockKeepsScreenOn" />
<java-symbol type="integer" name="config_criticalBatteryWarningLevel" />
<java-symbol type="integer" name="config_datause_notification_type" />
@@ -1984,12 +1989,16 @@
<java-symbol type="string" name="timepicker_transition_end_radius_multiplier" />
<java-symbol type="string" name="battery_saver_description" />
<java-symbol type="string" name="downtime_condition_summary" />
+ <java-symbol type="string" name="downtime_condition_line_one" />
<java-symbol type="string" name="zen_mode_forever" />
<java-symbol type="plurals" name="zen_mode_duration_minutes" />
<java-symbol type="plurals" name="zen_mode_duration_hours" />
<java-symbol type="plurals" name="zen_mode_duration_minutes_summary" />
<java-symbol type="plurals" name="zen_mode_duration_hours_summary" />
<java-symbol type="string" name="zen_mode_until" />
+ <java-symbol type="string" name="zen_mode_next_alarm_summary" />
+ <java-symbol type="string" name="zen_mode_next_alarm_line_one" />
+ <java-symbol type="integer" name="config_next_alarm_condition_lookahead_threshold_hrs" />
<java-symbol type="string" name="item_is_selected" />
<java-symbol type="string" name="day_of_week_label_typeface" />
@@ -2096,6 +2105,7 @@
<java-symbol type="layout" name="simple_account_item" />
<java-symbol type="id" name="scrollIndicatorUp" />
<java-symbol type="id" name="scrollIndicatorDown" />
+ <java-symbol type="array" name="config_sms_convert_destination_number_support" />
<!-- From SignalStrength -->
<java-symbol type="integer" name="config_LTE_RSRP_threshold_type" />
diff --git a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
index f916711..9bb44d0 100644
--- a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
+++ b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
@@ -59,7 +59,7 @@
// Package 1 - Valid scorer.
Pair<ResolveInfo, ResolveInfo> package1 = buildResolveInfo("package1", true, true, false);
- // Package 2 - Receiver does not have BROADCAST_SCORE_NETWORKS permission.
+ // Package 2 - Receiver does not have BROADCAST_NETWORK_PRIVILEGED permission.
Pair<ResolveInfo, ResolveInfo> package2 = buildResolveInfo("package2", false, true, false);
// Package 3 - App does not have SCORE_NETWORKS permission.
@@ -134,7 +134,7 @@
resolveInfo.activityInfo.packageName = packageName;
resolveInfo.activityInfo.applicationInfo = new ApplicationInfo();
if (hasReceiverPermission) {
- resolveInfo.activityInfo.permission = permission.BROADCAST_SCORE_NETWORKS;
+ resolveInfo.activityInfo.permission = permission.BROADCAST_NETWORK_PRIVILEGED;
}
ResolveInfo configActivityInfo = null;
diff --git a/core/tests/inputmethodtests/src/android/os/InputMethodTest.java b/core/tests/inputmethodtests/src/android/os/InputMethodTest.java
index 577dd64..1557918 100644
--- a/core/tests/inputmethodtests/src/android/os/InputMethodTest.java
+++ b/core/tests/inputmethodtests/src/android/os/InputMethodTest.java
@@ -33,6 +33,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
public class InputMethodTest extends InstrumentationTestCase {
private static final boolean IS_AUX = true;
@@ -46,84 +47,104 @@
private static final Locale LOCALE_EN_IN = new Locale("en", "IN");
private static final Locale LOCALE_HI = new Locale("hi");
private static final Locale LOCALE_JA_JP = new Locale("ja", "JP");
+ private static final Locale LOCALE_ZH_CN = new Locale("zh", "CN");
+ private static final Locale LOCALE_ZH_TW = new Locale("zh", "TW");
private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
private static final String SUBTYPE_MODE_VOICE = "voice";
@SmallTest
public void testVoiceImes() throws Exception {
// locale: en_US
- assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US, !IS_SYSTEM_READY,
- "DummyDefaultEnKeyboardIme");
- assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US, !IS_SYSTEM_READY,
- "DummyDefaultEnKeyboardIme");
- assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US, IS_SYSTEM_READY,
- "DummyDefaultAutoVoiceIme", "DummyDefaultEnKeyboardIme");
- assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US, IS_SYSTEM_READY,
- "DummyNonDefaultAutoVoiceIme0", "DummyNonDefaultAutoVoiceIme1",
- "DummyDefaultEnKeyboardIme");
+ assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US,
+ !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
+ assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US,
+ !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme");
+ assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US,
+ IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
+ assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US,
+ IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0",
+ "DummyNonDefaultAutoVoiceIme1");
// locale: en_GB
- assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB, !IS_SYSTEM_READY,
- "DummyDefaultEnKeyboardIme");
- assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB, !IS_SYSTEM_READY,
- "DummyDefaultEnKeyboardIme");
- assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB, IS_SYSTEM_READY,
- "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
- assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB, IS_SYSTEM_READY,
- "DummyNonDefaultAutoVoiceIme0", "DummyNonDefaultAutoVoiceIme1",
- "DummyDefaultEnKeyboardIme");
+ assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB,
+ !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
+ assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB,
+ !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme");
+ assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB,
+ IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
+ assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB,
+ IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0",
+ "DummyNonDefaultAutoVoiceIme1");
// locale: ja_JP
- assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP, !IS_SYSTEM_READY,
- "DummyDefaultEnKeyboardIme");
- assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP, !IS_SYSTEM_READY,
- "DummyDefaultEnKeyboardIme");
- assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP, IS_SYSTEM_READY,
- "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
- assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP, IS_SYSTEM_READY,
- "DummyNonDefaultAutoVoiceIme0", "DummyNonDefaultAutoVoiceIme1",
- "DummyDefaultEnKeyboardIme");
+ assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP,
+ !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
+ assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP,
+ !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme");
+ assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP,
+ IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
+ assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP,
+ IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0",
+ "DummyNonDefaultAutoVoiceIme1");
}
@SmallTest
public void testKeyboardImes() throws Exception {
// locale: en_US
- assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_US, !IS_SYSTEM_READY,
- "com.android.apps.inputmethod.latin");
- assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_US, IS_SYSTEM_READY,
- "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("en-rUS"), LOCALE_EN_US,
+ !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("en-rUS"), LOCALE_EN_US,
+ IS_SYSTEM_READY, "com.android.apps.inputmethod.latin",
+ "com.android.apps.inputmethod.voice");
// locale: en_GB
- assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_GB, !IS_SYSTEM_READY,
- "com.android.apps.inputmethod.latin");
- assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_GB, IS_SYSTEM_READY,
- "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("en-rGB"), LOCALE_EN_GB,
+ !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("en-rGB"), LOCALE_EN_GB,
+ IS_SYSTEM_READY, "com.android.apps.inputmethod.latin",
+ "com.android.apps.inputmethod.voice");
// locale: en_IN
- assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_IN, !IS_SYSTEM_READY,
- "com.android.apps.inputmethod.latin");
- assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_IN, IS_SYSTEM_READY,
- "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin",
- "com.android.apps.inputmethod.hindi");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("en-rIN"), LOCALE_EN_IN,
+ !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("en-rIN"), LOCALE_EN_IN,
+ IS_SYSTEM_READY, "com.android.apps.inputmethod.hindi",
+ "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice");
// locale: hi
- assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_HI, !IS_SYSTEM_READY,
- "com.android.apps.inputmethod.latin");
- assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_HI, IS_SYSTEM_READY,
- "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin",
- "com.android.apps.inputmethod.hindi");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("hi"), LOCALE_HI,
+ !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("hi"), LOCALE_HI,
+ IS_SYSTEM_READY, "com.android.apps.inputmethod.hindi",
+ "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice");
// locale: ja_JP
- assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_JA_JP, !IS_SYSTEM_READY,
- "com.android.apps.inputmethod.latin");
- assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_JA_JP, IS_SYSTEM_READY,
- "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin",
- "com.android.apps.inputmethod.japanese");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("ja-rJP"), LOCALE_JA_JP,
+ !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("ja-rJP"), LOCALE_JA_JP,
+ IS_SYSTEM_READY, "com.android.apps.inputmethod.japanese",
+ "com.android.apps.inputmethod.voice");
+
+ // locale: zh_CN
+ assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rCN"), LOCALE_ZH_CN,
+ !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rCN"), LOCALE_ZH_CN,
+ IS_SYSTEM_READY, "com.android.apps.inputmethod.pinyin",
+ "com.android.apps.inputmethod.voice");
+
+ // locale: zh_TW
+ // Note: In this case, no IME is suitable for the system locale. Hence we will pick up a
+ // fallback IME regardless of the "default" attribute.
+ assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rTW"), LOCALE_ZH_TW,
+ !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
+ assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rTW"), LOCALE_ZH_TW,
+ IS_SYSTEM_READY, "com.android.apps.inputmethod.latin",
+ "com.android.apps.inputmethod.voice");
}
@SmallTest
public void testParcelable() throws Exception {
- final ArrayList<InputMethodInfo> originalList = getSamplePreinstalledImes();
+ final ArrayList<InputMethodInfo> originalList = getSamplePreinstalledImes("en-rUS");
final List<InputMethodInfo> clonedList = cloneViaParcel(originalList);
assertNotNull(clonedList);
final List<InputMethodInfo> clonedClonedList = cloneViaParcel(clonedList);
@@ -139,11 +160,14 @@
}
private void assertDefaultEnabledImes(final ArrayList<InputMethodInfo> preinstalledImes,
- final Locale systemLocale, final boolean isSystemReady, String... imeNames) {
+ final Locale systemLocale, final boolean isSystemReady, String... expectedImeNames) {
final Context context = getInstrumentation().getTargetContext();
- assertEquals(new HashSet<String>(Arrays.asList(imeNames)),
- getPackageNames(callGetDefaultEnabledImesUnderWithLocale(context,
- isSystemReady, preinstalledImes, systemLocale)));
+ final String[] actualImeNames = getPackageNames(callGetDefaultEnabledImesUnderWithLocale(
+ context, isSystemReady, preinstalledImes, systemLocale));
+ assertEquals(expectedImeNames.length, actualImeNames.length);
+ for (int i = 0; i < expectedImeNames.length; ++i) {
+ assertEquals(expectedImeNames[i], actualImeNames[i]);
+ }
}
private static List<InputMethodInfo> cloneViaParcel(final List<InputMethodInfo> list) {
@@ -172,11 +196,10 @@
}
}
- private HashSet<String> getPackageNames(final ArrayList<InputMethodInfo> imis) {
- final HashSet<String> packageNames = new HashSet<>();
- for (final InputMethodInfo imi : imis) {
- final String actualPackageName = imi.getPackageName();
- packageNames.add(actualPackageName);
+ private String[] getPackageNames(final ArrayList<InputMethodInfo> imis) {
+ final String[] packageNames = new String[imis.size()];
+ for (int i = 0; i < imis.size(); ++i) {
+ packageNames[i] = imis.get(i).getPackageName();
}
return packageNames;
}
@@ -278,21 +301,34 @@
return preinstalledImes;
}
- private static ArrayList<InputMethodInfo> getSamplePreinstalledImes() {
+ private static boolean contains(final String[] textList, final String textToBeChecked) {
+ if (textList == null) {
+ return false;
+ }
+ for (final String text : textList) {
+ if (Objects.equals(textToBeChecked, text)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static ArrayList<InputMethodInfo> getSamplePreinstalledImes(final String localeString) {
ArrayList<InputMethodInfo> preinstalledImes = new ArrayList<>();
// a dummy Voice IME
{
+ final boolean isDefaultIme = false;
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
subtypes.add(createDummyInputMethodSubtype("", SUBTYPE_MODE_VOICE, IS_AUX,
IS_AUTO, !IS_ASCII_CAPABLE));
preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.voice",
- "com.android.inputmethod.voice", "DummyVoiceIme", IS_AUX, IS_DEFAULT,
+ "com.android.inputmethod.voice", "DummyVoiceIme", IS_AUX, isDefaultIme,
subtypes));
}
-
// a dummy Hindi IME
{
+ final boolean isDefaultIme = contains(new String[]{ "hi", "en-rIN" }, localeString);
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
// TODO: This subtype should be marked as IS_ASCII_CAPABLE
subtypes.add(createDummyInputMethodSubtype("en_IN", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
@@ -300,32 +336,36 @@
subtypes.add(createDummyInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
!IS_AUTO, !IS_ASCII_CAPABLE));
preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.hindi",
- "com.android.inputmethod.hindi", "DummyHindiIme", !IS_AUX, IS_DEFAULT,
+ "com.android.inputmethod.hindi", "DummyHindiIme", !IS_AUX, isDefaultIme,
subtypes));
}
// a dummy Pinyin IME
{
+ final boolean isDefaultIme = contains(new String[]{ "zh-rCN" }, localeString);
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
subtypes.add(createDummyInputMethodSubtype("zh_CN", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
!IS_AUTO, !IS_ASCII_CAPABLE));
- preinstalledImes.add(createDummyInputMethodInfo("ccom.android.apps.inputmethod.pinyin",
- "com.android.apps.inputmethod.pinyin", "DummyPinyinIme", !IS_AUX, IS_DEFAULT,
+ preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.pinyin",
+ "com.android.apps.inputmethod.pinyin", "DummyPinyinIme", !IS_AUX, isDefaultIme,
subtypes));
}
- // a dummy Korian IME
+ // a dummy Korean IME
{
+ final boolean isDefaultIme = contains(new String[]{ "ko" }, localeString);
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
subtypes.add(createDummyInputMethodSubtype("ko", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
!IS_AUTO, !IS_ASCII_CAPABLE));
preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.korean",
- "com.android.apps.inputmethod.korean", "DummyKorianIme", !IS_AUX, IS_DEFAULT,
+ "com.android.apps.inputmethod.korean", "DummyKoreanIme", !IS_AUX, isDefaultIme,
subtypes));
}
// a dummy Latin IME
{
+ final boolean isDefaultIme = contains(
+ new String[]{ "en-rUS", "en-rGB", "en-rIN", "en", "hi" }, localeString);
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
!IS_AUTO, IS_ASCII_CAPABLE));
@@ -336,12 +376,13 @@
subtypes.add(createDummyInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
!IS_AUTO, IS_ASCII_CAPABLE));
preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.latin",
- "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
+ "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, isDefaultIme,
subtypes));
}
// a dummy Japanese IME
{
+ final boolean isDefaultIme = contains(new String[]{ "ja", "ja-rJP" }, localeString);
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
subtypes.add(createDummyInputMethodSubtype("ja", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
!IS_AUTO, !IS_ASCII_CAPABLE));
@@ -349,7 +390,7 @@
!IS_AUTO, !IS_ASCII_CAPABLE));
preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.japanese",
"com.android.apps.inputmethod.japanese", "DummyJapaneseIme", !IS_AUX,
- IS_DEFAULT, subtypes));
+ isDefaultIme, subtypes));
}
return preinstalledImes;
diff --git a/docs/html/about/about_toc.cs b/docs/html/about/about_toc.cs
index c025b61..9033d69 100644
--- a/docs/html/about/about_toc.cs
+++ b/docs/html/about/about_toc.cs
@@ -11,6 +11,7 @@
<span class="en">Lollipop</span></a></div>
<ul>
<li><a href="<?cs var:toroot ?>about/versions/android-5.0.html">Android 5.0 APIs</a></li>
+ <li><a href="<?cs var:toroot ?>about/versions/android-5.0-changes.html">Android 5.0 Changes</a></li>
</ul>
</li>
<li class="nav-section">
diff --git a/docs/html/about/versions/android-5.0-changes.jd b/docs/html/about/versions/android-5.0-changes.jd
new file mode 100644
index 0000000..c1b9f09
--- /dev/null
+++ b/docs/html/about/versions/android-5.0-changes.jd
@@ -0,0 +1,527 @@
+page.title=Android 5.0 Changes
+excludeFromSuggestions=true
+sdk.platform.version=5.0
+sdk.platform.apiLevel=21
+@jd:body
+
+
+<div id="qv-wrapper">
+<div id="qv">
+
+<h2>In this document</h2>
+
+<ol id="toc44" class="hide-nested">
+ <li><a href="#UI"><a href="#ART">ART Runtime</a></a></li>
+ <li><a href="#BehaviorNotifications">Notifications</a></li>
+ <li><a href="#BehaviorMediaControl">Media Controls</a></li>
+ <li><a href="#BehaviorGetRecentTasks">getRecentTasks()</a></li>
+ <li><a href="#64BitSupport">64-Bit Android NDK</a></li>
+ <li><a href="#BindService">Binding to a Service</a></li>
+ <li><a href="#Power"><a href="#BehaviorWebView">WebView</a></a></li>
+ <li><a href="#custom_permissions">Custom Permissions</a></li>
+ <li><a href="#ssl">TLS/SSL Configuration</a></li>
+</ol>
+
+<h2>API Differences</h2>
+<ol>
+<li><a href="{@docRoot}sdk/api_diff/21/changes.html">API level 20 to 21 »</a> </li>
+<li><a href="{@docRoot}sdk/api_diff/preview-21/changes.html">L Developer Preview to 21 »</a> </li>
+</ol>
+
+
+<h2>See Also</h2>
+<ol>
+<li><a href="{@docRoot}about/versions/lollipop.html">Android Lollipop Highlights</a> </li>
+<li><a href="{@docRoot}about/versions/android-5.0.html">Android 5.0 API Overview</a> </li>
+</ol>
+
+
+</div>
+</div>
+
+<p>API Level: {@sdkPlatformApiLevel}</p>
+<p>Along with new features and capabilities, Android 5.0 includes a variety of changes
+API changes,
+behavior changes, system enhancements, and bug fixes. This document highlights
+some of the key changes that you should be understand and account for in your apps.</p>
+
+<p>f you have previously published an app for Android, be aware that your app
+ might be affected by these changes in Android 5.0.</p>
+
+
+<p>For a high-level look at the new platform features, instead
+see the
+<a href="{@docRoot}about/versions/lollipop.html">Android Lollipop
+highlights</a>.</p>
+
+
+
+<h2 id="ART">Android Runtime (ART)</h2>
+
+<p>In Android 5.0 the ART runtime replaces Dalvik as the platform default. The ART runtime was
+introduced in Android 4.4 on an experimental basis.</p>
+
+<p>For an overview of ART's new features, see
+<a href="https://source.android.com/devices/tech/dalvik/art.html">Introducing
+ART</a>. Some of the major new features are:</p>
+
+<ul>
+ <li>Ahead-of-time (AOT) compilation</li>
+ <li>Improved garbage collection (GC)</li>
+ <li>Improved debugging support</li>
+</ul>
+
+<p>Most Android apps should just work without any changes under ART. However, some
+techniques that work on Dalvik do not work on ART. For information about the
+most important issues, see
+<a href="{@docRoot}guide/practices/verifying-apps-art.html">Verifying App
+Behavior on the Android Runtime (ART)</a>. Pay particular attention if:</p>
+
+<ul>
+ <li>Your app uses Java Native Interface (JNI) to run C/C++ code.</li>
+ <li>You use development tools that generate non-standard code (such as some
+ obfuscators).</li>
+ <li>You use techniques that are incompatible with compacting garbage
+ collection. (ART does not currently implement compacting GC, but
+ compacting GC is under development in the Android Open Source
+ Project.)</li>
+</ul>
+
+
+<h2 id="BehaviorNotifications">Notifications</h2>
+
+<p>Make sure your notifications take these Android 5.0 changes into account.
+ To learn more about designing your notifications for Android 5.0 and higher,
+ see the <a href="{@docRoot}design/patterns/notifications.html">notifications design guide</a>.
+</p>
+
+<h3 id="NotificationsMaterialDesignStyle">Material design style</h3>
+<p>Notifications are drawn with dark text atop white (or very light) backgrounds
+ to match the new material design widgets. Make sure that all your
+ notifications look right with the new color scheme. If your notifications
+look wrong, fix them:</p>
+
+<ul>
+ <li>Use {@link android.app.Notification.Builder#setColor(int) setColor()}
+ to set an accent color in a circle behind your icon image. </li>
+ <li>Update or remove assets that involve color. The system ignores all
+ non-alpha channels in action icons and in the main notification icon. You
+ should assume that these icons will be alpha-only. The system draws
+ notification icons in white and action icons in dark gray.</li>
+</ul>
+
+<h3 id="NotificationsSoundVibration">Sound and vibration</h3>
+<p>If you are currently adding sounds and vibrations to your notifications by
+using the {@link android.media.Ringtone}, {@link android.media.MediaPlayer},
+or {@link android.os.Vibrator} classes, remove this code so that
+the system can present notifications correctly in
+<em>priority</em> mode. Instead, use
+{@link android.app.Notification.Builder} methods to add sounds and
+vibration.</p>
+
+<p>Setting the device to
+{@link android.media.AudioManager#RINGER_MODE_SILENT RINGER_MODE_SILENT} causes
+the device to enter the new priority mode. The device leaves priority
+mode if you set it to
+{@link android.media.AudioManager#RINGER_MODE_NORMAL RINGER_MODE_NORMAL} or
+{@link android.media.AudioManager#RINGER_MODE_NORMAL RINGER_MODE_VIBRATE}.</p>
+
+<p>Previously, Android used {@link android.media.AudioManager#STREAM_MUSIC STREAM_MUSIC}
+as the master stream to control volume on tablet devices. In Android 5.0, the
+master volume stream for both phone and tablet devices is now unified, and
+is controlled by {@link android.media.AudioManager#STREAM_RING STREAM_RING} or
+{@link android.media.AudioManager#STREAM_NOTIFICATION STREAM_NOTIFICATION}.</p>
+
+<h3 id="NotificationsLockscreenVisibility">Lock screen visibility</h3>
+<p>By default, notifications now appear on the user's lock screen in Android 5.0.
+Users can choose to protect sensitive information from being exposed, in which
+case the system automatically redacts the text displayed by the notification. To
+customize this redacted notification, use
+{@link android.app.Notification.Builder#setPublicVersion(android.app.Notification)
+ setPublicVersion()}.</p>
+<p>If the notification does not contain personal information, or if you want to
+allow media playback control on the notification, call the
+{@link android.app.Notification.Builder#setVisibility(int) setVisibility()}
+method and set the notification's visibility level to
+{@link android.app.Notification#VISIBILITY_PUBLIC VISIBILITY_PUBLIC}.
+</p>
+
+<h3 id="NotificationsMediaPlayback">Media playback</h3>
+<p>If you are implementing notifications that present media playback
+status or transport controls, consider using the new
+{@link android.app.Notification.MediaStyle} template instead of a custom
+{@link android.widget.RemoteViews.RemoteView} object. Whichever approach you
+choose, make sure to set the notification's visibility to
+{@link android.app.Notification#VISIBILITY_PUBLIC VISIBILITY_PUBLIC} so that
+your controls are accessible from the lock screen. Note that beginning in
+Android 5.0, the system no longer shows
+{@link android.media.RemoteControlClient} objects on the lock screen. For more
+information, see
+<a href="#BehaviorMediaControl">If your app uses RemoteControlClient</a>.</p>
+
+<h3 id="NotificationsHeadsup">Heads-up notification</h3>
+<p>Notifications may now appear in a small floating window (also called a
+ heads-up notification) when the device is active (that is, the device is
+ unlocked and its screen is on). These notifications appear similar to the
+ compact form of your notification, except that the heads-up notification also
+ shows action buttons. Users can act on, or dismiss, a heads-up notification
+ without leaving the current app.</p>
+
+<p>Examples of conditions that may trigger heads-up notifications include:</p>
+
+<ul>
+ <li>The user's activity is in fullscreen mode (the app uses
+{@link android.app.Notification#fullScreenIntent})</li>
+ <li>The notification has high priority and uses ringtones or vibrations</li>
+</ul>
+
+<p>If your app implements notifications under any of those scenarios, make sure
+that heads-up notifications are presented correctly.</p>
+
+<h2 id="BehaviorMediaControl">Media Controls and RemoteControlClient</h2>
+<p>The {@link android.media.RemoteControlClient} class is now deprecated. Switch
+ to the new {@link android.media.session.MediaSession} API as
+ soon as possible.</p>
+
+<p>Lock screens in Android 5.0 do not show transport controls for
+your {@link android.media.session.MediaSession} or
+{@link android.media.RemoteControlClient}. Instead, your app can provide
+media playback control from the lock screen through a notification. This
+gives your app more control over the presentation of media buttons, while
+providing a consistent experience for users across locked and
+unlocked devices.</p>
+
+<p>Android 5.0 introduces a new
+{@link android.app.Notification.MediaStyle} template for this purpose.
+{@link android.app.Notification.MediaStyle} converts notification
+actions that you added with
+{@link android.app.Notification.Builder#addAction(int, java.lang.CharSequence,
+ android.app.PendingIntent)
+Notification.Builder.addAction()} into compact buttons embedded in your app's
+media playback notifications. Pass your session token to the
+{@link android.app.Notification.MediaStyle#setMediaSession(android.media.session.MediaSession.Token)
+ setSession()} method to inform the system that this notification controls an
+ ongoing media session.</p>
+
+<p>Make sure to set the notification's visibility to
+ {@link android.app.Notification#VISIBILITY_PUBLIC VISIBILITY_PUBLIC}
+ to mark the notification as safe to show on any lock screen (secure or
+ otherwise). For more information, see
+ <a href="#LockscreenNotifications">Lock screen notifications</a>.</p>
+
+<p>To display media playback controls if your app is running on the
+Android <a href="{@docRoot}tv/index.html">TV</a> or
+<a href="{@docRoot}wear/index.html">Wear</a> platform, implement the
+{@link android.media.session.MediaSession} class. You should also implement
+{@link android.media.session.MediaSession} if your app needs to receive media
+button events on Android devices.</p>
+
+<h2 id="BehaviorGetRecentTasks">getRecentTasks()</h2>
+
+<p>With the introduction of the new <em>concurrent documents and activities
+tasks</em> feature in Android 5.0 (see <a href="#Recents">Concurrent
+documents and activities in the recents screen</a> below),
+the {@link android.app.ActivityManager#getRecentTasks
+ActivityManager.getRecentTasks()} method is now deprecated to improve user
+privacy. For backward compatibility, this method still returns a small subset of
+its data, including the calling application’s own tasks and possibly some other
+non-sensitive tasks (such as Home). If your app is using this method to retrieve
+its own tasks, use {@link android.app.ActivityManager#getAppTasks() getAppTasks()}
+instead to retrieve that information.</p>
+
+<h2 id="64BitSupport">64-Bit Support in the Android NDK</h2>
+
+<p>Android 5.0 introduces support for 64-bit systems. The 64-bit enhancement
+ increases address space and improves performance, while still supporting
+ existing 32-bit apps fully. The 64-bit support also improves the performance of
+ OpenSSL for cryptography. In addition, this release introduces new native
+ media NDK APIs, as well as native OpenGL ES (GLES) 3.1 support.</p>
+
+<p>To use the 64-bit support provided in Android 5.0, download and install NDK
+ Revision 10c from the
+<a href="{@docRoot}tools/sdk/ndk/index.html">Android NDK page</a>. Refer to the
+Revision 10c <a href="{@docRoot}tools/sdk/ndk/index.html#Revisions">release notes</a>
+for more information about important changes and bug fixes to the NDK.</p>
+
+<h2 id="BindService">Binding to a Service</h2>
+
+<p>The
+ {@link android.content.Context#bindService(android.content.Intent, android.content.ServiceConnection, int) Context.bindService()}
+ method now requires an explicit {@link android.content.Intent},
+and throws an exception if given an implicit intent.
+To ensure your app is secure, use an explicit intent when starting or binding
+your {@link android.app.Service}, and do not declare intent filters for the service.</p>
+
+<h2 id="BehaviorWebView">WebView</h2>
+
+<p>Android 5.0 changes the default behavior for your app.</p>
+<ul>
+<li><strong>If your app targets API level 21 or higher:</strong>
+ <ul>
+ <li>The system
+ blocks <a href="https://developer.mozilla.org/en-US/docs/Security/MixedContent"
+ class="external-link">mixed content</a> and third party cookies by default. To allow mixed
+ content and third party cookies, use the
+ {@link android.webkit.WebSettings#setMixedContentMode(int) setMixedContentMode()}
+and {@link android.webkit.CookieManager#setAcceptThirdPartyCookies(android.webkit.WebView, boolean) setAcceptThirdPartyCookies()}
+methods respectively.</li>
+ <li>The system now intelligently chooses portions of the HTML
+ document to draw. This new default behavior helps to reduce memory
+ footprint and increase performance. If you want to
+ render the whole document at once, disable this optimization by calling
+ {@link android.webkit.WebView#enableSlowWholeDocumentDraw()}.</li>
+ </ul>
+</li>
+<li><strong>If your app targets API levels lower than 21:</strong> The system
+ allows mixed content and third party cookies, and always renders the whole
+ document at once.</li>
+</ul>
+
+<h2 id="custom_permissions">Uniqueness Requirement for Custom Permissions</h2>
+
+<p>
+ As documented in the <a href=
+ "{@docRoot}guide/topics/manifest/manifest-intro.html#perms">Permissions</a>
+ overview, Android apps can define custom permissions as a means of managing
+ access to components in a proprietary way, without using the platform’s
+ pre-defined system permissions. Apps define custom permissions in <a href=
+ "http://developer.android.com/guide/topics/manifest/permission-element.html"><code>
+ <permission></code></a> elements declared in their manifest files.
+</p>
+
+<p>
+ There are a small number of scenarios where defining custom permissions is a
+ legitimate and secure approach. However, creating custom permissions is
+ sometimes unnecessary and can even introduce potential risk to an app,
+ depending on the protection level assigned to the permissions.
+</p>
+
+<p>
+ Android 5.0 includes a behavior change to ensure
+ that only one app can define a given custom permission, unless signed with the
+ same key as other apps defining the permission.
+</p>
+
+<h3>
+ Apps using duplicate custom permissions
+</h3>
+
+<p>
+ Any app can define any custom permission it wants, so it can happen
+ that multiple apps might <strong>define the same custom permission</strong>.
+ For example, if two apps offer a similar capability, they might derive the
+ same logical name for their custom permissions. Apps might also incorporate
+ common public libraries or code examples that themselves include the same
+ custom permission definitions.
+</p>
+
+<p>
+ In Android 4.4 and earlier, users were able to install multiple such
+ apps on a given device, although the system assigned the protection level
+ specified by the first-installed app.
+</p>
+
+<p>
+ Starting in Android 5.0, the system enforces a new
+ <strong>uniqueness restriction on custom permissions</strong> for
+ apps that are signed with different keys. Now only one app on a device can
+ define a given custom permission (as determined by its name), unless the
+ other app defining the permission is signed with the same key. If the user
+ tries to install an app with a duplicate custom permission and is not signed
+ with the same key as the resident app that defines the permission, the system
+ blocks the installation.
+</p>
+
+<h3>
+ Considerations for your app
+</h3>
+
+<p>
+ In Android 5.0 and later, apps can continue to define their own custom
+ permissions just as before and to request custom permissions from other apps
+ through the <code><uses-permission></code> mechanism. However with the
+ new requirement introduced in Android 5.0, you should carefully assess
+ possible impacts on your app.
+</p>
+
+<p>
+ Here are some points to consider:
+</p>
+
+<ul>
+ <li>Does your app declare any <a href=
+ "http://developer.android.com/guide/topics/manifest/permission-element.html">
+ <code><permission></code></a> elements in its manifest? If so, are
+ they actually necessary to the proper function of your app or service? Or
+ could you use a system default permission instead?
+ </li>
+
+ <li>If you have <a href=
+ "http://developer.android.com/guide/topics/manifest/permission-element.html">
+ <code><permission></code></a> elements in your app, do you know where
+ they came from?
+ </li>
+
+ <li>Do you actually intend for other apps to request your custom permissions
+ through <a href=
+ "http://developer.android.com/guide/topics/manifest/uses-permission-element.html">
+ <code><uses-permission></code></a>?
+ </li>
+
+ <li>Are you using boilerplate or example code in your app that includes
+ <a href=
+ "http://developer.android.com/guide/topics/manifest/permission-element.html">
+ <code><permission></code></a> elements? Are those permission elements
+ actually necessary?
+ </li>
+
+ <li>Do your custom permissions use names that are simple or based on common
+ terms that other apps might share?
+ </li>
+</ul>
+
+<h3>
+ New installs and updates
+</h3>
+
+<p>
+ As mentioned above, for new installs and updates of your app on devices
+ running Android 4.4 or earlier are unaffected and there is no change in
+ behavior. For new installs and updates on devices running Android 5.0 or
+ later, the system <strong>prevents installation of your app</strong> if it
+ defines a custom permission that is already defined by an existing resident
+ app.
+</p>
+
+<h3>
+ Existing installs with Android 5.0 system update
+</h3>
+
+<p>
+ If your app uses custom permissions and is widely distributed and installed,
+ there’s a chance that it will be affected when users receive update their
+ devices to Android 5.0. After the system update is installed, the system
+ revalidates installed apps, including a check of their custom permissions. If
+ your app defines a custom permission that is already defined by another app
+ that has already been validated, and your app is not signed with the same key
+ as the other app, the system <strong>does not re-install your app</strong>.
+</p>
+
+<h3>
+ Recommendations
+</h3>
+
+<p>
+ On devices running Android 5.0 or later, we recommend that you examine your
+ app immediately, make any adjustments needed, and publish the updated version
+ as soon as possible to your users.
+</p>
+
+<ul>
+ <li>If you are using custom permissions in your app, consider their origin
+ and whether you actually need them. Remove all <a href=
+ "http://developer.android.com/guide/topics/manifest/permission-element.html">
+ <code><permission></code></a> elements from your app, unless you are
+ certain that they are required for proper function of your app.
+ </li>
+
+ <li>Consider replacing your custom permissions with system default
+ permissions where possible.
+ </li>
+
+ <li>If your app requires custom permissions, rename your custom permissions
+ to be unique to your app, such as by appending them to the full package name
+ of your app.
+ </li>
+
+ <li>If you have a suite of apps <em>signed with different keys</em> and the apps
+ access a shared component by means of a custom permission, make sure that the
+ custom permission is only defined once, in the shared component. Apps that
+ use the shared component should not define the custom permission themselves,
+ but should instead request access through the <a href=
+ "{@docRoot}guide/topics/manifest/uses-permission-element.html">
+ <code><uses-permission></code></a> mechanism.
+ </li>
+
+ <li>If you have a suite of apps are <em>signed with the same key</em>,
+ each app can define the same custom permission(s) as <span style="white-space:nowrap;">needed
+ — the</span> system allows the apps to be installed in the usual way.
+ </li>
+
+</ul>
+
+
+<h2 id="ssl">
+ TLS/SSL Default Configuration Changes
+</h2>
+
+<p>
+ Android 5.0 introduces changes the default TLS/SSL configuration used by apps
+ for HTTPS and other TLS/SSL traffic:
+</p>
+
+<ul>
+ <li>TLSv1.2 and TLSv1.1 protocols are now enabled,</li>
+ <li>AES-GCM (AEAD) cipher suites are now enabled,</li>
+ <li>MD5, 3DES, export, and static key ECDH cipher suites are now disabled,</li>
+ <li>Forward Secrecy cipher suites (ECDHE and DHE) are preferred.</li>
+</ul>
+
+<p>
+ These changes may lead to breakages in HTTPS or TLS/SSL connectivity in a
+ small number of cases listed below.
+</p>
+
+<p>
+ Note that the security ProviderInstaller from Google Play services already
+ offers these changes across Android platform versions back to Android 2.3.
+</p>
+
+<h3>
+ Server does not support any of the enabled ciphers suites
+</h3>
+
+<p>
+ For example, a server might support only 3DES or MD5 cipher suites. The
+ preferred fix is to improve the server’s configuration to enable stronger and
+ more modern cipher suites and protocols. Ideally, TLSv1.2 and AES-GCM should
+ be enabled, and Forward Secrecy cipher suites (ECDHE, DHE) should be enabled
+ and preferred.
+</p>
+
+<p>
+ An alternative is to modify the app to use a custom SSLSocketFactory to
+ communicate with the server. The factory should be designed to create
+ SSLSocket instances which have some of the cipher suites required by the
+ server enabled in addition to default cipher suites.
+</p>
+
+<h3>
+ App is making wrong assumptions about cipher suites used to connect to server
+</h3>
+
+<p>
+ For example, some apps contain a custom X509TrustManager that breaks because
+ it expects the authType parameter to be RSA but encounters ECDHE_RSA or
+ DHE_RSA.
+</p>
+
+<h3>
+ Server is intolerant to TLSv1.1, TLSv1.2 or new TLS extensions
+</h3>
+
+<p>
+ For example, the TLS/SSL handshake with a server is erroneously rejected or
+ stalls. The preferred fix is to upgrade the server to comply with the TLS/SSL
+ protocol. This will make the server successfully negotiate these newer
+ protocols or negotiate TLSv1 or older protocols and ignore TLS extensions it
+ does not understand. In some cases disabling TLSv1.1 and TLSv1.2 on the
+ server may work as a stopgap measure until the server software is upgraded.
+</p>
+
+<p>
+ An alternative is to modify the app to use a custom SSLSocketFactory to
+ communicate with the server. The factory should be designed to create
+ SSLSocket instances with only those protocols enabled which are correctly
+ supported by the server.
+</p>
\ No newline at end of file
diff --git a/docs/html/about/versions/android-5.0.jd b/docs/html/about/versions/android-5.0.jd
index a438420..756b75f 100644
--- a/docs/html/about/versions/android-5.0.jd
+++ b/docs/html/about/versions/android-5.0.jd
@@ -15,17 +15,10 @@
<ol id="toc44" class="hide-nested">
<li><a href="#ApiLevel">Update your target API level</a></li>
- <li><a href="#Behaviors">Important Behavior Changes</a>
- <ol>
- <li><a href="#ART">If you haven't tested your app against the new Android Runtime (ART)...</a></li>
- <li><a href="#BehaviorNotifications">If your app implements notifications...</a></li>
- <li><a href="#BehaviorMediaControl">If your app uses RemoteControlClient...</a></li>
-<li><a href="#BehaviorGetRecentTasks">If your app uses getRecentTasks()...</a></li>
-<li><a href="#64BitSupport">If you are using the Android Native Development Kit (NDK)...</a></li>
-<li><a href="#BindService">If your app binds to a Service...</a></li>
-<li><a href="#BehaviorWebView">If your app uses a WebView...</a></li>
- </ol>
- </li>
+
+
+
+
<li><a href="#UI">User Interface</a>
<ol>
<li><a href="#MaterialDesign">Material design support</a></li>
@@ -113,6 +106,13 @@
<li><a href="{@docRoot}sdk/api_diff/preview-21/changes.html">L Developer Preview to 21 »</a> </li>
</ol>
+<h2>See Also</h2>
+<ol>
+<li><a href="{@docRoot}about/versions/android-5.0-changes.html">Android 5.0 Behavior Changes</a> </li>
+<li><a href="{@docRoot}about/versions/lollipop.html">Android Lollipop Highlights</a> </li>
+</ol>
+
+
</div>
</div>
@@ -122,12 +122,19 @@
offers new features for users and app developers. This document provides an
introduction to the most notable new APIs.</p>
+<p>
+ If you have a published app, make sure to check out the <a href=
+ "{@docRoot}about/versions/android-5.0-changes.html">Android 5.0 Behavior
+ Changes</a> that you should account for in your app. These behavior changes
+ may affect your app on Android 5.0 devices, even if you are not using new APIs
+ or targeting new functionality.
+</p>
+
<p>For a high-level look at the new platform features, instead
see the
<a href="{@docRoot}about/versions/lollipop.html">Android Lollipop
highlights</a>.</p>
-
<h3 id="Start">Start developing</h3>
<p>To start building apps for Android 5.0, you must first <a href="{@docRoot}sdk/index.html">get
@@ -143,8 +150,6 @@
">To test your apps on a real device, flash a Nexus 5 or Nexus 7 with the <br>
<a href="/preview/index.html#Start"><b>ANDROID PREVIEW SYSTEM IMAGE</b></a>.</p>
-
-
<h3 id="ApiLevel">Update your target API level</h3>
<p>To better optimize your app for devices running Android {@sdkPlatformVersion},
@@ -166,237 +171,18 @@
href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#ApiLevels">What is API
Level?</a></p>
-<h2 id="Behaviors">Important Behavior Changes</h2>
+<h3 id="Behaviors">Important behavior changes</h3>
-<p>If you have previously published an app for Android, be aware that your app
- might be affected by changes in Android 5.0.</p>
+<p>If you have previously published an app for Android, be aware that your app might be affected by changes in Android 5.0.</p>
-<h3 id="ART">If you haven't tested your app against the new Android Runtime (ART)...</h3>
+<p>Please see <a href="{@docRoot}about/versions/android-5.0-changes.html">Android 5.0 Changes</a> for complete information.</p>
-<p>The 4.4 release introduced a new, experimental Android runtime, ART. Under
-4.4, ART was optional, and the default runtime remained Dalvik. With Android
-5.0, ART is now the default runtime.</p>
-
-<p>For an overview of ART's new features, see
-<a href="https://source.android.com/devices/tech/dalvik/art.html">Introducing
-ART</a>. Some of the major new features are:</p>
-
-<ul>
- <li>Ahead-of-time (AOT) compilation</li>
- <li>Improved garbage collection (GC)</li>
- <li>Improved debugging support</li>
-</ul>
-
-<p>Most Android apps should just work without any changes under ART. However, some
-techniques that work on Dalvik do not work on ART. For information about the
-most important issues, see
-<a href="{@docRoot}guide/practices/verifying-apps-art.html">Verifying App
-Behavior on the Android Runtime (ART)</a>. Pay particular attention if:</p>
-
-<ul>
- <li>Your app uses Java Native Interface (JNI) to run C/C++ code.</li>
- <li>You use development tools that generate non-standard code (such as some
- obfuscators).</li>
- <li>You use techniques that are incompatible with compacting garbage
- collection. (ART does not currently implement compacting GC, but
- compacting GC is under development in the Android Open Source
- Project.)</li>
-</ul>
-
-<h3 id="BehaviorNotifications">If your app implements notifications...</h3>
-
-<p>Make sure your notifications take these Android 5.0 changes into account.
- To learn more about designing your notifications for Android 5.0 and higher,
- see the <a href="{@docRoot}design/patterns/notifications.html">notifications design guide</a>.
-</p>
-
-<h4 id="NotificationsMaterialDesignStyle">Material design style</h4>
-<p>Notifications are drawn with dark text atop white (or very light) backgrounds
- to match the new material design widgets. Make sure that all your
- notifications look right with the new color scheme. If your notifications
-look wrong, fix them:</p>
-
-<ul>
- <li>Use {@link android.app.Notification.Builder#setColor(int) setColor()}
- to set an accent color in a circle behind your icon image. </li>
- <li>Update or remove assets that involve color. The system ignores all
- non-alpha channels in action icons and in the main notification icon. You
- should assume that these icons will be alpha-only. The system draws
- notification icons in white and action icons in dark gray.</li>
-</ul>
-
-<h4 id="NotificationsSoundVibration">Sound and vibration</h4>
-<p>If you are currently adding sounds and vibrations to your notifications by
-using the {@link android.media.Ringtone}, {@link android.media.MediaPlayer},
-or {@link android.os.Vibrator} classes, remove this code so that
-the system can present notifications correctly in
-<em>priority</em> mode. Instead, use
-{@link android.app.Notification.Builder} methods to add sounds and
-vibration.</p>
-
-<p>Setting the device to
-{@link android.media.AudioManager#RINGER_MODE_SILENT RINGER_MODE_SILENT} causes
-the device to enter the new priority mode. The device leaves priority
-mode if you set it to
-{@link android.media.AudioManager#RINGER_MODE_NORMAL RINGER_MODE_NORMAL} or
-{@link android.media.AudioManager#RINGER_MODE_NORMAL RINGER_MODE_VIBRATE}.</p>
-
-<p>Previously, Android used {@link android.media.AudioManager#STREAM_MUSIC STREAM_MUSIC}
-as the master stream to control volume on tablet devices. In Android 5.0, the
-master volume stream for both phone and tablet devices is now unified, and
-is controlled by {@link android.media.AudioManager#STREAM_RING STREAM_RING} or
-{@link android.media.AudioManager#STREAM_NOTIFICATION STREAM_NOTIFICATION}.</p>
-
-<h4 id="NotificationsLockscreenVisibility">Lock screen visibility</h4>
-<p>By default, notifications now appear on the user's lock screen in Android 5.0.
-Users can choose to protect sensitive information from being exposed, in which
-case the system automatically redacts the text displayed by the notification. To
-customize this redacted notification, use
-{@link android.app.Notification.Builder#setPublicVersion(android.app.Notification)
- setPublicVersion()}.</p>
-<p>If the notification does not contain personal information, or if you want to
-allow media playback control on the notification, call the
-{@link android.app.Notification.Builder#setVisibility(int) setVisibility()}
-method and set the notification's visibility level to
-{@link android.app.Notification#VISIBILITY_PUBLIC VISIBILITY_PUBLIC}.
-</p>
-
-<h4 id="NotificationsMediaPlayback">Media playback</h4>
-<p>If you are implementing notifications that present media playback
-status or transport controls, consider using the new
-{@link android.app.Notification.MediaStyle} template instead of a custom
-{@link android.widget.RemoteViews.RemoteView} object. Whichever approach you
-choose, make sure to set the notification's visibility to
-{@link android.app.Notification#VISIBILITY_PUBLIC VISIBILITY_PUBLIC} so that
-your controls are accessible from the lock screen. Note that beginning in
-Android 5.0, the system no longer shows
-{@link android.media.RemoteControlClient} objects on the lock screen. For more
-information, see
-<a href="#BehaviorMediaControl">If your app uses RemoteControlClient</a>.</p>
-
-<h4 id="NotificationsHeadsup">Heads-up notification</h4>
-<p>Notifications may now appear in a small floating window (also called a
- heads-up notification) when the device is active (that is, the device is
- unlocked and its screen is on). These notifications appear similar to the
- compact form of your notification, except that the heads-up notification also
- shows action buttons. Users can act on, or dismiss, a heads-up notification
- without leaving the current app.</p>
-
-<p>Examples of conditions that may trigger heads-up notifications include:</p>
-
-<ul>
- <li>The user's activity is in fullscreen mode (the app uses
-{@link android.app.Notification#fullScreenIntent})</li>
- <li>The notification has high priority and uses ringtones or vibrations</li>
-</ul>
-
-<p>If your app implements notifications under any of those scenarios, make sure
-that heads-up notifications are presented correctly.</p>
-
-<h3 id="BehaviorMediaControl">If your app uses RemoteControlClient...</h3>
-<p>The {@link android.media.RemoteControlClient} class is now deprecated. Switch
- to the new {@link android.media.session.MediaSession} API as
- soon as possible.</p>
-
-<p>Lock screens in Android 5.0 do not show transport controls for
-your {@link android.media.session.MediaSession} or
-{@link android.media.RemoteControlClient}. Instead, your app can provide
-media playback control from the lock screen through a notification. This
-gives your app more control over the presentation of media buttons, while
-providing a consistent experience for users across locked and
-unlocked devices.</p>
-
-<p>Android 5.0 introduces a new
-{@link android.app.Notification.MediaStyle} template for this purpose.
-{@link android.app.Notification.MediaStyle} converts notification
-actions that you added with
-{@link android.app.Notification.Builder#addAction(int, java.lang.CharSequence,
- android.app.PendingIntent)
-Notification.Builder.addAction()} into compact buttons embedded in your app's
-media playback notifications. Pass your session token to the
-{@link android.app.Notification.MediaStyle#setMediaSession(android.media.session.MediaSession.Token)
- setSession()} method to inform the system that this notification controls an
- ongoing media session.</p>
-
-<p>Make sure to set the notification's visibility to
- {@link android.app.Notification#VISIBILITY_PUBLIC VISIBILITY_PUBLIC}
- to mark the notification as safe to show on any lock screen (secure or
- otherwise). For more information, see
- <a href="#LockscreenNotifications">Lock screen notifications</a>.</p>
-
-<p>To display media playback controls if your app is running on the
-Android <a href="{@docRoot}tv/index.html">TV</a> or
-<a href="{@docRoot}wear/index.html">Wear</a> platform, implement the
-{@link android.media.session.MediaSession} class. You should also implement
-{@link android.media.session.MediaSession} if your app needs to receive media
-button events on Android devices.</p>
-
-<h3 id="BehaviorGetRecentTasks">If your app uses getRecentTasks()...</h3>
-
-<p>With the introduction of the new <em>concurrent documents and activities
-tasks</em> feature in Android 5.0 (see <a href="#Recents">Concurrent
-documents and activities in the recents screen</a> below),
-the {@link android.app.ActivityManager#getRecentTasks
-ActivityManager.getRecentTasks()} method is now deprecated to improve user
-privacy. For backward compatibility, this method still returns a small subset of
-its data, including the calling application’s own tasks and possibly some other
-non-sensitive tasks (such as Home). If your app is using this method to retrieve
-its own tasks, use {@link android.app.ActivityManager#getAppTasks() getAppTasks()}
-instead to retrieve that information.</p>
-
-<h3 id="64BitSupport">If you are using the Android Native Development Kit (NDK)...</h3>
-
-<p>Android 5.0 introduces support for 64-bit systems. The 64-bit enhancement
- increases address space and improves performance, while still supporting
- existing 32-bit apps fully. The 64-bit support also improves the performance of
- OpenSSL for cryptography. In addition, this release introduces new native
- media NDK APIs, as well as native OpenGL ES (GLES) 3.1 support.</p>
-
-<p>To use the 64-bit support provided in Android 5.0, download and install NDK
- Revision 10c from the
-<a href="{@docRoot}tools/sdk/ndk/index.html">Android NDK page</a>. Refer to the
-Revision 10c <a href="{@docRoot}tools/sdk/ndk/index.html#Revisions">release notes</a>
-for more information about important changes and bug fixes to the NDK.</p>
-
-<h3 id="BindService">If your app binds to a Service...</h3>
-
-<p>The
- {@link android.content.Context#bindService(android.content.Intent, android.content.ServiceConnection, int) Context.bindService()}
- method now requires an explicit {@link android.content.Intent},
-and throws an exception if given an implicit intent.
-To ensure your app is secure, use an explicit intent when starting or binding
-your {@link android.app.Service}, and do not declare intent filters for the service.</p>
-
-<h3 id="BehaviorWebView">If your app uses WebView...</h3>
-
-<p>Android 5.0 changes the default behavior for your app.</p>
-<ul>
-<li><strong>If your app targets API level 21 or higher:</strong>
- <ul>
- <li>The system
- blocks <a href="https://developer.mozilla.org/en-US/docs/Security/MixedContent"
- class="external-link">mixed content</a> and third party cookies by default. To allow mixed
- content and third party cookies, use the
- {@link android.webkit.WebSettings#setMixedContentMode(int) setMixedContentMode()}
-and {@link android.webkit.CookieManager#setAcceptThirdPartyCookies(android.webkit.WebView, boolean) setAcceptThirdPartyCookies()}
-methods respectively.</li>
- <li>The system now intelligently chooses portions of the HTML
- document to draw. This new default behavior helps to reduce memory
- footprint and increase performance. If you want to
- render the whole document at once, disable this optimization by calling
- {@link android.webkit.WebView#enableSlowWholeDocumentDraw()}.</li>
- </ul>
-</li>
-<li><strong>If your app targets API levels lower than 21:</strong> The system
- allows mixed content and third party cookies, and always renders the whole
- document at once.</li>
-</ul>
<h2 id="UI">User Interface</h2>
<h3 id="MaterialDesign">Material design support</h3>
-<p>The upcoming release adds support for Android's new <em>material design</em>
+<p>Android 5.0 adds support for Android's new <em>material design</em>
style. You can create apps with material design that are visually dynamic and
have UI element transitions that feel natural to users. This support includes:</p>
diff --git a/docs/html/distribute/googleplay/googleplay_toc.cs b/docs/html/distribute/googleplay/googleplay_toc.cs
index fc7cd11..b3aa9bf 100644
--- a/docs/html/distribute/googleplay/googleplay_toc.cs
+++ b/docs/html/distribute/googleplay/googleplay_toc.cs
@@ -18,6 +18,12 @@
</div>
</li>
<li class="nav-section">
+ <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/googleplay/guide.html">
+ <span class="en">Finding Success on Google Play</span>
+ </a>
+ </div>
+ </li>
+ <li class="nav-section">
<div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/googleplay/tv.html">
<span class="en">Distributing to <span style="white-space:nowrap">Android TV</span></span>
</a>
diff --git a/docs/html/distribute/googleplay/guide.jd b/docs/html/distribute/googleplay/guide.jd
new file mode 100644
index 0000000..8317206
--- /dev/null
+++ b/docs/html/distribute/googleplay/guide.jd
@@ -0,0 +1,71 @@
+page.title=Finding Success on Google Play
+page.metaDescription=A guide to help you find success with your app or game business on Google Play.
+meta.tags="distribute", "bestpractices"
+page.tags="google play", "business", "monetize", "engagement"
+page.image=distribute/images/play_dev_guide.png
+
+@jd:body
+
+<p>
+ We’ve created a downloadable guide to help you find success with your app or
+ game business on Google Play. In it, you’ll find features, tips, and best
+ practices to help you build an effective strategy.
+</p>
+
+<p>
+ The guide is separated into the following sections:
+</p>
+<ul>
+ <li>
+ <strong>Publishing on Google Play</strong> — using the Google Play
+ Developer Console to distribute your app to over 1 billion Android users
+ worldwide.
+ </li>
+
+ <li>
+ <strong>Quality</strong> — The fundamentals of building a great app
+ and an insight into the Google Play guidelines and policies.
+ </li>
+
+ <li>
+ <strong>Discoverability & reach</strong> — Maximizing your app's
+ discoverability and reaching the widest audience possible.
+ </li>
+
+ <li>
+ <strong>Engagement & retention</strong> — Converting
+ installations into active users and improving user retention.
+ </li>
+
+ <li>
+ <strong>Monetization</strong> — Monetization strategies to generate
+ ongoing, growing revenue streams.
+ </li>
+
+ <li>
+ <strong>Measurement with Google Analytics</strong> — Understanding
+ your users and improving your app experience, conversions, and marketing.
+ </li>
+
+ <li>
+ <strong>Going global</strong> — Launching your app in local markets
+ around the world.
+ </li>
+</ul>
+
+<p>
+ Download the guide by clicking the image below or <a href=
+ "http://goo.gl/DFjbrS">get it on Google Play</a>.
+</p>
+
+<p>
+ We’ll release the guide in more languages in the coming months. Check back to
+ this website regularly as we post information on new features and best
+ practices to help you distribute and monetize your app.
+</p>
+ <div class="resource-widget resource-flow-layout col-16"
+ data-query="collection:play_dev_guide"
+ data-cardSizes="9x6"
+ data-maxResults="1">
+ </div>
+
diff --git a/docs/html/distribute/images/play_dev_guide.png b/docs/html/distribute/images/play_dev_guide.png
new file mode 100644
index 0000000..5b0c0d9
--- /dev/null
+++ b/docs/html/distribute/images/play_dev_guide.png
Binary files differ
diff --git a/docs/html/distribute/images/play_dev_guide_b.jpg b/docs/html/distribute/images/play_dev_guide_b.jpg
new file mode 100644
index 0000000..15fd59f
--- /dev/null
+++ b/docs/html/distribute/images/play_dev_guide_b.jpg
Binary files differ
diff --git a/docs/html/distribute/tools/promote/brand.jd b/docs/html/distribute/tools/promote/brand.jd
index 9b9f9a3..22441df 100644
--- a/docs/html/distribute/tools/promote/brand.jd
+++ b/docs/html/distribute/tools/promote/brand.jd
@@ -11,17 +11,17 @@
<p>Use of the Android or Google Play brands must be reviewed by the Android
Partner Marketing team. Use the <a
-href="https://docs.google.com/forms/d/1YE5gZpAAcFKjYcUddCsK1Bv9a9Y-luaLVnkazVlaJ2w/viewform">Android and Google Play Brand Permissions Inquiry form</a> to submit your
+href="https://support.google.com/googleplay/contact/brand_developer">Android and Google Play Brand Permissions Inquiry form</a> to submit your
marketing for review.</p>
<h2 id="brand-android">Android</h2>
<p>The following are guidelines for the Android brand
and related assets.</p>
-
+
<h4 style="clear:right">Android in text</h4>
-
+
<div style="float:right;clear:right;width:200px;margin:0 0 20px 30px">
<img alt="" src="{@docRoot}images/brand/mediaplayer.png">
</div>
@@ -46,7 +46,7 @@
</ul>
</li>
</ul>
-
+
<p>Any use of the Android name needs to include this
attribution in your communication:</p>
<blockquote><em>Android is a trademark of Google Inc.</em></blockquote></p>
@@ -70,10 +70,10 @@
<p>When using the Android Robot or any modification of it, proper attribution is
required under the terms of the <a href="http://creativecommons.org/licenses/by/3.0/">Creative
Commons Attribution 3.0</a> license:</p>
-
+
<blockquote><em>The Android robot is reproduced or modified from work created and shared by Google and
used according to terms described in the Creative Commons 3.0 Attribution License.</em></blockquote>
-
+
<p>You may not file trademark applications incorporating the Android robot
logo or derivatives thereof within your company logo or business name. We
want to ensure that the Android robot remains available for all to use.</p>
@@ -119,7 +119,7 @@
<a href="{@docRoot}images/brand/Google_Play_Store_600.png">600x576</a>
</p>
</div>
-
+
<h4>Google Play store icon</h4>
<p>You may use the Google Play store icon, but you may not modify it.</p>
@@ -128,29 +128,29 @@
"Google Play store." However, when labeling the Google Play store icon directly, it's OK to use
"Play Store" alone to accurately reflect the icon label as it appears on a device.</p>
-
+
<h4>Google Play badge</h4>
-
+
<div style="float:right;clear:right;width:172px;margin-left:30px">
<img src="{@docRoot}images/brand/en_app_rgb_wo_60.png" alt="">
<p style="text-align:center">
<a href="{@docRoot}images/brand/en_app_rgb_wo_45.png">129x45</a> |
<a href="{@docRoot}images/brand/en_app_rgb_wo_60.png">172x60</a></p>
</div>
-
+
<div style="float:right;clear:right;width:172px;margin-left:30px">
<img src="{@docRoot}images/brand/en_generic_rgb_wo_60.png" alt="">
<p style="text-align:center">
<a href="{@docRoot}images/brand/en_generic_rgb_wo_45.png">129x45</a> |
<a href="{@docRoot}images/brand/en_generic_rgb_wo_60.png">172x60</a></p>
</div>
-
+
<p>The "Get it on Google Play" and "Android App on Google Play" logos are
badges that you can use on your website and promotional materials, to point
to your products on Google Play. Additional Google Play badge formats and
badges for music, books, magazines, movies, and TV shows are also available.
Use the <a
- href="https://docs.google.com/forms/d/1YE5gZpAAcFKjYcUddCsK1Bv9a9Y-luaLVnkazVlaJ2w/viewform">Android
+ href="https://support.google.com/googleplay/contact/brand_developer">Android
and Google Play Brand Permissions Inquiry form</a> to request
those badges.</p>
@@ -170,22 +170,22 @@
</ul>
</li>
</ul>
-
+
<p>To quickly create a badge that links to your apps on Google Play,
use the <a
href="{@docRoot}distribute/tools/promote/badges.html">Google Play badge generator</a>
(provides the badge in over 40 languages).</p>
-
+
<p>To create your own size, download an Adobe® Illustrator® (.ai) file for the
<a href="{@docRoot}distribute/tools/promote/badge-files.html">Google Play
badge in over 40 languages</a>.</p>
-
- <p>For details on all the ways that you can link to your product details page in Google Play,
+
+ <p>For details on all the ways that you can link to your product details page in Google Play,
see <a href="{@docRoot}distribute/tools/promote/linking.html">Linking to your products</a>.</p>
<h2 id="Marketing_Review">Marketing Reviews and Brand Inquiries</h2>
<p>Use the <a
-href="https://docs.google.com/forms/d/1YE5gZpAAcFKjYcUddCsK1Bv9a9Y-luaLVnkazVlaJ2w/viewform">Android
+href="https://support.google.com/googleplay/contact/brand_developer">Android
and Google Play Brand Permissions Inquiry form</a> to submit any marketing
reviews or brand inquires. Typical response time is at least one week.</p>
diff --git a/docs/html/google/auth/api-client.jd b/docs/html/google/auth/api-client.jd
index 5331d1e..a0836d1 100644
--- a/docs/html/google/auth/api-client.jd
+++ b/docs/html/google/auth/api-client.jd
@@ -15,6 +15,7 @@
<ol>
<li><a href="#HandlingFailures">Handle connection failures</a></li>
<li><a href="#MaintainingState">Maintain state while resolving an error</a></li>
+ <li><a href="#WearableApi">Access the Wearable API</a></li>
</ol>
</li>
<li><a href="#Communicating">Communicate with Google Services</a>
@@ -104,7 +105,17 @@
<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.Builder.html#addScope(com.google.android.gms.common.api.Scope)"
>{@code addScope()}</a>.</p>
-<p>However, before you can begin a connection by calling <a
+<p class="caution">
+<strong>Important:</strong> To avoid client connection errors on devices that do not have the
+<a href="https://play.google.com/store/apps/details?id=com.google.android.wearable.app&hl=en">Android
+Wear app</a> installed, use a separate <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html">{@code
+GoogleApiClient}</a> instance to access only the <a
+href="{@docRoot}reference/com/google/android/gms/wearable/Wearable.html">{@code
+Wearable}</a> API. For more information, see <a href="#WearableApi">Access the Wearable
+API</a>.</p>
+
+<p>Before you can begin a connection by calling <a
href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()"
>{@code connect()}</a> on the <a
href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html">{@code
@@ -408,6 +419,45 @@
</p>
+<h3 id="WearableApi">Access the Wearable API</h3>
+
+<p>On devices that do not have the <a
+href="https://play.google.com/store/apps/details?id=com.google.android.wearable.app&hl=en">Android
+Wear app</a> installed, connection requests that include the <a
+href="{@docRoot}reference/com/google/android/gms/wearable/Wearable.html">{@code
+Wearable}</a> API fail with the <a
+href="{@docRoot}reference/com/google/android/gms/common/ConnectionResult.html#API_UNAVAILABLE">
+<code>API_UNAVAILABLE</code></a> error code. If your app uses the <a
+href="{@docRoot}reference/com/google/android/gms/wearable/Wearable.html">{@code
+Wearable}</a> API in addition to other Google APIs, use a separate <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html">{@code
+GoogleApiClient}</a> instance to access the <a
+href="{@docRoot}reference/com/google/android/gms/wearable/Wearable.html">{@code
+Wearable}</a> API. This approach enables you to access other Google APIs on devices that are not
+paired with a wearable device.</p>
+
+<p>When you use a separate <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html">{@code
+GoogleApiClient}</a> instance to access only the Wearable API, you can determine
+whether the <a
+href="https://play.google.com/store/apps/details?id=com.google.android.wearable.app&hl=en">Android
+Wear app</a> is installed on the device:</p>
+
+<pre>
+// Connection failed listener method for a client that only
+// requests access to the Wearable API
+@Override
+public void onConnectionFailed(ConnectionResult result) {
+ if (result.getErrorCode() == ConnectionResult.API_UNAVAILABLE) {
+ // The Android Wear app is not installed
+ }
+ ...
+}
+</pre>
+
+
+
+
<h2 id="Communicating">Communicate with Google Services</h2>
diff --git a/docs/html/images/emulator-wvga800l.png b/docs/html/images/emulator-wvga800l.png
deleted file mode 100644
index c92c1b9..0000000
--- a/docs/html/images/emulator-wvga800l.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/emulator.png b/docs/html/images/emulator.png
new file mode 100644
index 0000000..96a2507
--- /dev/null
+++ b/docs/html/images/emulator.png
Binary files differ
diff --git a/docs/html/images/emulator@2x.png b/docs/html/images/emulator@2x.png
new file mode 100644
index 0000000..9b825a7
--- /dev/null
+++ b/docs/html/images/emulator@2x.png
Binary files differ
diff --git a/docs/html/images/tools/as-attach.png b/docs/html/images/tools/as-attach.png
new file mode 100644
index 0000000..c572b1e
--- /dev/null
+++ b/docs/html/images/tools/as-attach.png
Binary files differ
diff --git a/docs/html/jd_collections.js b/docs/html/jd_collections.js
index d63580e..a38b80b 100644
--- a/docs/html/jd_collections.js
+++ b/docs/html/jd_collections.js
@@ -57,9 +57,9 @@
},
"distribute/gp/gpfelanding": {
"resources": [
+ "distribute/googleplay/guide.html",
"distribute/googleplay/tv.html",
- "distribute/googleplay/edu/about.html",
- "distribute/googleplay/edu/videos.html"
+ "distribute/googleplay/edu/about.html"
]
},
"distribute/essentials": {
@@ -781,6 +781,12 @@
"training/tv/index.html"
]
},
+ "play_dev_guide": {
+ "title": "",
+ "resources": [
+ "shareables/distribute/play_dev_guide_secrets_en.pdf"
+ ]
+ },
"distribute/stories/games": {
"title": "",
"resources": [
diff --git a/docs/html/jd_extras.js b/docs/html/jd_extras.js
index 89d9761..36f26e8 100644
--- a/docs/html/jd_extras.js
+++ b/docs/html/jd_extras.js
@@ -1394,5 +1394,18 @@
"keywords": ["stories"],
"type": "Case Study Deck",
"titleFriendly": ""
+ },
+ {
+ "lang": "en",
+ "group": "",
+ "tags": [],
+ "url": "shareables/distribute/play_dev_guide_secrets_en.pdf",
+ "timestamp": null,
+ "image": "distribute/images/play_dev_guide_b.jpg",
+ "title": "The Secrets to App Success on Google Play",
+ "summary": "A guide to useful features, tips, and best practices that will help you grow a successful app business on Google Play.",
+ "keywords": ["distribute"],
+ "type": "PDF DOWNLOAD (11MB)",
+ "titleFriendly": ""
}
]);
diff --git a/docs/html/sdk/installing/studio-debug.jd b/docs/html/sdk/installing/studio-debug.jd
index 2e3e137..b048400 100644
--- a/docs/html/sdk/installing/studio-debug.jd
+++ b/docs/html/sdk/installing/studio-debug.jd
@@ -6,7 +6,11 @@
<div id="qv">
<h2>In this document</h2>
<ol>
- <li><a href="#runDebug">Run your App in Debug Mode</a></li>
+ <li><a href="#runDebug">Run your App in Debug Mode</a>
+ <ol>
+ <li><a href="#attachDebug">Attach the debugger to a running process</a></li>
+ </ol>
+ </li>
<li><a href="#systemLog">Use the System Log</a>
<ol>
<li><a href="#systemLogWrite">Write log messages in your code</a></li>
@@ -94,6 +98,22 @@
<p class="img-caption"><strong>Figure 2.</strong> The Debug tool window in Android Studio showing
the current thread and the object tree for a variable.</p>
+<h3 id="attachDebug">Attach the debugger to a running process</h3>
+
+<p>You don't always have to restart your app to debug it. To debug an app that you're already
+running:</p>
+
+<ol>
+<li>Click <strong>Attach debugger to Android proccess</strong>
+<img src="{@docRoot}images/tools/as-attach.png" alt=""
+style="vertical-align:bottom;margin:0;height:20px"/>.</li>
+<li>In the <em>Choose Process</em> window, select the device and app you want to attach the
+debugger to.</li>
+<li>To open the <em>Debug</em> tool window, click <strong>Debug</strong>
+<img src="{@docRoot}images/tools/as-debugwindowbutton.png"
+alt="" style="vertical-align:bottom;margin:0;height:20px"/>.</li>
+</ol>
+
<h2 id="systemLog">Use the System Log</h2>
diff --git a/docs/html/sdk/installing/studio.jd b/docs/html/sdk/installing/studio.jd
index 6991dea..f02cdbc 100644
--- a/docs/html/sdk/installing/studio.jd
+++ b/docs/html/sdk/installing/studio.jd
@@ -340,7 +340,6 @@
</td>
</tr>
</table>
-<p class="note"><strong>Note:</strong> The full SDK/NDK requires 13 GB of disk space.</p>
</div><!-- end pax -->
diff --git a/docs/html/tools/devices/emulator.jd b/docs/html/tools/devices/emulator.jd
index ea1549d..d7bb8c7 100644
--- a/docs/html/tools/devices/emulator.jd
+++ b/docs/html/tools/devices/emulator.jd
@@ -80,7 +80,9 @@
provides a screen in which your application is displayed, together with any other
active Android applications. </p>
-<img src="{@docRoot}images/emulator-wvga800l.png" width="367" height="349" />
+<img src="{@docRoot}images/emulator@2x.png"
+srcset="{@docRoot}images/emulator.png 1x, {@docRoot}images/emulator@2x.png 2x" alt=""
+ width="367" height="330"/>
<p>To let you model and test your application more easily, the emulator utilizes
Android Virtual Device (AVD) configurations. AVDs let you define certain hardware
diff --git a/docs/html/tools/index.jd b/docs/html/tools/index.jd
deleted file mode 100644
index 73f8969..0000000
--- a/docs/html/tools/index.jd
+++ /dev/null
@@ -1,122 +0,0 @@
-page.title=Developer Tools
-@jd:body
-
-
-<img src="{@docRoot}images/tools-home.png" style="float:right;" height="347" width="400" />
-
-<div style="position:relative;height:0">
-<div style="position:absolute;width:420px">
- <p>The Android Developer Tools (ADT) plugin for Eclipse provides
- a professional-grade development environment for building
- Android apps. It's a full Java IDE with advanced features to help you build, test, debug,
- and package your Android apps. </p>
- <p>Free, open-source, and runs on most major OS platforms.<br>To get started,
- <a href="{@docRoot}sdk/index.html">download the Android SDK.</a></p>
-</div>
-</div>
-
-<div style="margin-top:20px;"></div>
-
-<div class="col-7" style="margin-left:0">
-<h3>Full Java IDE</h3>
-
- <ul>
- <li>Android-specific refactoring, quick fixes, integrated navigation between Java and XML resources.</li>
- <li>Enhanced XML editors for Android XML resources.</li>
- <li>Static analysis tools to catch performance, usability, and correctness problems.</li>
- <li>Build support for complex projects, command-line support for CI through Ant. Includes ProGuard and app-signing. </li>
- <li>Template-based wizard to create standard Android projects and components.</li>
- </ul>
-</div>
-
-
-<div class="col-6" style="margin-right:0">
-
-<h3>Graphical UI Builders</h3>
- <ul>
- <li>Build rich Android UI with drag and drop.
- <li>Visualize your UI on tablets, phones, and other devices. Switch themes, locales, even platform versions instantly, without building.</li>
- <li>Visual refactoring lets you extracts layout for inclusion, convert layouts, extract styles.</li>
- <li>Editor support for working with custom UI components.</li>
- </ul>
-
-</div>
-
-
-<div class="col-7" style="clear:both;margin-left:0;">
-
-<h3>On-device Developer Options</h3>
-<ul>
- <li>Enable debugging over USB.</li>
- <li>Quickly capture bug reports onto the device.</li>
- <li>Show CPU usage on screen.</li>
- <li>Draw debugging information on screen such as layout bounds,
- updates on GPU views and hardware layers, and other information.</li>
- <li>Plus many more options to simulate app stresses or enable debugging options.</li>
-</ul>
-<p>To access these settings, open the <em>Developer options</em> in the
-system Settings. On Android 4.2 and higher, the Developer options screen is
-hidden by default. To make it available, go to
-<b>Settings > About phone</b> and tap <b>Build number</b> seven times. Return to the previous
-screen to find Developer options.</p>
-
-</div>
-
-<div class="col-6" style="margin-right:0">
- <img src="{@docRoot}images/tools/dev-options-inmilk.png" alt="" style="margin:-10px 0 0;">
-</div>
-
-
-<div class="col-7" style="clear:both;margin-left:0;">
-<h3>Develop on Hardware Devices</h3>
-
- <ul>
- <li>Use any commercial Android hardware device or multiple devices.</li>
- <li>Deploy your app to connected devices directy from the IDE.</li>
- <li>Live, on-device debugging, testing, and profiling.</li>
- </ul>
-</div>
-
-<div class="col-6" style="margin-right:0">
-<h3>Develop on Virtual Devices</h3>
- <ul>
- <li>Emulate any device. Use custom screen sizes, keyboards, and other hardware components. </li>
- <li>Advanced hardware emulation, including camera, sensors, multitouch, telephony.</li>
- <li>Develop and test for broad device compatibility.</li>
- </ul>
-
-</div>
-
-<div style="margin-top:20px;"></div>
-
-<div class="col-7" style="margin-left:0">
-<h3>Powerful Debugging</h3>
-
- <ul>
- <li>Full Java debugger with on-device debugging and Android-specific tools.</li>
- <li>Built-in memory analysis, performance/CPU profiling, OpenGL ES tracing.</li>
- <li>Graphical tools for debugging and optimizing UI, runtime inspecton of UI structure and performance.</li>
- <li>Runtime graphical analysis of your app's network bandwidth usage.</li>
- </ul>
-
-<h3>Testing</h3>
-
- <ul>
- <li>Fully instrumentated, scriptable test environment.</li>
- <li>Integrated reports using standard test UI.</li>
- <li>Create and run unit tests on hardware devices or emulator.</li>
- </ul>
-
-<h3>Native Development</h3>
-
- <ul>
- <li>Support for compiling and packaging existing code written in C or C++.</li>
- <li>Support for packaging multiple architectures in a single binary, for broad compatibility.</li>
- </ul>
-</div>
-
-<div class="col-6" style="margin-right:0">
- <img src="{@docRoot}images/debugging-tall.png" align="left" style="margin-top:10px">
-</div>
-
-
diff --git a/docs/html/training/articles/security-ssl.jd b/docs/html/training/articles/security-ssl.jd
index 0639fb0..7f43d9c 100644
--- a/docs/html/training/articles/security-ssl.jd
+++ b/docs/html/training/articles/security-ssl.jd
@@ -22,6 +22,7 @@
<li><a href="#Blacklisting">Blacklisting</a></li>
<li><a href="#Pinning">Pinning</a></li>
<li><a href="#ClientCert">Client Certificates</a></li>
+ <li><a href="#nogotofail">Nogotofail: Network Security Testing</a></li>
</ol>
@@ -511,8 +512,42 @@
+<h2 id="nogotofail">
+ Nogotofail: A Network Traffic Security Testing Tool
+</h2>
+<p>
+ Nogotofail is a tool gives you an easy way to confirm that your apps are safe
+ against known TLS/SSL vulnerabilities and misconfigurations. It's an
+ automated, powerful, and scalable tool for testing network security issues on
+ any device whose network traffic could be made to go through it. </p>
+ <p>Nogotofail is useful for three main use cases:
+</p>
+
+<ul>
+ <li>Finding bugs and vulnerabilities.
+ </li>
+
+ <li>Verifying fixes and watching for regressions.
+ </li>
+
+ <li>Understanding what applications and devices are generating what traffic.
+ </li>
+</ul>
+
+<p>
+ Nogotofail works for Android, iOS, Linux, Windows, Chrome OS, OSX, in fact
+ any device you use to connect to the Internet. There’s an easy-to-use client
+ to configure the settings and get notifications on Android and Linux, as well
+ as the attack engine itself which can be deployed as a router, VPN server, or
+ proxy.
+</p>
+
+<p>
+ You can access the tool at the <a href=
+ "https://github.com/google/nogotofail">Nogotofail open source project</a>.
+</p>
diff --git a/docs/html/training/material/animations.jd b/docs/html/training/material/animations.jd
index e8291b8..efc0ee3 100644
--- a/docs/html/training/material/animations.jd
+++ b/docs/html/training/material/animations.jd
@@ -84,12 +84,14 @@
int cy = (myView.getTop() + myView.getBottom()) / 2;
// get the final radius for the clipping circle
-int finalRadius = myView.getWidth();
+int finalRadius = Math.max(myView.getWidth(), myView.getHeight());
-// create and start the animator for this view
-// (the start radius is zero)
+// create the animator for this view (the start radius is zero)
Animator anim =
ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
+
+// make the view visible and start the animation
+myView.setVisibility(View.VISIBLE);
anim.start();
</pre>
diff --git a/docs/html/training/tv/start/hardware.jd b/docs/html/training/tv/start/hardware.jd
index 33d396b..fc52602 100644
--- a/docs/html/training/tv/start/hardware.jd
+++ b/docs/html/training/tv/start/hardware.jd
@@ -85,27 +85,27 @@
</tr>
<tr>
<td>Touchscreen</td>
- <td>android.hardware.touchscreen</td>
+ <td>{@code android.hardware.touchscreen}</td>
</tr>
<tr>
<td>Telephony</td>
- <td>android.hardware.telephony</td>
+ <td>{@code android.hardware.telephony}</td>
</tr>
<tr>
<td>Camera</td>
- <td>android.hardware.camera</td>
+ <td>{@code android.hardware.camera}</td>
</tr>
<tr>
<td>Near Field Communications (NFC)</td>
- <td>android.hardware.nfc</td>
+ <td>{@code android.hardware.nfc}</td>
</tr>
<tr>
<td>GPS</td>
- <td>android.hardware.location.gps</td>
+ <td>{@code android.hardware.location.gps}</td>
</tr>
<tr>
<td>Microphone</td>
- <td>android.hardware.microphone</td>
+ <td>{@code android.hardware.microphone}</td>
</tr>
</table>
@@ -142,20 +142,17 @@
android:required="false"/>
</pre>
-<p class="caution">
- <strong>Caution:</strong> Declaring an unavailable hardware feature as required by setting its
- value to {@code true} in your app manifest prevents your app from being installed on TV
- devices or appearing in the Android TV home screen launcher.
+<p>
+ All apps intended for use on TV devices must declare that the touch screen feature is not required
+ as described in <a href="{@docRoot}training/tv/start/start.html#no-touchscreen">Get Started with
+ TV Apps</a>. If your app normally uses one or more of the features listed above, change the
+ {@code android:required} attribute setting to {@code false} for those features in your manifest.
</p>
<p class="caution">
- <strong>Caution:</strong> Some <a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">{@code uses-permission}</a> manifest declarations <em>imply hardware use</em>, which can also
- prevent your app from being installed and used on TV devices. For example, requesting the
- {@link android.Manifest.permission#RECORD_AUDIO} permission in your app implies the
- {@code android.hardware.microphone} hardware feature requirement. In which case, you must declare
- the microphone feature as not required ({@code android:required="false"}) in your app manifest.
- For a list of permission requests that imply a hardware feature requirement, see <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html#permissions-features">
- {@code uses-feature}</a> guide.
+ <strong>Caution:</strong> Declaring a hardware feature as required by setting its
+ value to {@code true} prevents your app from being installed on TV
+ devices or appearing in the Android TV home screen launcher.
</p>
<p>
@@ -172,6 +169,52 @@
</p>
+<h3 id="hardware-permissions">Declaring permissions that imply hardware features</h3>
+
+<p>
+ Some <a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">{@code uses-permission}</a>
+ manifest declarations <em>imply hardware features</em>. This behavior means that requesting some
+ permissions in your app manifest can exclude your app from from being installed and used on TV
+ devices. The following commonly requested permissions create an implicit hardware feature
+ requirement:
+</p>
+
+<table>
+ <tr>
+ <th>Permission</th>
+ <th>Implied hardware feature</th>
+ </tr>
+ <tr>
+ <td>{@link android.Manifest.permission#RECORD_AUDIO}</td>
+ <td>{@code android.hardware.microphone}</td>
+ </tr>
+ <tr>
+ <td>{@link android.Manifest.permission#CAMERA}</td>
+ <td>{@code android.hardware.camera} <em>and</em> <br>
+ {@code android.hardware.camera.autofocus}</td>
+ </tr>
+ <tr>
+ <td>{@link android.Manifest.permission#ACCESS_COARSE_LOCATION}</td>
+ <td>{@code android.hardware.location} <em>and</em> <br>
+ {@code android.hardware.location.network}</td>
+ </tr>
+ <tr>
+ <td>{@link android.Manifest.permission#ACCESS_FINE_LOCATION}</td>
+ <td>{@code android.hardware.location} <em>and</em> <br>
+ {@code android.hardware.location.gps}</td>
+ </tr>
+</table>
+
+<p>
+ For a complete list of permission requests that imply a hardware feature requirement, see
+ <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html#permissions-features">{@code
+ uses-feature}</a> guide. If your app requests one of the features listed above, include a
+ <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code uses-feature}</a>
+ declaration in your manifest for the implied hardware feature that indicates it is not
+ required ({@code android:required="false"}).
+</p>
+
+
<h3 id="check-features">Checking for hardware features</h2>
<p>
diff --git a/docs/html/training/tv/start/start.jd b/docs/html/training/tv/start/start.jd
index 3b26abf..aab1a39 100644
--- a/docs/html/training/tv/start/start.jd
+++ b/docs/html/training/tv/start/start.jd
@@ -11,6 +11,7 @@
<h2>This lesson teaches you how to</h2>
<ol>
<li><a href="#dev-project">Setup a TV Project</a></li>
+ <li><a href="#tv-libraries">Add TV Support Libraries</a></li>
<li><a href="#build-it">Build TV Apps</a></li>
<li><a href="#run">Run TV Apps</a></li>
</ol>
@@ -86,17 +87,10 @@
<p>An application intended to run on TV devices must declare a launcher activity for TV
in its manifest using a {@link android.content.Intent#CATEGORY_LEANBACK_LAUNCHER} intent filter.
- This filter identifies your app as being enabled for TV, allowing it to be considered a TV app
- in Google Play. Declaring this intent also identifies which activity
+ This filter identifies your app as being enabled for TV, and is required for your app to be
+ considered a TV app in Google Play. Declaring this intent also identifies which activity
in your app to launch when a user selects its icon on the TV home screen.</p>
-<p class="caution">
- <strong>Caution:</strong> If you do not include the {@link android.content.Intent#CATEGORY_LEANBACK_LAUNCHER} intent filter in
- your app, it is not visible to users running the Google Play store on TV devices. Also, if your
- app does not have this filter when you load it onto a TV device using developer tools, the app
- does not appear in the TV user interface.
-</p>
-
<p>The following code snippet shows how to include this intent filter in your manifest:</p>
<pre>
@@ -132,6 +126,14 @@
launch on a TV device.
</p>
+<p class="caution">
+ <strong>Caution:</strong> If you do not include the
+ {@link android.content.Intent#CATEGORY_LEANBACK_LAUNCHER} intent filter in
+ your app, it is not visible to users running the Google Play store on TV devices. Also, if your
+ app does not have this filter when you load it onto a TV device using developer tools, the app
+ does not appear in the TV user interface.
+</p>
+
<p>
If you are modifying an existing app for use on TV, your app should not use the same
activity layout for TV that it does for phones and tablets. The user interface of your TV app (or
@@ -142,6 +144,31 @@
"{@docRoot}training/tv/start/layouts.html">Building TV Layouts</a>.
</p>
+
+<h3 id="no-touchscreen">Declare touchscreen not required</h3>
+
+<p>
+ Applications that are intended to run on TV devices do not rely on touch screens for input. In
+ order to make this clear, the manifest of your TV app must declare that a the {@code
+ android.hardware.touchscreen} feature is not required. This setting identifies your app as being
+ able to work on a TV device, and is required for your app to be considered a TV app in Google
+ Play. The following code example shows how to include this manifest declaration:
+</p>
+
+<pre>
+<manifest>
+ <strong><uses-feature android:name="android.hardware.touchscreen"
+ android:required="false" /></strong>
+ ...
+</manifest>
+</pre>
+
+<p class="caution">
+ <strong>Caution:</strong> You must declare that a touch screen is not required in your app
+ manifest, as shown this example code, or your app cannot appear in the Google Play store on TV
+ devices.
+</p>
+
<h3 id="banner">Provide a home screen banner</h3>
<p>
@@ -152,9 +179,10 @@
<pre>
<application
- . . .
+ ...
android:banner="@drawable/banner" >
- . . .
+
+ ...
</application>
</pre>
@@ -171,7 +199,7 @@
design guide.
</p>
-<h3 id="tv-libraries">Add TV support libraries</h3>
+<h2 id="tv-libraries">Add TV Support Libraries</h3>
<p>
The Android SDK includes support libraries that are intended for use with TV apps. These
diff --git a/docs/html/training/wearables/apps/bt-debugging.jd b/docs/html/training/wearables/apps/bt-debugging.jd
index 98cf804..7569e7e 100644
--- a/docs/html/training/wearables/apps/bt-debugging.jd
+++ b/docs/html/training/wearables/apps/bt-debugging.jd
@@ -19,7 +19,7 @@
</div>
</div>
-<p>You can debug your wearable over Bluetooth by routing it's debug output to the
+<p>You can debug your wearable over Bluetooth by routing its debug output to the
handheld device that's connected to your development machine.</p>
<h2 id="SetupDevices">Setup Devices for Debugging</h2>
@@ -90,4 +90,4 @@
adb -e logcat
adb -e shell
adb -e bugreport
-</pre>
\ No newline at end of file
+</pre>
diff --git a/docs/html/training/wearables/apps/creating.jd b/docs/html/training/wearables/apps/creating.jd
index 7252ada..018d9f7 100644
--- a/docs/html/training/wearables/apps/creating.jd
+++ b/docs/html/training/wearables/apps/creating.jd
@@ -92,6 +92,11 @@
<li>Leave the Android Wear app open on your phone.</li>
<li>Connect the wearable to your machine through USB, so you can install apps directly to it
as you develop. A message appears on both the wearable and the Android Wear app prompting you to allow debugging.</li>
+ <p class="note"><strong>Note:</strong> If you can not connect your wearable to your machine via USB,
+ follow the directions on
+ <a href="{@docRoot}training/wearables/apps/bt-debugging.html">Debugging over
+ Bluetooth</a>.
+ </p>
<li>On the Android Wear app, check <strong>Always allow from this computer</strong> and tap
<strong>OK</strong>.</li>
</ol>
diff --git a/docs/html/training/wearables/apps/index.jd b/docs/html/training/wearables/apps/index.jd
index 256205b..4bdd6bf 100644
--- a/docs/html/training/wearables/apps/index.jd
+++ b/docs/html/training/wearables/apps/index.jd
@@ -64,7 +64,7 @@
<dd>Learn how to create and display custom layouts for notifications and
activities.</dd>
<dt><a href="{@docRoot}training/wearables/apps/voice.html">Adding Voice Capabilities</a></dt>
- <dd>Learn how to launch an activity with a voice actions and how to start the
+ <dd>Learn how to launch an activity with voice actions and how to start the
system speech recognizer app to obtain free-form voice input.</dd>
<dt><a href="{@docRoot}training/wearables/apps/packaging.html">Packaging Wearable Apps</a></dt>
<dd>Learn how to package a wearable app inside a
diff --git a/docs/html/training/wearables/data-layer/accessing.jd b/docs/html/training/wearables/data-layer/accessing.jd
index 896a698..b7ecf5b 100644
--- a/docs/html/training/wearables/data-layer/accessing.jd
+++ b/docs/html/training/wearables/data-layer/accessing.jd
@@ -12,9 +12,9 @@
<h2>Dependencies and Prerequisites</h2>
<ol>
- <li><a href="{@docRoot}training/wearables/apps/environment.html">Creating
- Wearable Apps > Setting up Your Environment</a></li>
- <li><a href="{@docRoot}training/wearables/apps/creating.html">Creating
+ <li><a href="{@docRoot}training/wearables/apps/creating.html#SetupEmulator">Creating
+ Wearable Apps > Set Up an Android Wear Emulator or Device</a></li>
+ <li><a href="{@docRoot}training/wearables/apps/creating.html#CreateProject">Creating
Wearable Apps > Creating a Project</a></li>
</ol>
</div>
@@ -37,7 +37,7 @@
implementing its callbacks, and handling error cases.</p>
<pre style="clear:right">
-GoogleApiClient mGoogleAppiClient = new GoogleApiClient.Builder(this)
+GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(new ConnectionCallbacks() {
@Override
public void onConnected(Bundle connectionHint) {
@@ -55,13 +55,24 @@
Log.d(TAG, "onConnectionFailed: " + result);
}
})
+ // Request access only to the Wearable API
.addApi(Wearable.API)
.build();
</pre>
+<p class="caution">
+<strong>Important:</strong> To avoid client connection errors on devices that do not have the
+<a href="https://play.google.com/store/apps/details?id=com.google.android.wearable.app&hl=en">Android
+Wear app</a> installed, use a separate <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html">{@code
+GoogleApiClient}</a> instance to access only the <a
+href="{@docRoot}reference/com/google/android/gms/wearable/Wearable.html">{@code
+Wearable}</a> API. For more information, see <a
+href="{@docRoot}google/auth/api-client.html#WearableApi">Access the Wearable API</a>.</p>
+
<p>Before you use the data layer API, start a connection on your client by calling the
<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()">connect()</a>
method, as described in
<a href="{@docRoot}google/auth/api-client.html#Starting">Accessing Google Play services APIs</a>.
When the system invokes the <code>onConnected()</code> callback for your client, you're ready
-to use the data layer API.</p>
\ No newline at end of file
+to use the data layer API.</p>
diff --git a/docs/html/training/wearables/data-layer/data-items.jd b/docs/html/training/wearables/data-layer/data-items.jd
index 63c32ea..f843bb6 100644
--- a/docs/html/training/wearables/data-layer/data-items.jd
+++ b/docs/html/training/wearables/data-layer/data-items.jd
@@ -47,14 +47,14 @@
<p>
However, instead of working with raw bytes using <a href="{@docRoot}reference/com/google/android/gms/wearable/PutDataRequest.html#setData(byte[])">setData()</a>,
-we recommend you <a href="#data-map">use a data map</a>, which exposes
+we recommend you <a href="#SyncData">use a data map</a>, which exposes
a data item in an easy-to-use {@link android.os.Bundle}-like interface.
</p>
<h2 id="SyncData">Sync Data with a Data Map</h2>
<p>
-When possible, use the <a href="{@docRoot}reference/com/google/android/gms/wearable/DataMap.html"><code>DataMap</code></a> class,
-which lets you work with data items in the form of an Android {@link android.os.Bundle},
+When possible, use the <a href="{@docRoot}reference/com/google/android/gms/wearable/DataMap.html"><code>DataMap</code></a> class.
+This approach lets you work with data items in the form of an Android {@link android.os.Bundle},
so object serialization and de-serialization is done for you, and you can manipulate data with key-value pairs.
</p>
@@ -120,5 +120,5 @@
<p>
This is just a snippet that requires more implementation details. Learn about
how to implement a full listener service or activity in
-<a href="{@docRoot}training/wearables/data-layer/events.html">Listening for Data Layer Events</a>.
+<a href="{@docRoot}training/wearables/data-layer/events.html#Listen">Listening for Data Layer Events</a>.
</p>
\ No newline at end of file
diff --git a/docs/html/training/wearables/data-layer/events.jd b/docs/html/training/wearables/data-layer/events.jd
index 9e8acbc..9196a2c 100644
--- a/docs/html/training/wearables/data-layer/events.jd
+++ b/docs/html/training/wearables/data-layer/events.jd
@@ -159,7 +159,7 @@
}
// Loop through the events and send a message
- / to the node that created the data item.
+ // to the node that created the data item.
for (DataEvent event : events) {
Uri uri = event.getDataItem().getUri();
diff --git a/docs/html/training/wearables/data-layer/index.jd b/docs/html/training/wearables/data-layer/index.jd
index 6ef3fc7..73d9ee5 100644
--- a/docs/html/training/wearables/data-layer/index.jd
+++ b/docs/html/training/wearables/data-layer/index.jd
@@ -83,5 +83,3 @@
<dt><a href="{@docRoot}training/wearables/data-layer/events.html">Handling Data Layer Events</a></dt>
<dd>Be notified of changes and events to the data layer.</dd>
</dl>
-
-</div>
\ No newline at end of file
diff --git a/docs/html/training/wearables/data-layer/messages.jd b/docs/html/training/wearables/data-layer/messages.jd
index 71f1bb1..b3afacb8 100644
--- a/docs/html/training/wearables/data-layer/messages.jd
+++ b/docs/html/training/wearables/data-layer/messages.jd
@@ -27,7 +27,7 @@
such as sending a message to the wearable
to start an activity. You can also use messages in request/response model
where one side of the connection sends a message, does some work,
-sends back a response message.</p>
+and sends back a response message.</p>
<h2 id="SendMessage">Send a Message</h2>
@@ -95,5 +95,5 @@
<p>
This is just a snippet that requires more implementation details. Learn about
how to implement a full listener service or activity in
-<a href="{@docRoot}training/wearables/data-layer/events.html">Listening for Data Layer Events</a>.
+<a href="{@docRoot}training/wearables/data-layer/events.html#Listen">Listening for Data Layer Events</a>.
</p>
\ No newline at end of file
diff --git a/docs/html/training/wearables/notifications/creating.jd b/docs/html/training/wearables/notifications/creating.jd
index 84e3311..57ac36e 100644
--- a/docs/html/training/wearables/notifications/creating.jd
+++ b/docs/html/training/wearables/notifications/creating.jd
@@ -4,7 +4,6 @@
<div id="tb-wrapper">
<div id="tb">
-
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#Import">Import the Necessary Classes</a></li>
@@ -30,7 +29,6 @@
layouts and the wearable only displays the text and icons. However, you can create
<a href="{@docRoot}training/wearables/apps/layouts.html#CustomNotifications">create custom notifications</a>
that use custom card layouts by creating a wearable app that runs on the wearable device.</p>
-</div>
<h2 id="Import">Import the necessary classes</h2>
@@ -255,7 +253,7 @@
Notification notif = new NotificationCompat.Builder(mContext)
.setContentTitle("New mail from " + sender)
.setContentText(subject)
- .setSmallIcon(R.drawable.new_mail);
+ .setSmallIcon(R.drawable.new_mail)
.extend(wearableExtender)
.build();
</pre>
@@ -278,7 +276,7 @@
<p>If you ever need to read wearable-specific options at a later time, use the corresponding get
method for the option. This example calls the
{@link android.support.v4.app.NotificationCompat.WearableExtender#getHintHideIcon()} method to
-get whether or not this notification hides the icon:
+get whether or not this notification hides the icon:</p>
<pre>
NotificationCompat.WearableExtender wearableExtender =
new NotificationCompat.WearableExtender(notif);
@@ -304,14 +302,3 @@
features from {@link android.support.v4.app.NotificationCompat.WearableExtender}
do not work, so make sure to use {@link android.support.v4.app.NotificationCompat}.
</p>
-
-<pre>
-NotificationCompat.WearableExtender wearableExtender =
- new NotificationCompat.WearableExtender(notif);
-boolean hintHideIcon = wearableExtender.getHintHideIcon();
- </pre>
-
-<p>The {@link android.support.v4.app.NotificationCompat.WearableExtender} APIs allow you to add
-additional pages to notifications, stack notifications, and more. Continue to the following lessons
-to learn about these features.
-</p>
diff --git a/docs/html/training/wearables/notifications/index.jd b/docs/html/training/wearables/notifications/index.jd
index a7b6733..2833dfa 100644
--- a/docs/html/training/wearables/notifications/index.jd
+++ b/docs/html/training/wearables/notifications/index.jd
@@ -49,5 +49,3 @@
<dd>Learn how to place all similar notifications from your app in a stack, allowing users to view
each notification individually without adding multiple cards to the card stream.</dd>
</dl>
-
-</div>
\ No newline at end of file
diff --git a/docs/html/training/wearables/notifications/stacks.jd b/docs/html/training/wearables/notifications/stacks.jd
index 9a528a4..9e70e1b 100644
--- a/docs/html/training/wearables/notifications/stacks.jd
+++ b/docs/html/training/wearables/notifications/stacks.jd
@@ -87,7 +87,7 @@
on the summary notification.</p>
<p>This notification does not appear in your stack of notifications on the wearable, but
-appears as the only notification on the handheld device.</p>
+it appears as the only notification on the handheld device.</p>
<pre style="clear:right">
Bitmap largeIcon = BitmapFactory.decodeResource(getResources(),
diff --git a/docs/html/training/wearables/notifications/voice-input.jd b/docs/html/training/wearables/notifications/voice-input.jd
index 3ce1c80..5a49343 100644
--- a/docs/html/training/wearables/notifications/voice-input.jd
+++ b/docs/html/training/wearables/notifications/voice-input.jd
@@ -86,7 +86,7 @@
{@link android.support.v4.app.RemoteInput}:</p>
<pre>
-public static final EXTRA_VOICE_REPLY = "extra_voice_reply";
+public static final String EXTRA_VOICE_REPLY = "extra_voice_reply";
...
String replyLabel = getResources().getString(R.string.reply_label);
String[] replyChoices = getResources().getStringArray(R.array.reply_choices);
@@ -116,7 +116,7 @@
// Create the reply action and add the remote input
NotificationCompat.Action action =
new NotificationCompat.Action.Builder(R.drawable.ic_reply_icon,
- getString(R.string.label, replyPendingIntent))
+ getString(R.string.label), replyPendingIntent)
.addRemoteInput(remoteInput)
.build();
@@ -171,4 +171,4 @@
}
return null;
}
-</pre>
\ No newline at end of file
+</pre>
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 1458238..94c7026 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -943,7 +943,11 @@
float radius = st.mGradientRadius;
if (st.mGradientRadiusType == RADIUS_TYPE_FRACTION) {
- radius *= Math.min(st.mWidth, st.mHeight);
+ // Fall back to parent width or height if intrinsic
+ // size is not specified.
+ final float width = st.mWidth >= 0 ? st.mWidth : r.width();
+ final float height = st.mHeight >= 0 ? st.mHeight : r.height();
+ radius *= Math.min(width, height);
} else if (st.mGradientRadiusType == RADIUS_TYPE_FRACTION_PARENT) {
radius *= Math.min(r.width(), r.height());
}
@@ -954,9 +958,9 @@
mGradientRadius = radius;
- if (radius == 0) {
- // We can't have a shader with zero radius, so let's
- // have a very, very small radius.
+ if (radius <= 0) {
+ // We can't have a shader with non-positive radius, so
+ // let's have a very, very small radius.
radius = 0.001f;
}
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index f1e4858..3cf1021 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3645,8 +3645,12 @@
Entry entry;
status_t err = getEntry(grp, t, e, &desiredConfig, &entry);
if (err != NO_ERROR) {
+ // Only log the failure when we're not running on the host as
+ // part of a tool. The caller will do its own logging.
+#ifndef STATIC_ANDROIDFW_FOR_TOOLS
ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) (error %d)\n",
resID, t, e, err);
+#endif
return err;
}
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp
index cb3a002..21c869b 100644
--- a/libs/hwui/AmbientShadow.cpp
+++ b/libs/hwui/AmbientShadow.cpp
@@ -326,9 +326,9 @@
shadowVertexBuffer.updateVertexCount(vertexBufferIndex);
shadowVertexBuffer.updateIndexCount(indexBufferIndex);
- ShadowTessellator::checkOverflow(vertexBufferIndex, totalVertexCount, "Vertex Buffer");
- ShadowTessellator::checkOverflow(indexBufferIndex, totalIndexCount, "Index Buffer");
- ShadowTessellator::checkOverflow(umbraIndex, totalUmbraCount, "Umbra Buffer");
+ ShadowTessellator::checkOverflow(vertexBufferIndex, totalVertexCount, "Ambient Vertex Buffer");
+ ShadowTessellator::checkOverflow(indexBufferIndex, totalIndexCount, "Ambient Index Buffer");
+ ShadowTessellator::checkOverflow(umbraIndex, totalUmbraCount, "Ambient Umbra Buffer");
#if DEBUG_SHADOW
for (int i = 0; i < vertexBufferIndex; i++) {
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 7aa628c..e338686 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -340,7 +340,6 @@
TessellationCache tessellationCache;
TextDropShadowCache dropShadowCache;
FboCache fboCache;
- ResourceCache resourceCache;
GammaFontRenderer* fontRenderer;
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 4a927cf..8953166 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -39,29 +39,28 @@
}
void DisplayListData::cleanupResources() {
- Caches& caches = Caches::getInstance();
- caches.unregisterFunctors(functors.size());
- caches.resourceCache.lock();
+ ResourceCache& resourceCache = ResourceCache::getInstance();
+ resourceCache.lock();
for (size_t i = 0; i < bitmapResources.size(); i++) {
- caches.resourceCache.decrementRefcountLocked(bitmapResources.itemAt(i));
+ resourceCache.decrementRefcountLocked(bitmapResources.itemAt(i));
}
for (size_t i = 0; i < ownedBitmapResources.size(); i++) {
const SkBitmap* bitmap = ownedBitmapResources.itemAt(i);
- caches.resourceCache.decrementRefcountLocked(bitmap);
- caches.resourceCache.destructorLocked(bitmap);
+ resourceCache.decrementRefcountLocked(bitmap);
+ resourceCache.destructorLocked(bitmap);
}
for (size_t i = 0; i < patchResources.size(); i++) {
- caches.resourceCache.decrementRefcountLocked(patchResources.itemAt(i));
+ resourceCache.decrementRefcountLocked(patchResources.itemAt(i));
}
for (size_t i = 0; i < sourcePaths.size(); i++) {
- caches.resourceCache.decrementRefcountLocked(sourcePaths.itemAt(i));
+ resourceCache.decrementRefcountLocked(sourcePaths.itemAt(i));
}
- caches.resourceCache.unlock();
+ resourceCache.unlock();
for (size_t i = 0; i < paints.size(); i++) {
delete paints.itemAt(i);
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index c17dd09..1b1f6cc 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -21,7 +21,7 @@
#include <private/hwui/DrawGlInfo.h>
-#include "Caches.h"
+#include "ResourceCache.h"
#include "DeferredDisplayList.h"
#include "DisplayListLogBuffer.h"
#include "DisplayListOp.h"
@@ -32,7 +32,7 @@
namespace uirenderer {
DisplayListRenderer::DisplayListRenderer()
- : mCaches(Caches::getInstance())
+ : mResourceCache(ResourceCache::getInstance())
, mDisplayListData(NULL)
, mTranslateX(0.0f)
, mTranslateY(0.0f)
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 901e8f0..8068663 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -24,6 +24,7 @@
#include "DisplayListLogBuffer.h"
#include "RenderNode.h"
+#include "ResourceCache.h"
namespace android {
namespace uirenderer {
@@ -209,7 +210,7 @@
mDisplayListData->paths.add(pathCopy);
}
if (mDisplayListData->sourcePaths.indexOf(path) < 0) {
- mCaches.resourceCache.incrementRefcount(path);
+ mResourceCache.incrementRefcount(path);
mDisplayListData->sourcePaths.add(path);
}
return pathCopy;
@@ -273,19 +274,19 @@
// contents, and drawing again. The only fix would be to always copy it the first time,
// which doesn't seem worth the extra cycles for this unlikely case.
mDisplayListData->bitmapResources.add(bitmap);
- mCaches.resourceCache.incrementRefcount(bitmap);
+ mResourceCache.incrementRefcount(bitmap);
return bitmap;
}
inline const SkBitmap* refBitmapData(const SkBitmap* bitmap) {
mDisplayListData->ownedBitmapResources.add(bitmap);
- mCaches.resourceCache.incrementRefcount(bitmap);
+ mResourceCache.incrementRefcount(bitmap);
return bitmap;
}
inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) {
mDisplayListData->patchResources.add(patch);
- mCaches.resourceCache.incrementRefcount(patch);
+ mResourceCache.incrementRefcount(patch);
return patch;
}
@@ -293,7 +294,7 @@
DefaultKeyedVector<const SkPath*, const SkPath*> mPathMap;
DefaultKeyedVector<const SkRegion*, const SkRegion*> mRegionMap;
- Caches& mCaches;
+ ResourceCache& mResourceCache;
DisplayListData* mDisplayListData;
float mTranslateX;
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index 281ca02..27ef06f 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -812,7 +812,7 @@
// determine point shape
SkPath path;
float radius = paintInfo.halfStrokeWidth;
- if (radius == 0.0f) radius = 0.25f;
+ if (radius == 0.0f) radius = 0.5f;
if (paintInfo.cap == SkPaint::kRound_Cap) {
path.addCircle(0, 0, radius);
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index cc72ae0..0dad0dc 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -141,11 +141,12 @@
GLint status;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
+ ALOGE("Error while compiling this shader:\n===\n%s\n===", source);
// Some drivers return wrong values for GL_INFO_LOG_LENGTH
// use a fixed size instead
GLchar log[512];
glGetShaderInfoLog(shader, sizeof(log), 0, &log[0]);
- LOG_ALWAYS_FATAL("Error while compiling shader: %s", log);
+ LOG_ALWAYS_FATAL("Shader info log: %s", log);
return 0;
}
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index c9ed9a7..13c5499 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -98,9 +98,6 @@
mNeedsDisplayListDataSync = true;
delete mStagingDisplayListData;
mStagingDisplayListData = data;
- if (mStagingDisplayListData) {
- Caches::getInstance().registerFunctors(mStagingDisplayListData->functors.size());
- }
}
/**
@@ -305,6 +302,10 @@
// changes in isRenderable or, in the future, bounds
damageSelf(info);
deleteDisplayListData();
+ // TODO: Remove this caches stuff
+ if (mStagingDisplayListData && mStagingDisplayListData->functors.size()) {
+ Caches::getInstance().registerFunctors(mStagingDisplayListData->functors.size());
+ }
mDisplayListData = mStagingDisplayListData;
mStagingDisplayListData = NULL;
if (mDisplayListData) {
@@ -321,6 +322,9 @@
for (size_t i = 0; i < mDisplayListData->children().size(); i++) {
mDisplayListData->children()[i]->mRenderNode->decParentRefCount();
}
+ if (mDisplayListData->functors.size()) {
+ Caches::getInstance().unregisterFunctors(mDisplayListData->functors.size());
+ }
}
delete mDisplayListData;
mDisplayListData = NULL;
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index 329d92f..12d4928 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -21,6 +21,12 @@
#include "Caches.h"
namespace android {
+
+#ifdef USE_OPENGL_RENDERER
+using namespace uirenderer;
+ANDROID_SINGLETON_STATIC_INSTANCE(ResourceCache);
+#endif
+
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
index 8539d12..a922d53 100644
--- a/libs/hwui/ResourceCache.h
+++ b/libs/hwui/ResourceCache.h
@@ -22,6 +22,7 @@
#include <SkBitmap.h>
#include <utils/KeyedVector.h>
+#include <utils/Singleton.h>
#include <androidfw/ResourceTypes.h>
@@ -53,11 +54,14 @@
ResourceType resourceType;
};
-class ANDROID_API ResourceCache {
-public:
+class ANDROID_API ResourceCache: public Singleton<ResourceCache> {
ResourceCache();
~ResourceCache();
+ friend class Singleton<ResourceCache>;
+
+public:
+
/**
* When using these two methods, make sure to only invoke the *Locked()
* variants of increment/decrementRefcount(), recyle() and destructor()
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index df28ae8..e8f1b9a 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -618,68 +618,6 @@
}
/**
- * Converts a polygon specified with CW vertices into an array of distance-from-centroid values.
- *
- * Returns false in error conditions
- *
- * @param poly Array of vertices. Note that these *must* be CW.
- * @param polyLength The number of vertices in the polygon.
- * @param polyCentroid The centroid of the polygon, from which rays will be cast
- * @param rayDist The output array for the calculated distances, must be SHADOW_RAY_COUNT in size
- */
-bool convertPolyToRayDist(const Vector2* poly, int polyLength, const Vector2& polyCentroid,
- float* rayDist) {
- const int rays = SHADOW_RAY_COUNT;
- const float step = M_PI * 2 / rays;
-
- const Vector2* lastVertex = &(poly[polyLength - 1]);
- float startAngle = angle(*lastVertex, polyCentroid);
-
- // Start with the ray that's closest to and less than startAngle
- int rayIndex = floor((startAngle - EPSILON) / step);
- rayIndex = (rayIndex + rays) % rays; // ensure positive
-
- for (int polyIndex = 0; polyIndex < polyLength; polyIndex++) {
- /*
- * For a given pair of vertices on the polygon, poly[i-1] and poly[i], the rays that
- * intersect these will be those that are between the two angles from the centroid that the
- * vertices define.
- *
- * Because the polygon vertices are stored clockwise, the closest ray with an angle
- * *smaller* than that defined by angle(poly[i], centroid) will be the first ray that does
- * not intersect with poly[i-1], poly[i].
- */
- float currentAngle = angle(poly[polyIndex], polyCentroid);
-
- // find first ray that will not intersect the line segment poly[i-1] & poly[i]
- int firstRayIndexOnNextSegment = floor((currentAngle - EPSILON) / step);
- firstRayIndexOnNextSegment = (firstRayIndexOnNextSegment + rays) % rays; // ensure positive
-
- // Iterate through all rays that intersect with poly[i-1], poly[i] line segment.
- // This may be 0 rays.
- while (rayIndex != firstRayIndexOnNextSegment) {
- float distanceToIntersect = rayIntersectPoints(polyCentroid,
- cos(rayIndex * step),
- sin(rayIndex * step),
- *lastVertex, poly[polyIndex]);
- if (distanceToIntersect < 0) {
-#if DEBUG_SHADOW
- ALOGW("ERROR: convertPolyToRayDist failed");
-#endif
- return false; // error case, abort
- }
-
- rayDist[rayIndex] = distanceToIntersect;
-
- rayIndex = (rayIndex - 1 + rays) % rays;
- }
- lastVertex = &poly[polyIndex];
- }
-
- return true;
-}
-
-/**
* This is only for experimental purpose.
* After intersections are calculated, we could smooth the polygon if needed.
* So far, we don't think it is more appealing yet.
@@ -700,490 +638,223 @@
}
}
-/**
- * Generate a array of the angleData for either umbra or penumbra vertices.
- *
- * This array will be merged and used to guide where to shoot the rays, in clockwise order.
- *
- * @param angleDataList The result array of angle data.
- *
- * @return int The maximum angle's index in the array.
- */
-int SpotShadow::setupAngleList(VertexAngleData* angleDataList,
- int polyLength, const Vector2* polygon, const Vector2& centroid,
- bool isPenumbra, const char* name) {
- float maxAngle = FLT_MIN;
- int maxAngleIndex = 0;
- for (int i = 0; i < polyLength; i++) {
- float currentAngle = angle(polygon[i], centroid);
- if (currentAngle > maxAngle) {
- maxAngle = currentAngle;
- maxAngleIndex = i;
- }
- angleDataList[i].set(currentAngle, isPenumbra, i);
-#if DEBUG_SHADOW
- ALOGD("%s AngleList i %d %f", name, i, currentAngle);
-#endif
- }
- return maxAngleIndex;
-}
+// Index pair is meant for storing the tessellation information for the penumbra
+// area. One index must come from exterior tangent of the circles, the other one
+// must come from the interior tangent of the circles.
+struct IndexPair {
+ int outerIndex;
+ int innerIndex;
+};
-/**
- * Make sure the polygons are indeed in clockwise order.
- *
- * Possible reasons to return false: 1. The input polygon is not setup properly. 2. The hull
- * algorithm is not able to generate it properly.
- *
- * Anyway, since the algorithm depends on the clockwise, when these kind of unexpected error
- * situation is found, we need to detect it and early return without corrupting the memory.
- *
- * @return bool True if the angle list is actually from big to small.
- */
-bool SpotShadow::checkClockwise(int indexOfMaxAngle, int listLength, VertexAngleData* angleList,
- const char* name) {
- int currentIndex = indexOfMaxAngle;
-#if DEBUG_SHADOW
- ALOGD("max index %d", currentIndex);
-#endif
- for (int i = 0; i < listLength - 1; i++) {
- // TODO: Cache the last angle.
- float currentAngle = angleList[currentIndex].mAngle;
- float nextAngle = angleList[(currentIndex + 1) % listLength].mAngle;
- if (currentAngle < nextAngle) {
-#if DEBUG_SHADOW
- ALOGE("%s, is not CW, at index %d", name, currentIndex);
-#endif
- return false;
- }
- currentIndex = (currentIndex + 1) % listLength;
- }
- return true;
-}
-
-/**
- * Check the polygon is clockwise.
- *
- * @return bool True is the polygon is clockwise.
- */
-bool SpotShadow::checkPolyClockwise(int polyAngleLength, int maxPolyAngleIndex,
- const float* polyAngleList) {
- bool isPolyCW = true;
- // Starting from maxPolyAngleIndex , check around to make sure angle decrease.
- for (int i = 0; i < polyAngleLength - 1; i++) {
- float currentAngle = polyAngleList[(i + maxPolyAngleIndex) % polyAngleLength];
- float nextAngle = polyAngleList[(i + maxPolyAngleIndex + 1) % polyAngleLength];
- if (currentAngle < nextAngle) {
- isPolyCW = false;
- }
- }
- return isPolyCW;
-}
-
-/**
- * Given the sorted array of all the vertices angle data, calculate for each
- * vertices, the offset value to array element which represent the start edge
- * of the polygon we need to shoot the ray at.
- *
- * TODO: Calculate this for umbra and penumbra in one loop using one single array.
- *
- * @param distances The result of the array distance counter.
- */
-void SpotShadow::calculateDistanceCounter(bool needsOffsetToUmbra, int angleLength,
- const VertexAngleData* allVerticesAngleData, int* distances) {
-
- bool firstVertexIsPenumbra = allVerticesAngleData[0].mIsPenumbra;
- // If we want distance to inner, then we just set to 0 when we see inner.
- bool needsSearch = needsOffsetToUmbra ? firstVertexIsPenumbra : !firstVertexIsPenumbra;
- int distanceCounter = 0;
- if (needsSearch) {
- int foundIndex = -1;
- for (int i = (angleLength - 1); i >= 0; i--) {
- bool currentIsOuter = allVerticesAngleData[i].mIsPenumbra;
- // If we need distance to inner, then we need to find a inner vertex.
- if (currentIsOuter != firstVertexIsPenumbra) {
- foundIndex = i;
- break;
- }
- }
- LOG_ALWAYS_FATAL_IF(foundIndex == -1, "Wrong index found, means either"
- " umbra or penumbra's length is 0");
- distanceCounter = angleLength - foundIndex;
- }
-#if DEBUG_SHADOW
- ALOGD("distances[0] is %d", distanceCounter);
-#endif
-
- distances[0] = distanceCounter; // means never see a target poly
-
- for (int i = 1; i < angleLength; i++) {
- bool firstVertexIsPenumbra = allVerticesAngleData[i].mIsPenumbra;
- // When we needs for distance for each outer vertex to inner, then we
- // increase the distance when seeing outer vertices. Otherwise, we clear
- // to 0.
- bool needsIncrement = needsOffsetToUmbra ? firstVertexIsPenumbra : !firstVertexIsPenumbra;
- // If counter is not -1, that means we have seen an other polygon's vertex.
- if (needsIncrement && distanceCounter != -1) {
- distanceCounter++;
- } else {
- distanceCounter = 0;
- }
- distances[i] = distanceCounter;
- }
-}
-
-/**
- * Given umbra and penumbra angle data list, merge them by sorting the angle
- * from the biggest to smallest.
- *
- * @param allVerticesAngleData The result array of merged angle data.
- */
-void SpotShadow::mergeAngleList(int maxUmbraAngleIndex, int maxPenumbraAngleIndex,
- const VertexAngleData* umbraAngleList, int umbraLength,
- const VertexAngleData* penumbraAngleList, int penumbraLength,
- VertexAngleData* allVerticesAngleData) {
-
- int totalRayNumber = umbraLength + penumbraLength;
- int umbraIndex = maxUmbraAngleIndex;
- int penumbraIndex = maxPenumbraAngleIndex;
-
- float currentUmbraAngle = umbraAngleList[umbraIndex].mAngle;
- float currentPenumbraAngle = penumbraAngleList[penumbraIndex].mAngle;
-
- // TODO: Clean this up using a while loop with 2 iterators.
- for (int i = 0; i < totalRayNumber; i++) {
- if (currentUmbraAngle > currentPenumbraAngle) {
- allVerticesAngleData[i] = umbraAngleList[umbraIndex];
- umbraIndex = (umbraIndex + 1) % umbraLength;
-
- // If umbraIndex round back, that means we are running out of
- // umbra vertices to merge, so just copy all the penumbra leftover.
- // Otherwise, we update the currentUmbraAngle.
- if (umbraIndex != maxUmbraAngleIndex) {
- currentUmbraAngle = umbraAngleList[umbraIndex].mAngle;
- } else {
- for (int j = i + 1; j < totalRayNumber; j++) {
- allVerticesAngleData[j] = penumbraAngleList[penumbraIndex];
- penumbraIndex = (penumbraIndex + 1) % penumbraLength;
- }
- break;
- }
- } else {
- allVerticesAngleData[i] = penumbraAngleList[penumbraIndex];
- penumbraIndex = (penumbraIndex + 1) % penumbraLength;
- // If penumbraIndex round back, that means we are running out of
- // penumbra vertices to merge, so just copy all the umbra leftover.
- // Otherwise, we update the currentPenumbraAngle.
- if (penumbraIndex != maxPenumbraAngleIndex) {
- currentPenumbraAngle = penumbraAngleList[penumbraIndex].mAngle;
- } else {
- for (int j = i + 1; j < totalRayNumber; j++) {
- allVerticesAngleData[j] = umbraAngleList[umbraIndex];
- umbraIndex = (umbraIndex + 1) % umbraLength;
- }
- break;
- }
- }
- }
-}
-
-#if DEBUG_SHADOW
-/**
- * DEBUG ONLY: Verify all the offset compuation is correctly done by examining
- * each vertex and its neighbor.
- */
-static void verifyDistanceCounter(const VertexAngleData* allVerticesAngleData,
- const int* distances, int angleLength, const char* name) {
- int currentDistance = distances[0];
- for (int i = 1; i < angleLength; i++) {
- if (distances[i] != INT_MIN) {
- if (!((currentDistance + 1) == distances[i]
- || distances[i] == 0)) {
- ALOGE("Wrong distance found at i %d name %s", i, name);
- }
- currentDistance = distances[i];
- if (currentDistance != 0) {
- bool currentOuter = allVerticesAngleData[i].mIsPenumbra;
- for (int j = 1; j <= (currentDistance - 1); j++) {
- bool neigborOuter =
- allVerticesAngleData[(i + angleLength - j) % angleLength].mIsPenumbra;
- if (neigborOuter != currentOuter) {
- ALOGE("Wrong distance found at i %d name %s", i, name);
- }
- }
- bool oppositeOuter =
- allVerticesAngleData[(i + angleLength - currentDistance) % angleLength].mIsPenumbra;
- if (oppositeOuter == currentOuter) {
- ALOGE("Wrong distance found at i %d name %s", i, name);
- }
- }
- }
- }
-}
-
-/**
- * DEBUG ONLY: Verify all the angle data compuated are is correctly done
- */
-static void verifyAngleData(int totalRayNumber, const VertexAngleData* allVerticesAngleData,
- const int* distancesToInner, const int* distancesToOuter,
- const VertexAngleData* umbraAngleList, int maxUmbraAngleIndex, int umbraLength,
- const VertexAngleData* penumbraAngleList, int maxPenumbraAngleIndex,
- int penumbraLength) {
- for (int i = 0; i < totalRayNumber; i++) {
- ALOGD("currentAngleList i %d, angle %f, isInner %d, index %d distancesToInner"
- " %d distancesToOuter %d", i, allVerticesAngleData[i].mAngle,
- !allVerticesAngleData[i].mIsPenumbra,
- allVerticesAngleData[i].mVertexIndex, distancesToInner[i], distancesToOuter[i]);
- }
-
- verifyDistanceCounter(allVerticesAngleData, distancesToInner, totalRayNumber, "distancesToInner");
- verifyDistanceCounter(allVerticesAngleData, distancesToOuter, totalRayNumber, "distancesToOuter");
-
- for (int i = 0; i < totalRayNumber; i++) {
- if ((distancesToInner[i] * distancesToOuter[i]) != 0) {
- ALOGE("distancesToInner wrong at index %d distancesToInner[i] %d,"
- " distancesToOuter[i] %d", i, distancesToInner[i], distancesToOuter[i]);
- }
- }
- int currentUmbraVertexIndex =
- umbraAngleList[maxUmbraAngleIndex].mVertexIndex;
- int currentPenumbraVertexIndex =
- penumbraAngleList[maxPenumbraAngleIndex].mVertexIndex;
- for (int i = 0; i < totalRayNumber; i++) {
- if (allVerticesAngleData[i].mIsPenumbra == true) {
- if (allVerticesAngleData[i].mVertexIndex != currentPenumbraVertexIndex) {
- ALOGW("wrong penumbra indexing i %d allVerticesAngleData[i].mVertexIndex %d "
- "currentpenumbraVertexIndex %d", i,
- allVerticesAngleData[i].mVertexIndex, currentPenumbraVertexIndex);
- }
- currentPenumbraVertexIndex = (currentPenumbraVertexIndex + 1) % penumbraLength;
- } else {
- if (allVerticesAngleData[i].mVertexIndex != currentUmbraVertexIndex) {
- ALOGW("wrong umbra indexing i %d allVerticesAngleData[i].mVertexIndex %d "
- "currentUmbraVertexIndex %d", i,
- allVerticesAngleData[i].mVertexIndex, currentUmbraVertexIndex);
- }
- currentUmbraVertexIndex = (currentUmbraVertexIndex + 1) % umbraLength;
- }
- }
- for (int i = 0; i < totalRayNumber - 1; i++) {
- float currentAngle = allVerticesAngleData[i].mAngle;
- float nextAngle = allVerticesAngleData[(i + 1) % totalRayNumber].mAngle;
- if (currentAngle < nextAngle) {
- ALOGE("Unexpected angle values!, currentAngle nextAngle %f %f", currentAngle, nextAngle);
- }
- }
-}
-#endif
-
-/**
- * In order to compute the occluded umbra, we need to setup the angle data list
- * for the polygon data. Since we only store one poly vertex per polygon vertex,
- * this array only needs to be a float array which are the angles for each vertex.
- *
- * @param polyAngleList The result list
- *
- * @return int The index for the maximum angle in this array.
- */
-int SpotShadow::setupPolyAngleList(float* polyAngleList, int polyAngleLength,
- const Vector2* poly2d, const Vector2& centroid) {
- int maxPolyAngleIndex = -1;
- float maxPolyAngle = -FLT_MAX;
- for (int i = 0; i < polyAngleLength; i++) {
- polyAngleList[i] = angle(poly2d[i], centroid);
- if (polyAngleList[i] > maxPolyAngle) {
- maxPolyAngle = polyAngleList[i];
- maxPolyAngleIndex = i;
- }
- }
- return maxPolyAngleIndex;
-}
-
-/**
- * For umbra and penumbra, given the offset info and the current ray number,
- * find the right edge index (the (starting vertex) for the ray to shoot at.
- *
- * @return int The index of the starting vertex of the edge.
- */
-inline int SpotShadow::getEdgeStartIndex(const int* offsets, int rayIndex, int totalRayNumber,
- const VertexAngleData* allVerticesAngleData) {
- int tempOffset = offsets[rayIndex];
- int targetRayIndex = (rayIndex - tempOffset + totalRayNumber) % totalRayNumber;
- return allVerticesAngleData[targetRayIndex].mVertexIndex;
-}
-
-/**
- * For the occluded umbra, given the array of angles, find the index of the
- * starting vertex of the edge, for the ray to shoo at.
- *
- * TODO: Save the last result to shorten the search distance.
- *
- * @return int The index of the starting vertex of the edge.
- */
-inline int SpotShadow::getPolyEdgeStartIndex(int maxPolyAngleIndex, int polyLength,
- const float* polyAngleList, float rayAngle) {
- int minPolyAngleIndex = (maxPolyAngleIndex + polyLength - 1) % polyLength;
+// For one penumbra vertex, find the cloest umbra vertex and return its index.
+inline int getClosestUmbraIndex(const Vector2& pivot, const Vector2* polygon, int polygonLength) {
+ float minLengthSquared = FLT_MAX;
int resultIndex = -1;
- if (rayAngle > polyAngleList[maxPolyAngleIndex]
- || rayAngle <= polyAngleList[minPolyAngleIndex]) {
- resultIndex = minPolyAngleIndex;
- } else {
- for (int i = 0; i < polyLength - 1; i++) {
- int currentIndex = (maxPolyAngleIndex + i) % polyLength;
- int nextIndex = (maxPolyAngleIndex + i + 1) % polyLength;
- if (rayAngle <= polyAngleList[currentIndex]
- && rayAngle > polyAngleList[nextIndex]) {
- resultIndex = currentIndex;
+ bool hasDecreased = false;
+ // Starting with some negative offset, assuming both umbra and penumbra are starting
+ // at the same angle, this can help to find the result faster.
+ // Normally, loop 3 times, we can find the closest point.
+ int offset = polygonLength - 2;
+ for (int i = 0; i < polygonLength; i++) {
+ int currentIndex = (i + offset) % polygonLength;
+ float currentLengthSquared = (pivot - polygon[currentIndex]).lengthSquared();
+ if (currentLengthSquared < minLengthSquared) {
+ if (minLengthSquared != FLT_MAX) {
+ hasDecreased = true;
}
+ minLengthSquared = currentLengthSquared;
+ resultIndex = currentIndex;
+ } else if (currentLengthSquared > minLengthSquared && hasDecreased) {
+ // Early break b/c we have found the closet one and now the length
+ // is increasing again.
+ break;
}
}
- if (CC_UNLIKELY(resultIndex == -1)) {
- // TODO: Add more error handling here.
- ALOGE("Wrong index found, means no edge can't be found for rayAngle %f", rayAngle);
+ if(resultIndex == -1) {
+ ALOGE("resultIndex is -1, the polygon must be invalid!");
+ resultIndex = 0;
}
return resultIndex;
}
-/**
- * Convert the incoming polygons into arrays of vertices, for each ray.
- * Ray only shoots when there is one vertex either on penumbra on umbra.
- *
- * Finally, it will generate vertices per ray for umbra, penumbra and optionally
- * occludedUmbra.
- *
- * Return true (success) when all vertices are generated
- */
-int SpotShadow::convertPolysToVerticesPerRay(
- bool hasOccludedUmbraArea, const Vector2* poly2d, int polyLength,
- const Vector2* umbra, int umbraLength, const Vector2* penumbra,
- int penumbraLength, const Vector2& centroid,
- Vector2* umbraVerticesPerRay, Vector2* penumbraVerticesPerRay,
- Vector2* occludedUmbraVerticesPerRay) {
- int totalRayNumber = umbraLength + penumbraLength;
-
- // For incoming umbra / penumbra polygons, we will build an intermediate data
- // structure to help us sort all the vertices according to the vertices.
- // Using this data structure, we can tell where (the angle) to shoot the ray,
- // whether we shoot at penumbra edge or umbra edge, and which edge to shoot at.
- //
- // We first parse each vertices and generate a table of VertexAngleData.
- // Based on that, we create 2 arrays telling us which edge to shoot at.
- VertexAngleData allVerticesAngleData[totalRayNumber];
- VertexAngleData umbraAngleList[umbraLength];
- VertexAngleData penumbraAngleList[penumbraLength];
-
- int polyAngleLength = hasOccludedUmbraArea ? polyLength : 0;
- float polyAngleList[polyAngleLength];
-
- const int maxUmbraAngleIndex =
- setupAngleList(umbraAngleList, umbraLength, umbra, centroid, false, "umbra");
- const int maxPenumbraAngleIndex =
- setupAngleList(penumbraAngleList, penumbraLength, penumbra, centroid, true, "penumbra");
- const int maxPolyAngleIndex = setupPolyAngleList(polyAngleList, polyAngleLength, poly2d, centroid);
-
- // Check all the polygons here are CW.
- bool isPolyCW = checkPolyClockwise(polyAngleLength, maxPolyAngleIndex, polyAngleList);
- bool isUmbraCW = checkClockwise(maxUmbraAngleIndex, umbraLength,
- umbraAngleList, "umbra");
- bool isPenumbraCW = checkClockwise(maxPenumbraAngleIndex, penumbraLength,
- penumbraAngleList, "penumbra");
-
- if (!isUmbraCW || !isPenumbraCW || !isPolyCW) {
-#if DEBUG_SHADOW
- ALOGE("One polygon is not CW isUmbraCW %d isPenumbraCW %d isPolyCW %d",
- isUmbraCW, isPenumbraCW, isPolyCW);
-#endif
- return false;
+inline bool sameDirections(bool isPositiveCross, float a, float b) {
+ if (isPositiveCross) {
+ return a >= 0 && b >= 0;
+ } else {
+ return a <= 0 && b <= 0;
}
+}
- mergeAngleList(maxUmbraAngleIndex, maxPenumbraAngleIndex,
- umbraAngleList, umbraLength, penumbraAngleList, penumbraLength,
- allVerticesAngleData);
+// Find the right polygon edge to shoot the ray at.
+inline int findPolyIndex(bool isPositiveCross, int startPolyIndex, const Vector2& umbraDir,
+ const Vector2* polyToCentroid, int polyLength) {
+ // Make sure we loop with a bound.
+ for (int i = 0; i < polyLength; i++) {
+ int currentIndex = (i + startPolyIndex) % polyLength;
+ const Vector2& currentToCentroid = polyToCentroid[currentIndex];
+ const Vector2& nextToCentroid = polyToCentroid[(currentIndex + 1) % polyLength];
- // Calculate the offset to the left most Inner vertex for each outerVertex.
- // Then the offset to the left most Outer vertex for each innerVertex.
- int offsetToInner[totalRayNumber];
- int offsetToOuter[totalRayNumber];
- calculateDistanceCounter(true, totalRayNumber, allVerticesAngleData, offsetToInner);
- calculateDistanceCounter(false, totalRayNumber, allVerticesAngleData, offsetToOuter);
-
- // Generate both umbraVerticesPerRay and penumbraVerticesPerRay
- for (int i = 0; i < totalRayNumber; i++) {
- float rayAngle = allVerticesAngleData[i].mAngle;
- bool isUmbraVertex = !allVerticesAngleData[i].mIsPenumbra;
-
- float dx = cosf(rayAngle);
- float dy = sinf(rayAngle);
- float distanceToIntersectUmbra = -1;
-
- if (isUmbraVertex) {
- // We can just copy umbra easily, and calculate the distance for the
- // occluded umbra computation.
- int startUmbraIndex = allVerticesAngleData[i].mVertexIndex;
- umbraVerticesPerRay[i] = umbra[startUmbraIndex];
- if (hasOccludedUmbraArea) {
- distanceToIntersectUmbra = (umbraVerticesPerRay[i] - centroid).length();
- }
-
- //shoot ray to penumbra only
- int startPenumbraIndex = getEdgeStartIndex(offsetToOuter, i, totalRayNumber,
- allVerticesAngleData);
- float distanceToIntersectPenumbra = rayIntersectPoints(centroid, dx, dy,
- penumbra[startPenumbraIndex],
- penumbra[(startPenumbraIndex + 1) % penumbraLength]);
- if (distanceToIntersectPenumbra < 0) {
+ float currentCrossUmbra = currentToCentroid.cross(umbraDir);
+ float umbraCrossNext = umbraDir.cross(nextToCentroid);
+ if (sameDirections(isPositiveCross, currentCrossUmbra, umbraCrossNext)) {
#if DEBUG_SHADOW
- ALOGW("convertPolyToRayDist for penumbra failed rayAngle %f dx %f dy %f",
- rayAngle, dx, dy);
+ ALOGD("findPolyIndex loop %d times , index %d", i, currentIndex );
#endif
- distanceToIntersectPenumbra = 0;
- }
- penumbraVerticesPerRay[i].x = centroid.x + dx * distanceToIntersectPenumbra;
- penumbraVerticesPerRay[i].y = centroid.y + dy * distanceToIntersectPenumbra;
- } else {
- // We can just copy the penumbra
- int startPenumbraIndex = allVerticesAngleData[i].mVertexIndex;
- penumbraVerticesPerRay[i] = penumbra[startPenumbraIndex];
-
- // And shoot ray to umbra only
- int startUmbraIndex = getEdgeStartIndex(offsetToInner, i, totalRayNumber,
- allVerticesAngleData);
-
- distanceToIntersectUmbra = rayIntersectPoints(centroid, dx, dy,
- umbra[startUmbraIndex], umbra[(startUmbraIndex + 1) % umbraLength]);
- if (distanceToIntersectUmbra < 0) {
-#if DEBUG_SHADOW
- ALOGW("convertPolyToRayDist for umbra failed rayAngle %f dx %f dy %f",
- rayAngle, dx, dy);
-#endif
- distanceToIntersectUmbra = 0;
- }
- umbraVerticesPerRay[i].x = centroid.x + dx * distanceToIntersectUmbra;
- umbraVerticesPerRay[i].y = centroid.y + dy * distanceToIntersectUmbra;
+ return currentIndex;
}
+ }
+ LOG_ALWAYS_FATAL("Can't find the right polygon's edge from startPolyIndex %d", startPolyIndex);
+ return -1;
+}
- if (hasOccludedUmbraArea) {
- // Shoot the same ray to the poly2d, and get the distance.
- int startPolyIndex = getPolyEdgeStartIndex(maxPolyAngleIndex, polyLength,
- polyAngleList, rayAngle);
-
- float distanceToIntersectPoly = rayIntersectPoints(centroid, dx, dy,
- poly2d[startPolyIndex], poly2d[(startPolyIndex + 1) % polyLength]);
- if (distanceToIntersectPoly < 0) {
- distanceToIntersectPoly = 0;
+// Generate the index pair for penumbra / umbra vertices, and more penumbra vertices
+// if needed.
+inline void genNewPenumbraAndPairWithUmbra(const Vector2* penumbra, int penumbraLength,
+ const Vector2* umbra, int umbraLength, Vector2* newPenumbra, int& newPenumbraIndex,
+ IndexPair* verticesPair, int& verticesPairIndex) {
+ // In order to keep everything in just one loop, we need to pre-compute the
+ // closest umbra vertex for the last penumbra vertex.
+ int previousClosestUmbraIndex = getClosestUmbraIndex(penumbra[penumbraLength - 1],
+ umbra, umbraLength);
+ for (int i = 0; i < penumbraLength; i++) {
+ const Vector2& currentPenumbraVertex = penumbra[i];
+ // For current penumbra vertex, starting from previousClosestUmbraIndex,
+ // then check the next one until the distance increase.
+ // The last one before the increase is the umbra vertex we need to pair with.
+ int currentUmbraIndex = previousClosestUmbraIndex;
+ float currentLengthSquared = (currentPenumbraVertex - umbra[currentUmbraIndex]).lengthSquared();
+ int currentClosestUmbraIndex = -1;
+ int indexDelta = 0;
+ for (int j = 1; j < umbraLength; j++) {
+ int newUmbraIndex = (previousClosestUmbraIndex + j) % umbraLength;
+ float newLengthSquared = (currentPenumbraVertex - umbra[newUmbraIndex]).lengthSquared();
+ if (newLengthSquared > currentLengthSquared) {
+ currentClosestUmbraIndex = (previousClosestUmbraIndex + j - 1) % umbraLength;
+ break;
+ } else {
+ currentLengthSquared = newLengthSquared;
+ indexDelta++;
}
- distanceToIntersectPoly = MathUtils::min(distanceToIntersectUmbra, distanceToIntersectPoly);
- occludedUmbraVerticesPerRay[i].x = centroid.x + dx * distanceToIntersectPoly;
- occludedUmbraVerticesPerRay[i].y = centroid.y + dy * distanceToIntersectPoly;
+ }
+ LOG_ALWAYS_FATAL_IF(currentClosestUmbraIndex == -1, "Can't find a closet umbra vertext at all");
+
+ if (indexDelta > 1) {
+ // For those umbra don't have penumbra, generate new penumbra vertices by interpolation.
+ //
+ // Assuming Pi for penumbra vertices, and Ui for umbra vertices.
+ // In the case like below P1 paired with U1 and P2 paired with U5.
+ // U2 to U4 are unpaired umbra vertices.
+ //
+ // P1 P2
+ // | |
+ // U1 U2 U3 U4 U5
+ //
+ // We will need to generate 3 more penumbra vertices P1.1, P1.2, P1.3
+ // to pair with U2 to U4.
+ //
+ // P1 P1.1 P1.2 P1.3 P2
+ // | | | | |
+ // U1 U2 U3 U4 U5
+ //
+ // That distance ratio b/t Ui to U1 and Ui to U5 decides its paired penumbra
+ // vertex's location.
+ int newPenumbraNumber = indexDelta - 1;
+
+ float accumulatedDeltaLength[newPenumbraNumber];
+ float totalDeltaLength = 0;
+
+ // To save time, cache the previous umbra vertex info outside the loop
+ // and update each loop.
+ Vector2 previousClosestUmbra = umbra[previousClosestUmbraIndex];
+ Vector2 skippedUmbra;
+ // Use umbra data to precompute the length b/t unpaired umbra vertices,
+ // and its ratio against the total length.
+ for (int k = 0; k < indexDelta; k++) {
+ int skippedUmbraIndex = (previousClosestUmbraIndex + k + 1) % umbraLength;
+ skippedUmbra = umbra[skippedUmbraIndex];
+ float currentDeltaLength = (skippedUmbra - previousClosestUmbra).length();
+
+ totalDeltaLength += currentDeltaLength;
+ accumulatedDeltaLength[k] = totalDeltaLength;
+
+ previousClosestUmbra = skippedUmbra;
+ }
+
+ const Vector2& previousPenumbra = penumbra[(i + penumbraLength - 1) % penumbraLength];
+ // Then for each unpaired umbra vertex, create a new penumbra by the ratio,
+ // and pair them togehter.
+ for (int k = 0; k < newPenumbraNumber; k++) {
+ float weightForCurrentPenumbra = 1.0f;
+ if (totalDeltaLength != 0.0f) {
+ weightForCurrentPenumbra = accumulatedDeltaLength[k] / totalDeltaLength;
+ }
+ float weightForPreviousPenumbra = 1.0f - weightForCurrentPenumbra;
+
+ Vector2 interpolatedPenumbra = currentPenumbraVertex * weightForCurrentPenumbra +
+ previousPenumbra * weightForPreviousPenumbra;
+
+ int skippedUmbraIndex = (previousClosestUmbraIndex + k + 1) % umbraLength;
+ verticesPair[verticesPairIndex++] = {newPenumbraIndex, skippedUmbraIndex};
+ newPenumbra[newPenumbraIndex++] = interpolatedPenumbra;
+ }
+ }
+ verticesPair[verticesPairIndex++] = {newPenumbraIndex, currentClosestUmbraIndex};
+ newPenumbra[newPenumbraIndex++] = currentPenumbraVertex;
+
+ previousClosestUmbraIndex = currentClosestUmbraIndex;
+ }
+}
+
+// Precompute all the polygon's vector, return true if the reference cross product is positive.
+inline bool genPolyToCentroid(const Vector2* poly2d, int polyLength,
+ const Vector2& centroid, Vector2* polyToCentroid) {
+ for (int j = 0; j < polyLength; j++) {
+ polyToCentroid[j] = poly2d[j] - centroid;
+ }
+ float refCrossProduct = 0;
+ for (int j = 0; j < polyLength; j++) {
+ refCrossProduct = polyToCentroid[j].cross(polyToCentroid[(j + 1) % polyLength]);
+ if (refCrossProduct != 0) {
+ break;
}
}
-#if DEBUG_SHADOW
- verifyAngleData(totalRayNumber, allVerticesAngleData, offsetToInner,
- offsetToOuter, umbraAngleList, maxUmbraAngleIndex, umbraLength,
- penumbraAngleList, maxPenumbraAngleIndex, penumbraLength);
-#endif
- return true; // success
+ return refCrossProduct > 0;
+}
+// For one umbra vertex, shoot an ray from centroid to it.
+// If the ray hit the polygon first, then return the intersection point as the
+// closer vertex.
+inline Vector2 getCloserVertex(const Vector2& umbraVertex, const Vector2& centroid,
+ const Vector2* poly2d, int polyLength, const Vector2* polyToCentroid,
+ bool isPositiveCross, int& previousPolyIndex) {
+ Vector2 umbraToCentroid = umbraVertex - centroid;
+ float distanceToUmbra = umbraToCentroid.length();
+ umbraToCentroid = umbraToCentroid / distanceToUmbra;
+
+ // previousPolyIndex is updated for each item such that we can minimize the
+ // looping inside findPolyIndex();
+ previousPolyIndex = findPolyIndex(isPositiveCross, previousPolyIndex,
+ umbraToCentroid, polyToCentroid, polyLength);
+
+ float dx = umbraToCentroid.x;
+ float dy = umbraToCentroid.y;
+ float distanceToIntersectPoly = rayIntersectPoints(centroid, dx, dy,
+ poly2d[previousPolyIndex], poly2d[(previousPolyIndex + 1) % polyLength]);
+ if (distanceToIntersectPoly < 0) {
+ distanceToIntersectPoly = 0;
+ }
+
+ // Pick the closer one as the occluded area vertex.
+ Vector2 closerVertex;
+ if (distanceToIntersectPoly < distanceToUmbra) {
+ closerVertex.x = centroid.x + dx * distanceToIntersectPoly;
+ closerVertex.y = centroid.y + dy * distanceToIntersectPoly;
+ } else {
+ closerVertex = umbraVertex;
+ }
+
+ return closerVertex;
}
/**
@@ -1193,7 +864,6 @@
Vector2* penumbra, int penumbraLength, Vector2* umbra, int umbraLength,
const Vector3* poly, int polyLength, VertexBuffer& shadowTriangleStrip,
const Vector2& centroid) {
-
bool hasOccludedUmbraArea = false;
Vector2 poly2d[polyLength];
@@ -1209,128 +879,140 @@
}
}
- int totalRayNum = umbraLength + penumbraLength;
- Vector2 umbraVertices[totalRayNum];
- Vector2 penumbraVertices[totalRayNum];
- Vector2 occludedUmbraVertices[totalRayNum];
- bool convertSuccess = convertPolysToVerticesPerRay(hasOccludedUmbraArea, poly2d,
- polyLength, umbra, umbraLength, penumbra, penumbraLength,
- centroid, umbraVertices, penumbraVertices, occludedUmbraVertices);
- if (!convertSuccess) {
- return;
+ // For each penumbra vertex, find its corresponding closest umbra vertex index.
+ //
+ // Penumbra Vertices marked as Pi
+ // Umbra Vertices marked as Ui
+ // (P3)
+ // (P2) | ' (P4)
+ // (P1)' | | '
+ // ' | | '
+ // (P0) ------------------------------------------------(P5)
+ // | (U0) |(U1)
+ // | |
+ // | |(U2) (P5.1)
+ // | |
+ // | |
+ // | |
+ // | |
+ // | |
+ // | |
+ // (U4)-----------------------------------(U3) (P6)
+ //
+ // At least, like P0, P1, P2, they will find the matching umbra as U0.
+ // If we jump over some umbra vertex without matching penumbra vertex, then
+ // we will generate some new penumbra vertex by interpolation. Like P6 is
+ // matching U3, but U2 is not matched with any penumbra vertex.
+ // So interpolate P5.1 out and match U2.
+ // In this way, every umbra vertex will have a matching penumbra vertex.
+ //
+ // The total pair number can be as high as umbraLength + penumbraLength.
+ const int maxNewPenumbraLength = umbraLength + penumbraLength;
+ IndexPair verticesPair[maxNewPenumbraLength];
+ int verticesPairIndex = 0;
+
+ // Cache all the existing penumbra vertices and newly interpolated vertices into a
+ // a new array.
+ Vector2 newPenumbra[maxNewPenumbraLength];
+ int newPenumbraIndex = 0;
+
+ // For each penumbra vertex, find its closet umbra vertex by comparing the
+ // neighbor umbra vertices.
+ genNewPenumbraAndPairWithUmbra(penumbra, penumbraLength, umbra, umbraLength, newPenumbra,
+ newPenumbraIndex, verticesPair, verticesPairIndex);
+ ShadowTessellator::checkOverflow(verticesPairIndex, maxNewPenumbraLength, "Spot pair");
+ ShadowTessellator::checkOverflow(newPenumbraIndex, maxNewPenumbraLength, "Spot new penumbra");
+#if DEBUG_SHADOW
+ for (int i = 0; i < umbraLength; i++) {
+ ALOGD("umbra i %d, [%f, %f]", i, umbra[i].x, umbra[i].y);
}
+ for (int i = 0; i < newPenumbraIndex; i++) {
+ ALOGD("new penumbra i %d, [%f, %f]", i, newPenumbra[i].x, newPenumbra[i].y);
+ }
+ for (int i = 0; i < verticesPairIndex; i++) {
+ ALOGD("index i %d, [%d, %d]", i, verticesPair[i].outerIndex, verticesPair[i].innerIndex);
+ }
+#endif
- // Minimal value is 1, for each vertex show up once.
- // The bigger this value is , the smoother the look is, but more memory
- // is consumed.
- // When the ray number is high, that means the polygon has been fine
- // tessellated, we don't need this extra slice, just keep it as 1.
- int sliceNumberPerEdge = (totalRayNum > FINE_TESSELLATED_POLYGON_RAY_NUMBER) ? 1 : 2;
-
- // For each polygon, we at most add (totalRayNum * sliceNumberPerEdge) vertices.
- int slicedVertexCountPerPolygon = totalRayNum * sliceNumberPerEdge;
- int totalVertexCount = slicedVertexCountPerPolygon * 2 + totalRayNum;
- int totalIndexCount = 2 * (slicedVertexCountPerPolygon * 2 + 2);
+ // For the size of vertex buffer, we need 3 rings, one has newPenumbraSize,
+ // one has umbraLength, the last one has at most umbraLength.
+ //
+ // For the size of index buffer, the umbra area needs (2 * umbraLength + 2).
+ // The penumbra one can vary a bit, but it is bounded by (2 * verticesPairIndex + 2).
+ // And 2 more for jumping between penumbra to umbra.
+ const int newPenumbraLength = newPenumbraIndex;
+ const int totalVertexCount = newPenumbraLength + umbraLength * 2;
+ const int totalIndexCount = 2 * umbraLength + 2 * verticesPairIndex + 6;
AlphaVertex* shadowVertices =
shadowTriangleStrip.alloc<AlphaVertex>(totalVertexCount);
uint16_t* indexBuffer =
shadowTriangleStrip.allocIndices<uint16_t>(totalIndexCount);
-
- int indexBufferIndex = 0;
int vertexBufferIndex = 0;
+ int indexBufferIndex = 0;
- uint16_t slicedUmbraVertexIndex[totalRayNum * sliceNumberPerEdge];
- // Should be something like 0 0 0 1 1 1 2 3 3 3...
- int rayNumberPerSlicedUmbra[totalRayNum * sliceNumberPerEdge];
- int realUmbraVertexCount = 0;
- for (int i = 0; i < totalRayNum; i++) {
- Vector2 currentPenumbra = penumbraVertices[i];
- Vector2 currentUmbra = umbraVertices[i];
-
- Vector2 nextPenumbra = penumbraVertices[(i + 1) % totalRayNum];
- Vector2 nextUmbra = umbraVertices[(i + 1) % totalRayNum];
- // NextUmbra/Penumbra will be done in the next loop!!
- for (int weight = 0; weight < sliceNumberPerEdge; weight++) {
- const Vector2& slicedPenumbra = (currentPenumbra * (sliceNumberPerEdge - weight)
- + nextPenumbra * weight) / sliceNumberPerEdge;
-
- const Vector2& slicedUmbra = (currentUmbra * (sliceNumberPerEdge - weight)
- + nextUmbra * weight) / sliceNumberPerEdge;
-
- // In the vertex buffer, we fill the Penumbra first, then umbra.
- indexBuffer[indexBufferIndex++] = vertexBufferIndex;
- AlphaVertex::set(&shadowVertices[vertexBufferIndex++], slicedPenumbra.x,
- slicedPenumbra.y, 0.0f);
-
- // When we add umbra vertex, we need to remember its current ray number.
- // And its own vertexBufferIndex. This is for occluded umbra usage.
- indexBuffer[indexBufferIndex++] = vertexBufferIndex;
- rayNumberPerSlicedUmbra[realUmbraVertexCount] = i;
- slicedUmbraVertexIndex[realUmbraVertexCount] = vertexBufferIndex;
- realUmbraVertexCount++;
- AlphaVertex::set(&shadowVertices[vertexBufferIndex++], slicedUmbra.x,
- slicedUmbra.y, M_PI);
- }
+ // Fill the IB and VB for the penumbra area.
+ for (int i = 0; i < newPenumbraLength; i++) {
+ AlphaVertex::set(&shadowVertices[vertexBufferIndex++], newPenumbra[i].x,
+ newPenumbra[i].y, 0.0f);
+ }
+ for (int i = 0; i < umbraLength; i++) {
+ AlphaVertex::set(&shadowVertices[vertexBufferIndex++], umbra[i].x, umbra[i].y,
+ M_PI);
}
- indexBuffer[indexBufferIndex++] = 0;
- //RealUmbraVertexIndex[0] must be 1, so we connect back well at the
- //beginning of occluded area.
- indexBuffer[indexBufferIndex++] = 1;
+ for (int i = 0; i < verticesPairIndex; i++) {
+ indexBuffer[indexBufferIndex++] = verticesPair[i].outerIndex;
+ // All umbra index need to be offseted by newPenumbraSize.
+ indexBuffer[indexBufferIndex++] = verticesPair[i].innerIndex + newPenumbraLength;
+ }
+ indexBuffer[indexBufferIndex++] = verticesPair[0].outerIndex;
+ indexBuffer[indexBufferIndex++] = verticesPair[0].innerIndex + newPenumbraLength;
- float occludedUmbraAlpha = M_PI;
+ // Now fill the IB and VB for the umbra area.
+ // First duplicated the index from previous strip and the first one for the
+ // degenerated triangles.
+ indexBuffer[indexBufferIndex] = indexBuffer[indexBufferIndex - 1];
+ indexBufferIndex++;
+ indexBuffer[indexBufferIndex++] = newPenumbraLength + 0;
+ // Save the first VB index for umbra area in order to close the loop.
+ int savedStartIndex = vertexBufferIndex;
+
if (hasOccludedUmbraArea) {
- // Now the occludedUmbra area;
- int currentRayNumber = -1;
- int firstOccludedUmbraIndex = -1;
- for (int i = 0; i < realUmbraVertexCount; i++) {
- indexBuffer[indexBufferIndex++] = slicedUmbraVertexIndex[i];
+ // Precompute all the polygon's vector, and the reference cross product,
+ // in order to find the right polygon edge for the ray to intersect.
+ Vector2 polyToCentroid[polyLength];
+ bool isPositiveCross = genPolyToCentroid(poly2d, polyLength, centroid, polyToCentroid);
- // If the occludedUmbra vertex has not been added yet, then add it.
- // Otherwise, just use the previously added occludedUmbra vertices.
- if (rayNumberPerSlicedUmbra[i] != currentRayNumber) {
- currentRayNumber++;
- indexBuffer[indexBufferIndex++] = vertexBufferIndex;
- // We need to remember the begining of the occludedUmbra vertices
- // to close this loop.
- if (currentRayNumber == 0) {
- firstOccludedUmbraIndex = vertexBufferIndex;
- }
- AlphaVertex::set(&shadowVertices[vertexBufferIndex++],
- occludedUmbraVertices[currentRayNumber].x,
- occludedUmbraVertices[currentRayNumber].y,
- occludedUmbraAlpha);
- } else {
- indexBuffer[indexBufferIndex++] = (vertexBufferIndex - 1);
- }
+ // Because both the umbra and polygon are going in the same direction,
+ // we can save the previous polygon index to make sure we have less polygon
+ // vertex to compute for each ray.
+ int previousPolyIndex = 0;
+ for (int i = 0; i < umbraLength; i++) {
+ // Shoot a ray from centroid to each umbra vertices and pick the one with
+ // shorter distance to the centroid, b/t the umbra vertex or the intersection point.
+ Vector2 closerVertex = getCloserVertex(umbra[i], centroid, poly2d, polyLength,
+ polyToCentroid, isPositiveCross, previousPolyIndex);
+
+ // We already stored the umbra vertices, just need to add the occlued umbra's ones.
+ indexBuffer[indexBufferIndex++] = newPenumbraLength + i;
+ indexBuffer[indexBufferIndex++] = vertexBufferIndex;
+ AlphaVertex::set(&shadowVertices[vertexBufferIndex++],
+ closerVertex.x, closerVertex.y, M_PI);
}
- // Close the loop here!
- indexBuffer[indexBufferIndex++] = slicedUmbraVertexIndex[0];
- indexBuffer[indexBufferIndex++] = firstOccludedUmbraIndex;
} else {
+ // If there is no occluded umbra at all, then draw the triangle fan
+ // starting from the centroid to all umbra vertices.
int lastCentroidIndex = vertexBufferIndex;
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid.x,
- centroid.y, occludedUmbraAlpha);
- for (int i = 0; i < realUmbraVertexCount; i++) {
- indexBuffer[indexBufferIndex++] = slicedUmbraVertexIndex[i];
+ centroid.y, M_PI);
+ for (int i = 0; i < umbraLength; i++) {
+ indexBuffer[indexBufferIndex++] = newPenumbraLength + i;
indexBuffer[indexBufferIndex++] = lastCentroidIndex;
}
- // Close the loop here!
- indexBuffer[indexBufferIndex++] = slicedUmbraVertexIndex[0];
- indexBuffer[indexBufferIndex++] = lastCentroidIndex;
}
-
-#if DEBUG_SHADOW
- ALOGD("allocated IB %d allocated VB is %d", totalIndexCount, totalVertexCount);
- ALOGD("IB index %d VB index is %d", indexBufferIndex, vertexBufferIndex);
- for (int i = 0; i < vertexBufferIndex; i++) {
- ALOGD("vertexBuffer i %d, (%f, %f %f)", i, shadowVertices[i].x, shadowVertices[i].y,
- shadowVertices[i].alpha);
- }
- for (int i = 0; i < indexBufferIndex; i++) {
- ALOGD("indexBuffer i %d, indexBuffer[i] %d", i, indexBuffer[i]);
- }
-#endif
+ // Closing the umbra area triangle's loop here.
+ indexBuffer[indexBufferIndex++] = newPenumbraLength;
+ indexBuffer[indexBufferIndex++] = savedStartIndex;
// At the end, update the real index and vertex buffer size.
shadowTriangleStrip.updateVertexCount(vertexBufferIndex);
diff --git a/libs/hwui/SpotShadow.h b/libs/hwui/SpotShadow.h
index 6fa2028f..e2d94f7 100644
--- a/libs/hwui/SpotShadow.h
+++ b/libs/hwui/SpotShadow.h
@@ -36,40 +36,6 @@
static float projectCasterToOutline(Vector2& outline,
const Vector3& lightCenter, const Vector3& polyVertex);
- static int setupAngleList(VertexAngleData* angleDataList,
- int polyLength, const Vector2* polygon, const Vector2& centroid,
- bool isPenumbra, const char* name);
-
- static int convertPolysToVerticesPerRay(
- bool hasOccludedUmbraArea, const Vector2* poly2d, int polyLength,
- const Vector2* umbra, int umbraLength, const Vector2* penumbra,
- int penumbraLength, const Vector2& centroid,
- Vector2* umbraVerticesPerRay, Vector2* penumbraVerticesPerRay,
- Vector2* occludedUmbraVerticesPerRay);
-
- static bool checkClockwise(int maxIndex, int listLength,
- VertexAngleData* angleList, const char* name);
-
- static void calculateDistanceCounter(bool needsOffsetToUmbra, int angleLength,
- const VertexAngleData* allVerticesAngleData, int* distances);
-
- static void mergeAngleList(int maxUmbraAngleIndex, int maxPenumbraAngleIndex,
- const VertexAngleData* umbraAngleList, int umbraLength,
- const VertexAngleData* penumbraAngleList, int penumbraLength,
- VertexAngleData* allVerticesAngleData);
-
- static int setupPolyAngleList(float* polyAngleList, int polyAngleLength,
- const Vector2* poly2d, const Vector2& centroid);
-
- static bool checkPolyClockwise(int polyAngleLength, int maxPolyAngleIndex,
- const float* polyAngleList);
-
- static int getEdgeStartIndex(const int* offsets, int rayIndex, int totalRayNumber,
- const VertexAngleData* allVerticesAngleData);
-
- static int getPolyEdgeStartIndex(int maxPolyAngleIndex, int polyLength,
- const float* polyAngleList, float rayAngle);
-
static void computeLightPolygon(int points, const Vector3& lightCenter,
float size, Vector3* ret);
diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h
index d033ed9..aa6acc9 100644
--- a/libs/hwui/Vector.h
+++ b/libs/hwui/Vector.h
@@ -99,6 +99,10 @@
return x * v.x + y * v.y;
}
+ float cross(const Vector2& v) const {
+ return x * v.y - y * v.x;
+ }
+
void dump() {
ALOGD("Vector2[%.2f, %.2f]", x, y);
}
diff --git a/location/java/android/location/GpsClock.java b/location/java/android/location/GpsClock.java
index 963b604..610d268 100644
--- a/location/java/android/location/GpsClock.java
+++ b/location/java/android/location/GpsClock.java
@@ -492,6 +492,7 @@
setType(TYPE_UNKNOWN);
setTimeInNs(Long.MIN_VALUE);
resetTimeUncertaintyInNs();
+ resetFullBiasInNs();
resetBiasInNs();
resetBiasUncertaintyInNs();
resetDriftInNsPerSec();
diff --git a/location/java/android/location/GpsMeasurementListenerTransport.java b/location/java/android/location/GpsMeasurementListenerTransport.java
index 2d9a372..610da96 100644
--- a/location/java/android/location/GpsMeasurementListenerTransport.java
+++ b/location/java/android/location/GpsMeasurementListenerTransport.java
@@ -26,14 +26,12 @@
*/
class GpsMeasurementListenerTransport
extends LocalListenerHelper<GpsMeasurementsEvent.Listener> {
- private final Context mContext;
private final ILocationManager mLocationManager;
private final IGpsMeasurementsListener mListenerTransport = new ListenerTransport();
public GpsMeasurementListenerTransport(Context context, ILocationManager locationManager) {
- super("GpsMeasurementListenerTransport");
- mContext = context;
+ super(context, "GpsMeasurementListenerTransport");
mLocationManager = locationManager;
}
@@ -41,7 +39,7 @@
protected boolean registerWithServer() throws RemoteException {
return mLocationManager.addGpsMeasurementsListener(
mListenerTransport,
- mContext.getPackageName());
+ getContext().getPackageName());
}
@Override
@@ -59,7 +57,18 @@
listener.onGpsMeasurementsReceived(event);
}
};
+ foreach(operation);
+ }
+ @Override
+ public void onStatusChanged(final int status) {
+ ListenerOperation<GpsMeasurementsEvent.Listener> operation =
+ new ListenerOperation<GpsMeasurementsEvent.Listener>() {
+ @Override
+ public void execute(GpsMeasurementsEvent.Listener listener) throws RemoteException {
+ listener.onStatusChanged(status);
+ }
+ };
foreach(operation);
}
}
diff --git a/location/java/android/location/GpsMeasurementsEvent.java b/location/java/android/location/GpsMeasurementsEvent.java
index e04ed81..94ca920 100644
--- a/location/java/android/location/GpsMeasurementsEvent.java
+++ b/location/java/android/location/GpsMeasurementsEvent.java
@@ -32,6 +32,24 @@
* @hide
*/
public class GpsMeasurementsEvent implements Parcelable {
+
+ /**
+ * The system does not support tracking of GPS Measurements. This status will not change in the
+ * future.
+ */
+ public static final int STATUS_NOT_SUPPORTED = 0;
+
+ /**
+ * GPS Measurements are successfully being tracked, it will receive updates once they are
+ * available.
+ */
+ public static final int STATUS_READY = 1;
+
+ /**
+ * GPS provider or Location is disabled, updates will not be received until they are enabled.
+ */
+ public static final int STATUS_GPS_LOCATION_DISABLED = 2;
+
private final GpsClock mClock;
private final Collection<GpsMeasurement> mReadOnlyMeasurements;
@@ -43,7 +61,16 @@
* @hide
*/
public interface Listener {
+
+ /**
+ * Returns the latest collected GPS Measurements.
+ */
void onGpsMeasurementsReceived(GpsMeasurementsEvent eventArgs);
+
+ /**
+ * Returns the latest status of the GPS Measurements sub-system.
+ */
+ void onStatusChanged(int status);
}
public GpsMeasurementsEvent(GpsClock clock, GpsMeasurement[] measurements) {
@@ -103,7 +130,9 @@
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeParcelable(mClock, flags);
- GpsMeasurement[] measurementsArray = mReadOnlyMeasurements.toArray(new GpsMeasurement[0]);
+ int measurementsCount = mReadOnlyMeasurements.size();
+ GpsMeasurement[] measurementsArray =
+ mReadOnlyMeasurements.toArray(new GpsMeasurement[measurementsCount]);
parcel.writeInt(measurementsArray.length);
parcel.writeTypedArray(measurementsArray, flags);
}
diff --git a/location/java/android/location/GpsNavigationMessageEvent.java b/location/java/android/location/GpsNavigationMessageEvent.java
index 50ffa75..b61dac0 100644
--- a/location/java/android/location/GpsNavigationMessageEvent.java
+++ b/location/java/android/location/GpsNavigationMessageEvent.java
@@ -21,9 +21,6 @@
import android.os.Parcelable;
import java.security.InvalidParameterException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
/**
* A class implementing a container for data associated with a navigation message event.
@@ -32,6 +29,24 @@
* @hide
*/
public class GpsNavigationMessageEvent implements Parcelable {
+
+ /**
+ * The system does not support tracking of GPS Navigation Messages. This status will not change
+ * in the future.
+ */
+ public static int STATUS_NOT_SUPPORTED = 0;
+
+ /**
+ * GPS Navigation Messages are successfully being tracked, it will receive updates once they are
+ * available.
+ */
+ public static int STATUS_READY = 1;
+
+ /**
+ * GPS provider or Location is disabled, updated will not be received until they are enabled.
+ */
+ public static int STATUS_GPS_LOCATION_DISABLED = 2;
+
private final GpsNavigationMessage mNavigationMessage;
/**
@@ -42,7 +57,16 @@
* @hide
*/
public interface Listener {
+
+ /**
+ * Returns the latest collected GPS Navigation Message.
+ */
void onGpsNavigationMessageReceived(GpsNavigationMessageEvent event);
+
+ /**
+ * Returns the latest status of the GPS Navigation Messages sub-system.
+ */
+ void onStatusChanged(int status);
}
public GpsNavigationMessageEvent(GpsNavigationMessage message) {
diff --git a/location/java/android/location/GpsNavigationMessageListenerTransport.java b/location/java/android/location/GpsNavigationMessageListenerTransport.java
index ec4812b..f6ba407 100644
--- a/location/java/android/location/GpsNavigationMessageListenerTransport.java
+++ b/location/java/android/location/GpsNavigationMessageListenerTransport.java
@@ -26,7 +26,6 @@
*/
class GpsNavigationMessageListenerTransport
extends LocalListenerHelper<GpsNavigationMessageEvent.Listener> {
- private final Context mContext;
private final ILocationManager mLocationManager;
private final IGpsNavigationMessageListener mListenerTransport = new ListenerTransport();
@@ -34,8 +33,7 @@
public GpsNavigationMessageListenerTransport(
Context context,
ILocationManager locationManager) {
- super("GpsNavigationMessageListenerTransport");
- mContext = context;
+ super(context, "GpsNavigationMessageListenerTransport");
mLocationManager = locationManager;
}
@@ -43,7 +41,7 @@
protected boolean registerWithServer() throws RemoteException {
return mLocationManager.addGpsNavigationMessageListener(
mListenerTransport,
- mContext.getPackageName());
+ getContext().getPackageName());
}
@Override
@@ -62,7 +60,19 @@
listener.onGpsNavigationMessageReceived(event);
}
};
+ foreach(operation);
+ }
+ @Override
+ public void onStatusChanged(final int status) {
+ ListenerOperation<GpsNavigationMessageEvent.Listener> operation =
+ new ListenerOperation<GpsNavigationMessageEvent.Listener>() {
+ @Override
+ public void execute(GpsNavigationMessageEvent.Listener listener)
+ throws RemoteException {
+ listener.onStatusChanged(status);
+ }
+ };
foreach(operation);
}
}
diff --git a/location/java/android/location/IGpsMeasurementsListener.aidl b/location/java/android/location/IGpsMeasurementsListener.aidl
index b34bb6c..cbd3100 100644
--- a/location/java/android/location/IGpsMeasurementsListener.aidl
+++ b/location/java/android/location/IGpsMeasurementsListener.aidl
@@ -23,4 +23,5 @@
*/
oneway interface IGpsMeasurementsListener {
void onGpsMeasurementsReceived(in GpsMeasurementsEvent event);
+ void onStatusChanged(in int status);
}
diff --git a/location/java/android/location/IGpsNavigationMessageListener.aidl b/location/java/android/location/IGpsNavigationMessageListener.aidl
index 18603fe..a708ea6 100644
--- a/location/java/android/location/IGpsNavigationMessageListener.aidl
+++ b/location/java/android/location/IGpsNavigationMessageListener.aidl
@@ -23,4 +23,5 @@
*/
oneway interface IGpsNavigationMessageListener {
void onGpsNavigationMessageReceived(in GpsNavigationMessageEvent event);
+ void onStatusChanged(in int status);
}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 1501710..af76175 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -62,12 +62,12 @@
boolean sendNiResponse(int notifId, int userResponse);
boolean addGpsMeasurementsListener(in IGpsMeasurementsListener listener, in String packageName);
- boolean removeGpsMeasurementsListener(in IGpsMeasurementsListener listener);
+ void removeGpsMeasurementsListener(in IGpsMeasurementsListener listener);
boolean addGpsNavigationMessageListener(
in IGpsNavigationMessageListener listener,
in String packageName);
- boolean removeGpsNavigationMessageListener(in IGpsNavigationMessageListener listener);
+ void removeGpsNavigationMessageListener(in IGpsNavigationMessageListener listener);
// --- deprecated ---
List<String> getAllProviders();
diff --git a/location/java/android/location/LocalListenerHelper.java b/location/java/android/location/LocalListenerHelper.java
index 1f3bf67..458c451 100644
--- a/location/java/android/location/LocalListenerHelper.java
+++ b/location/java/android/location/LocalListenerHelper.java
@@ -19,6 +19,7 @@
import com.android.internal.util.Preconditions;
import android.annotation.NonNull;
+import android.content.Context;
import android.os.RemoteException;
import android.util.Log;
@@ -32,17 +33,19 @@
* @hide
*/
abstract class LocalListenerHelper<TListener> {
- private final HashSet<TListener> mListeners = new HashSet<TListener>();
- private final String mTag;
+ private final HashSet<TListener> mListeners = new HashSet<>();
- protected LocalListenerHelper(String name) {
+ private final String mTag;
+ private final Context mContext;
+
+ protected LocalListenerHelper(Context context, String name) {
Preconditions.checkNotNull(name);
+ mContext = context;
mTag = name;
}
public boolean add(@NonNull TListener listener) {
Preconditions.checkNotNull(listener);
-
synchronized (mListeners) {
// we need to register with the service first, because we need to find out if the
// service will actually support the request before we attempt anything
@@ -59,18 +62,15 @@
return false;
}
}
-
if (mListeners.contains(listener)) {
return true;
}
- mListeners.add(listener);
+ return mListeners.add(listener);
}
- return true;
}
public void remove(@NonNull TListener listener) {
Preconditions.checkNotNull(listener);
-
synchronized (mListeners) {
boolean removed = mListeners.remove(listener);
boolean isLastRemoved = removed && mListeners.isEmpty();
@@ -78,7 +78,7 @@
try {
unregisterFromServer();
} catch (RemoteException e) {
-
+ Log.v(mTag, "Error handling last listener removal", e);
}
}
}
@@ -91,12 +91,15 @@
void execute(TListener listener) throws RemoteException;
}
- protected void foreach(ListenerOperation operation) {
+ protected Context getContext() {
+ return mContext;
+ }
+
+ protected void foreach(ListenerOperation<TListener> operation) {
Collection<TListener> listeners;
synchronized (mListeners) {
- listeners = new ArrayList<TListener>(mListeners);
+ listeners = new ArrayList<>(mListeners);
}
-
for (TListener listener : listeners) {
try {
operation.execute(listener);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index ed408e0..513a627 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1579,7 +1579,7 @@
* Adds a GPS Measurement listener.
*
* @param listener a {@link GpsMeasurementsEvent.Listener} object to register.
- * @return {@code true} if the listener was successfully registered, {@code false} otherwise.
+ * @return {@code true} if the listener was added successfully, {@code false} otherwise.
*
* @hide
*/
@@ -1602,7 +1602,7 @@
* Adds a GPS Navigation Message listener.
*
* @param listener a {@link GpsNavigationMessageEvent.Listener} object to register.
- * @return {@code true} if the listener was successfully registered, {@code false} otherwise.
+ * @return {@code true} if the listener was added successfully, {@code false} otherwise.
*
* @hide
*/
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 1062880..29d4930 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -533,6 +533,8 @@
private AudioOrientationEventListener mOrientationListener;
+ private static Long mLastDeviceConnectMsgTime = new Long(0);
+
///////////////////////////////////////////////////////////////////////////
// Construction
///////////////////////////////////////////////////////////////////////////
@@ -3269,8 +3271,15 @@
} else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
return;
}
-
- handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
+ synchronized (mLastDeviceConnectMsgTime) {
+ long time = SystemClock.uptimeMillis() + delay;
+ handler.sendMessageAtTime(handler.obtainMessage(msg, arg1, arg2, obj), time);
+ if (msg == MSG_SET_WIRED_DEVICE_CONNECTION_STATE ||
+ msg == MSG_SET_A2DP_SRC_CONNECTION_STATE ||
+ msg == MSG_SET_A2DP_SINK_CONNECTION_STATE) {
+ mLastDeviceConnectMsgTime = time;
+ }
+ }
}
boolean checkAudioSettingsPermission(String method) {
@@ -4587,7 +4596,12 @@
if (mAudioHandler.hasMessages(MSG_SET_A2DP_SRC_CONNECTION_STATE) ||
mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE) ||
mAudioHandler.hasMessages(MSG_SET_WIRED_DEVICE_CONNECTION_STATE)) {
- delay = 1000;
+ synchronized (mLastDeviceConnectMsgTime) {
+ long time = SystemClock.uptimeMillis();
+ if (mLastDeviceConnectMsgTime > time) {
+ delay = (int)(mLastDeviceConnectMsgTime - time);
+ }
+ }
}
return delay;
}
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 3b8cb19..7959841 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -572,6 +572,14 @@
*/
public native List<byte[]> getSecureStops();
+ /**
+ * Access secure stop by secure stop ID.
+ *
+ * @param ssid - The secure stop ID provided by the license server.
+ *
+ * @hide - not part of the public API at this time
+ */
+ public native byte[] getSecureStop(byte[] ssid);
/**
* Process the SecureStop server response message ssRelease. After authenticating
@@ -581,6 +589,12 @@
*/
public native void releaseSecureStops(byte[] ssRelease);
+ /**
+ * Remove all secure stops without requiring interaction with the server.
+ *
+ * @hide - not part of the public API at this time
+ */
+ public native void releaseAllSecureStops();
/**
* String property name: identifies the maker of the DRM engine plugin
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index b4e6033..924078c 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -122,6 +122,10 @@
/**
* The artwork for the media as a {@link Bitmap}.
+ * <p>
+ * The artwork should be relatively small and may be scaled down by the
+ * system if it is too large. For higher resolution artwork
+ * {@link #METADATA_KEY_ART_URI} should be used instead.
*/
public static final String METADATA_KEY_ART = "android.media.metadata.ART";
@@ -129,12 +133,20 @@
* The artwork for the media as a Uri formatted String. The artwork can be
* loaded using a combination of {@link ContentResolver#openInputStream} and
* {@link BitmapFactory#decodeStream}.
+ * <p>
+ * For the best results, Uris should use the content:// style and support
+ * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork through
+ * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)}.
*/
public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
/**
* The artwork for the album of the media's original source as a
* {@link Bitmap}.
+ * <p>
+ * The artwork should be relatively small and may be scaled down by the
+ * system if it is too large. For higher resolution artwork
+ * {@link #METADATA_KEY_ALBUM_ART_URI} should be used instead.
*/
public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
@@ -143,6 +155,10 @@
* formatted String. The artwork can be loaded using a combination of
* {@link ContentResolver#openInputStream} and
* {@link BitmapFactory#decodeStream}.
+ * <p>
+ * For the best results, Uris should use the content:// style and support
+ * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork through
+ * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)}.
*/
public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
@@ -188,6 +204,10 @@
* An icon or thumbnail that is suitable for display to the user. When
* displaying an icon for media described by this metadata this should be
* preferred to other fields if present. This must be a {@link Bitmap}.
+ * <p>
+ * The icon should be relatively small and may be scaled down by the system
+ * if it is too large. For higher resolution artwork
+ * {@link #METADATA_KEY_DISPLAY_ICON_URI} should be used instead.
*/
public static final String METADATA_KEY_DISPLAY_ICON
= "android.media.metadata.DISPLAY_ICON";
@@ -199,6 +219,10 @@
* fields when present. The icon can be loaded using a combination of
* {@link ContentResolver#openInputStream} and
* {@link BitmapFactory#decodeStream}.
+ * <p>
+ * For the best results, Uris should use the content:// style and support
+ * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork through
+ * {@link ContentResolver#openTypedAssetFileDescriptor(Uri, String, Bundle)}.
*/
public static final String METADATA_KEY_DISPLAY_ICON_URI
= "android.media.metadata.DISPLAY_ICON_URI";
@@ -602,6 +626,11 @@
* <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li>
* <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li>
* </ul>
+ * <p>
+ * Uris for artwork should use the content:// style and support
+ * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork
+ * through {@link ContentResolver#openTypedAssetFileDescriptor(Uri,
+ * String, Bundle)}.
*
* @param key The key for referencing this value
* @param value The String value to store
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 9fa3f50..a8b9686e 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -483,6 +483,10 @@
*/
public static boolean isEffectTypeAvailable(UUID type) {
AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
+ if (desc == null) {
+ return false;
+ }
+
for (int i = 0; i < desc.length; i++) {
if (desc[i].type.equals(type)) {
return true;
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index e490c2b..dd6bd20 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -449,6 +449,7 @@
}
MessageHandler holder = new MessageHandler(handler.getLooper(), cb);
mCallbacks.add(holder);
+ holder.mRegistered = true;
if (!mCbRegistered) {
try {
@@ -467,6 +468,7 @@
if (cb == handler.mCallback) {
mCallbacks.remove(i);
success = true;
+ handler.mRegistered = false;
}
}
if (mCbRegistered && mCallbacks.size() == 0) {
@@ -956,6 +958,7 @@
private final static class MessageHandler extends Handler {
private final MediaController.Callback mCallback;
+ private boolean mRegistered = false;
public MessageHandler(Looper looper, MediaController.Callback cb) {
super(looper, null, true);
@@ -964,6 +967,9 @@
@Override
public void handleMessage(Message msg) {
+ if (!mRegistered) {
+ return;
+ }
switch (msg.what) {
case MSG_EVENT:
mCallback.onSessionEvent((String) msg.obj, msg.getData());
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 86da80a..973527f 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -286,7 +286,9 @@
if (volumeProvider == null) {
throw new IllegalArgumentException("volumeProvider may not be null!");
}
- mVolumeProvider = volumeProvider;
+ synchronized (mLock) {
+ mVolumeProvider = volumeProvider;
+ }
volumeProvider.setCallback(new VolumeProvider.Callback() {
@Override
public void onVolumeChanged(VolumeProvider volumeProvider) {
@@ -449,6 +451,27 @@
}
/**
+ * Set the style of rating used by this session. Apps trying to set the
+ * rating should use this style. Must be one of the following:
+ * <ul>
+ * <li>{@link Rating#RATING_NONE}</li>
+ * <li>{@link Rating#RATING_3_STARS}</li>
+ * <li>{@link Rating#RATING_4_STARS}</li>
+ * <li>{@link Rating#RATING_5_STARS}</li>
+ * <li>{@link Rating#RATING_HEART}</li>
+ * <li>{@link Rating#RATING_PERCENTAGE}</li>
+ * <li>{@link Rating#RATING_THUMB_UP_DOWN}</li>
+ * </ul>
+ */
+ public void setRatingType(int type) {
+ try {
+ mBinder.setRatingType(type);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in setRatingType.", e);
+ }
+ }
+
+ /**
* Set some extras that can be associated with the {@link MediaSession}. No assumptions should
* be made as to how a {@link MediaController} will handle these extras.
* Keys should be fully qualified (e.g. com.example.MY_EXTRA) to avoid conflicts.
@@ -470,9 +493,11 @@
* @hide
*/
public void notifyRemoteVolumeChanged(VolumeProvider provider) {
- if (provider == null || provider != mVolumeProvider) {
- Log.w(TAG, "Received update from stale volume provider");
- return;
+ synchronized (mLock) {
+ if (provider == null || provider != mVolumeProvider) {
+ Log.w(TAG, "Received update from stale volume provider");
+ return;
+ }
}
try {
mBinder.setCurrentVolume(provider.getCurrentVolume());
@@ -537,6 +562,14 @@
postToCallback(CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent);
}
+ private void dispatchAdjustVolume(int direction) {
+ postToCallback(CallbackMessageHandler.MSG_ADJUST_VOLUME, direction);
+ }
+
+ private void dispatchSetVolumeTo(int volume) {
+ postToCallback(CallbackMessageHandler.MSG_SET_VOLUME, volume);
+ }
+
private void postToCallback(int what) {
postToCallback(what, null);
}
@@ -988,9 +1021,7 @@
public void onAdjustVolume(int direction) {
MediaSession session = mMediaSession.get();
if (session != null) {
- if (session.mVolumeProvider != null) {
- session.mVolumeProvider.onAdjustVolume(direction);
- }
+ session.dispatchAdjustVolume(direction);
}
}
@@ -998,9 +1029,7 @@
public void onSetVolumeTo(int value) {
MediaSession session = mMediaSession.get();
if (session != null) {
- if (session.mVolumeProvider != null) {
- session.mVolumeProvider.onSetVolumeTo(value);
- }
+ session.dispatchSetVolumeTo(value);
}
}
@@ -1117,6 +1146,8 @@
private static final int MSG_CUSTOM_ACTION = 13;
private static final int MSG_MEDIA_BUTTON = 14;
private static final int MSG_COMMAND = 15;
+ private static final int MSG_ADJUST_VOLUME = 16;
+ private static final int MSG_SET_VOLUME = 17;
private MediaSession.Callback mCallback;
@@ -1145,6 +1176,7 @@
@Override
public void handleMessage(Message msg) {
+ VolumeProvider vp;
switch (msg.what) {
case MSG_PLAY:
mCallback.onPlay();
@@ -1192,6 +1224,22 @@
Command cmd = (Command) msg.obj;
mCallback.onCommand(cmd.command, cmd.extras, cmd.stub);
break;
+ case MSG_ADJUST_VOLUME:
+ synchronized (mLock) {
+ vp = mVolumeProvider;
+ }
+ if (vp != null) {
+ vp.onAdjustVolume((int) msg.obj);
+ }
+ break;
+ case MSG_SET_VOLUME:
+ synchronized (mLock) {
+ vp = mVolumeProvider;
+ }
+ if (vp != null) {
+ vp.onSetVolumeTo((int) msg.obj);
+ }
+ break;
}
}
}
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 267d1ff..54d0acd 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -16,6 +16,7 @@
package android.media.session;
import android.annotation.DrawableRes;
+import android.annotation.Nullable;
import android.media.RemoteControlClient;
import android.os.Bundle;
import android.os.Parcel;
@@ -232,11 +233,12 @@
private final CharSequence mErrorMessage;
private final long mUpdateTime;
private final long mActiveItemId;
+ private final Bundle mExtras;
private PlaybackState(int state, long position, long updateTime, float speed,
long bufferedPosition, long transportControls,
List<PlaybackState.CustomAction> customActions, long activeItemId,
- CharSequence error) {
+ CharSequence error, Bundle extras) {
mState = state;
mPosition = position;
mSpeed = speed;
@@ -246,6 +248,7 @@
mCustomActions = new ArrayList<>(customActions);
mActiveItemId = activeItemId;
mErrorMessage = error;
+ mExtras = extras;
}
private PlaybackState(Parcel in) {
@@ -258,7 +261,7 @@
mCustomActions = in.createTypedArrayList(CustomAction.CREATOR);
mActiveItemId = in.readLong();
mErrorMessage = in.readCharSequence();
-
+ mExtras = in.readBundle();
}
@Override
@@ -293,6 +296,7 @@
dest.writeTypedList(mCustomActions);
dest.writeLong(mActiveItemId);
dest.writeCharSequence(mErrorMessage);
+ dest.writeBundle(mExtras);
}
/**
@@ -306,6 +310,7 @@
* <li> {@link PlaybackState#STATE_REWINDING}</li>
* <li> {@link PlaybackState#STATE_BUFFERING}</li>
* <li> {@link PlaybackState#STATE_ERROR}</li>
+ * </ul>
*/
public int getState() {
return mState;
@@ -394,6 +399,15 @@
}
/**
+ * Get any custom extras that were set on this playback state.
+ *
+ * @return The extras for this state or null.
+ */
+ public @Nullable Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
* Get the {@link PlaybackState} state for the given
* {@link RemoteControlClient} state.
*
@@ -737,6 +751,7 @@
private CharSequence mErrorMessage;
private long mUpdateTime;
private long mActiveItemId = MediaSession.QueueItem.UNKNOWN_ID;
+ private Bundle mExtras;
/**
* Creates an initially empty state builder.
@@ -765,6 +780,7 @@
mErrorMessage = from.mErrorMessage;
mUpdateTime = from.mUpdateTime;
mActiveItemId = from.mActiveItemId;
+ mExtras = from.mExtras;
}
/**
@@ -947,13 +963,25 @@
}
/**
- * Build and return the {@link PlaybackState} instance with these values.
+ * Set any custom extras to be included with the playback state.
+ *
+ * @param extras The extras to include.
+ * @return this
+ */
+ public Builder setExtras(Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
+ * Build and return the {@link PlaybackState} instance with these
+ * values.
*
* @return A new state instance.
*/
public PlaybackState build() {
return new PlaybackState(mState, mPosition, mUpdateTime, mSpeed, mBufferedPosition,
- mActions, mCustomActions, mActiveItemId, mErrorMessage);
+ mActions, mCustomActions, mActiveItemId, mErrorMessage, mExtras);
}
}
}
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index d50be42..0754fd4 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -101,7 +101,6 @@
* be thrown.
*
* @see MediaBrowserService#onLoadChildren
- * @see MediaBrowserService#onLoadIcon
*/
public class Result<T> {
private Object mDebug;
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 0fed27e..8e07ec0 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -1003,6 +1003,27 @@
return ListOfVectorsToArrayListOfByteArray(env, secureStops);
}
+static jbyteArray android_media_MediaDrm_getSecureStop(
+ JNIEnv *env, jobject thiz, jbyteArray ssid) {
+ sp<IDrm> drm = GetDrm(env, thiz);
+
+ if (drm == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "MediaDrm obj is null");
+ return NULL;
+ }
+
+ Vector<uint8_t> secureStop;
+
+ status_t err = drm->getSecureStop(JByteArrayToVector(env, ssid), secureStop);
+
+ if (throwExceptionAsNecessary(env, err, "Failed to get secure stop")) {
+ return NULL;
+ }
+
+ return VectorToJByteArray(env, secureStop);
+}
+
static void android_media_MediaDrm_releaseSecureStops(
JNIEnv *env, jobject thiz, jbyteArray jssRelease) {
sp<IDrm> drm = GetDrm(env, thiz);
@@ -1020,6 +1041,21 @@
throwExceptionAsNecessary(env, err, "Failed to release secure stops");
}
+static void android_media_MediaDrm_releaseAllSecureStops(
+ JNIEnv *env, jobject thiz) {
+ sp<IDrm> drm = GetDrm(env, thiz);
+
+ if (drm == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "MediaDrm obj is null");
+ return;
+ }
+
+ status_t err = drm->releaseAllSecureStops();
+
+ throwExceptionAsNecessary(env, err, "Failed to release all secure stops");
+}
+
static jstring android_media_MediaDrm_getPropertyString(
JNIEnv *env, jobject thiz, jstring jname) {
sp<IDrm> drm = GetDrm(env, thiz);
@@ -1384,9 +1420,15 @@
{ "getSecureStops", "()Ljava/util/List;",
(void *)android_media_MediaDrm_getSecureStops },
+ { "getSecureStop", "([B)[B",
+ (void *)android_media_MediaDrm_getSecureStop },
+
{ "releaseSecureStops", "([B)V",
(void *)android_media_MediaDrm_releaseSecureStops },
+ { "releaseAllSecureStops", "()V",
+ (void *)android_media_MediaDrm_releaseAllSecureStops },
+
{ "getPropertyString", "(Ljava/lang/String;)Ljava/lang/String;",
(void *)android_media_MediaDrm_getPropertyString },
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index c9cefbd..c364d469 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -803,28 +803,12 @@
android_media_AudioEffect_native_queryPreProcessings(JNIEnv *env, jclass clazz __unused,
jint audioSession)
{
- // kDefaultNumEffects is a "reasonable" value ensuring that only one query will be enough on
- // most devices to get all active audio pre processing on a given session.
- static const uint32_t kDefaultNumEffects = 5;
-
- effect_descriptor_t *descriptors = new effect_descriptor_t[kDefaultNumEffects];
- uint32_t numEffects = kDefaultNumEffects;
+ effect_descriptor_t *descriptors = new effect_descriptor_t[AudioEffect::kMaxPreProcessing];
+ uint32_t numEffects = AudioEffect::kMaxPreProcessing;
status_t status = AudioEffect::queryDefaultPreProcessing(audioSession,
descriptors,
&numEffects);
- if ((status != NO_ERROR && status != NO_MEMORY) ||
- numEffects == 0) {
- delete[] descriptors;
- return NULL;
- }
- if (status == NO_MEMORY) {
- delete [] descriptors;
- descriptors = new effect_descriptor_t[numEffects];
- status = AudioEffect::queryDefaultPreProcessing(audioSession,
- descriptors,
- &numEffects);
- }
if (status != NO_ERROR || numEffects == 0) {
delete[] descriptors;
return NULL;
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 1c6dc2c..aa5819e 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -518,7 +518,7 @@
* Determine whether the device is plugged in (USB, power, or wireless).
* @return true if the device is plugged in.
*/
- boolean isPluggedIn() {
+ public boolean isPluggedIn() {
return plugged == BatteryManager.BATTERY_PLUGGED_AC
|| plugged == BatteryManager.BATTERY_PLUGGED_USB
|| plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS;
diff --git a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
index c650a0f..09c7165 100644
--- a/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
+++ b/packages/Keyguard/test/SampleTrustAgent/src/com/android/trustagent/test/SampleTrustAgent.java
@@ -21,7 +21,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
-import android.os.Bundle;
+import android.os.PersistableBundle;
import android.preference.PreferenceManager;
import android.service.trust.TrustAgentService;
import android.support.v4.content.LocalBroadcastManager;
@@ -90,8 +90,15 @@
}
@Override
- public boolean onSetTrustAgentFeaturesEnabled(Bundle options) {
- Log.v(TAG, "Policy options received: " + options.getStringArrayList(KEY_FEATURES));
+ public boolean onConfigure(Configuration config) {
+ if (config != null && config.options != null) {
+ for (int i = 0; i < config.options.size(); i++) {
+ PersistableBundle options = config.options.get(i);
+ Log.v(TAG, "Policy options received: " + options.toString());
+ }
+ } else {
+ Log.w(TAG, "onConfigure() called with no options");
+ }
// TODO: Handle options
return true; // inform DPM that we support it
}
diff --git a/packages/SystemUI/res/anim/ic_invert_colors_disable_animation_cross_1.xml b/packages/SystemUI/res/anim/ic_invert_colors_disable_animation_cross_1.xml
new file mode 100644
index 0000000..a49ebf8
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_invert_colors_disable_animation_cross_1.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="pathData"
+ android:valueFrom="M 7.54049682617,3.9430847168 c 0.0,0.0 0.324981689453,0.399978637695 0.324981689453,0.399978637695 "
+ android:valueTo="M 7.54049682617,3.9430847168 c 0.0,0.0 0.324981689453,0.399978637695 0.324981689453,0.399978637695 "
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/linear" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:valueFrom="M 7.54049682617,3.9430847168 c 0.0,0.0 0.324981689453,0.399978637695 0.324981689453,0.399978637695 "
+ android:valueTo="M 7.54049682617,3.9430847168 c 0.0,0.0 31.5749816895,31.4499664307 31.5749816895,31.4499664307 "
+ android:valueType="pathType"
+ android:interpolator="@interpolator/ic_invert_colors_disable_cross_1_pathdata_interpolator" />
+ </set>
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="strokeAlpha"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:interpolator="@android:interpolator/linear" />
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="strokeAlpha"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:interpolator="@android:interpolator/linear" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_invert_colors_disable_animation_icon.xml b/packages/SystemUI/res/anim/ic_invert_colors_disable_animation_icon.xml
new file mode 100644
index 0000000..9cc3b8e
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_invert_colors_disable_animation_icon.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="alpha"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="alpha"
+ android:valueFrom="1"
+ android:valueTo="0.5"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_invert_colors_disable_animation_mask.xml b/packages/SystemUI/res/anim/ic_invert_colors_disable_animation_mask.xml
new file mode 100644
index 0000000..605ef90
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_invert_colors_disable_animation_mask.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="pathData"
+ android:valueFrom="M 37.8337860107,-40.4599914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 9.55097961426,9.55285644531 9.55097961426,9.55285644531 c 0.0,0.0 -2.61698913574,2.09387207031 -2.61698913574,2.09387207031 c 0.0,0.0 -9.75096130371,-9.56428527832 -9.75096130371,-9.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueTo="M 37.8337860107,-40.4599914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 9.55097961426,9.55285644531 9.55097961426,9.55285644531 c 0.0,0.0 -2.61698913574,2.09387207031 -2.61698913574,2.09387207031 c 0.0,0.0 -9.75096130371,-9.56428527832 -9.75096130371,-9.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:valueFrom="M 37.8337860107,-40.4599914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 9.55097961426,9.55285644531 9.55097961426,9.55285644531 c 0.0,0.0 -2.61698913574,2.09387207031 -2.61698913574,2.09387207031 c 0.0,0.0 -9.75096130371,-9.56428527832 -9.75096130371,-9.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueTo="M 37.8337860107,-40.3974914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 40.9884796143,40.9278411865 40.9884796143,40.9278411865 c 0.0,0.0 -2.61700439453,2.0938873291 -2.61700439453,2.0938873291 c 0.0,0.0 -41.1884460449,-40.9392852783 -41.1884460449,-40.9392852783 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_invert_colors_disable_animation_root.xml b/packages/SystemUI/res/anim/ic_invert_colors_disable_animation_root.xml
new file mode 100644
index 0000000..770c401
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_invert_colors_disable_animation_root.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="alpha"
+ android:valueFrom="1.0"
+ android:valueTo="0.3"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/ic_invert_colors_enable_animation_cross_1.xml b/packages/SystemUI/res/anim/ic_invert_colors_enable_animation_cross_1.xml
new file mode 100644
index 0000000..94f54b6
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_invert_colors_enable_animation_cross_1.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="pathData"
+ android:valueFrom="M 7.54049682617,3.9430847168 c 0.0,0.0 31.5749816895,31.4499664307 31.5749816895,31.4499664307 "
+ android:valueTo="M 7.54049682617,3.9430847168 c 0.0,0.0 31.5749816895,31.4499664307 31.5749816895,31.4499664307 "
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/linear" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:valueFrom="M 7.54049682617,3.9430847168 c 0.0,0.0 31.5749816895,31.4499664307 31.5749816895,31.4499664307 "
+ android:valueTo="M 7.54049682617,3.9430847168 c 0.0,0.0 0.324981689453,0.399978637695 0.324981689453,0.399978637695 "
+ android:valueType="pathType"
+ android:interpolator="@interpolator/ic_invert_colors_enable_cross_1_pathdata_interpolator" />
+ </set>
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="533"
+ android:propertyName="strokeAlpha"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:interpolator="@android:interpolator/linear" />
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="strokeAlpha"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:interpolator="@android:interpolator/linear" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_invert_colors_enable_animation_icon.xml b/packages/SystemUI/res/anim/ic_invert_colors_enable_animation_icon.xml
new file mode 100644
index 0000000..50a1af6
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_invert_colors_enable_animation_icon.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="alpha"
+ android:valueFrom="0.54"
+ android:valueTo="0.54"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="alpha"
+ android:valueFrom="0.54"
+ android:valueTo="1"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_invert_colors_enable_animation_mask.xml b/packages/SystemUI/res/anim/ic_invert_colors_enable_animation_mask.xml
new file mode 100644
index 0000000..9531cd9
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_invert_colors_enable_animation_mask.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="pathData"
+ android:valueFrom="M 37.8337860107,-40.3974914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 40.9884796143,40.9278411865 40.9884796143,40.9278411865 c 0.0,0.0 -2.61700439453,2.0938873291 -2.61700439453,2.0938873291 c 0.0,0.0 -41.1884460449,-40.9392852783 -41.1884460449,-40.9392852783 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueTo="M 37.8337860107,-40.3974914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 40.9884796143,40.9278411865 40.9884796143,40.9278411865 c 0.0,0.0 -2.61700439453,2.0938873291 -2.61700439453,2.0938873291 c 0.0,0.0 -41.1884460449,-40.9392852783 -41.1884460449,-40.9392852783 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/linear" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:valueFrom="M 37.8337860107,-40.3974914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 40.9884796143,40.9278411865 40.9884796143,40.9278411865 c 0.0,0.0 -2.61700439453,2.0938873291 -2.61700439453,2.0938873291 c 0.0,0.0 -41.1884460449,-40.9392852783 -41.1884460449,-40.9392852783 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueTo="M 37.8337860107,-40.4599914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 9.55097961426,9.55285644531 9.55097961426,9.55285644531 c 0.0,0.0 -2.61698913574,2.09387207031 -2.61698913574,2.09387207031 c 0.0,0.0 -9.75096130371,-9.56428527832 -9.75096130371,-9.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueType="pathType"
+ android:interpolator="@interpolator/ic_invert_colors_enable_mask_pathdata_interpolator" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_invert_colors_enable_animation_root.xml b/packages/SystemUI/res/anim/ic_invert_colors_enable_animation_root.xml
new file mode 100644
index 0000000..387ca29
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_invert_colors_enable_animation_root.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="alpha"
+ android:valueFrom="0.3"
+ android:valueTo="1.0"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_airplane_disable_animation_cross_1.xml b/packages/SystemUI/res/anim/ic_signal_airplane_disable_animation_cross_1.xml
new file mode 100644
index 0000000..553da1a
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_airplane_disable_animation_cross_1.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="pathData"
+ android:valueFrom="M 7.54049682617,3.9430847168 c 0.0,0.0 0.324981689453,0.399978637695 0.324981689453,0.399978637695 "
+ android:valueTo="M 7.54049682617,3.9430847168 c 0.0,0.0 0.324981689453,0.399978637695 0.324981689453,0.399978637695 "
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/linear" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:valueFrom="M 7.54049682617,3.9430847168 c 0.0,0.0 0.324981689453,0.399978637695 0.324981689453,0.399978637695 "
+ android:valueTo="M 7.54049682617,3.9430847168 c 0.0,0.0 31.5749816895,31.4499664307 31.5749816895,31.4499664307 "
+ android:valueType="pathType"
+ android:interpolator="@interpolator/ic_signal_airplane_disable_cross_1_pathdata_interpolator" />
+ </set>
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="strokeAlpha"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:interpolator="@android:interpolator/linear" />
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="strokeAlpha"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:interpolator="@android:interpolator/linear" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_airplane_disable_animation_ic_signal_airplane.xml b/packages/SystemUI/res/anim/ic_signal_airplane_disable_animation_ic_signal_airplane.xml
new file mode 100644
index 0000000..9cc3b8e
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_airplane_disable_animation_ic_signal_airplane.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="alpha"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="alpha"
+ android:valueFrom="1"
+ android:valueTo="0.5"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_airplane_disable_animation_mask.xml b/packages/SystemUI/res/anim/ic_signal_airplane_disable_animation_mask.xml
new file mode 100644
index 0000000..605ef90
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_airplane_disable_animation_mask.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="pathData"
+ android:valueFrom="M 37.8337860107,-40.4599914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 9.55097961426,9.55285644531 9.55097961426,9.55285644531 c 0.0,0.0 -2.61698913574,2.09387207031 -2.61698913574,2.09387207031 c 0.0,0.0 -9.75096130371,-9.56428527832 -9.75096130371,-9.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueTo="M 37.8337860107,-40.4599914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 9.55097961426,9.55285644531 9.55097961426,9.55285644531 c 0.0,0.0 -2.61698913574,2.09387207031 -2.61698913574,2.09387207031 c 0.0,0.0 -9.75096130371,-9.56428527832 -9.75096130371,-9.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:valueFrom="M 37.8337860107,-40.4599914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 9.55097961426,9.55285644531 9.55097961426,9.55285644531 c 0.0,0.0 -2.61698913574,2.09387207031 -2.61698913574,2.09387207031 c 0.0,0.0 -9.75096130371,-9.56428527832 -9.75096130371,-9.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueTo="M 37.8337860107,-40.3974914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 40.9884796143,40.9278411865 40.9884796143,40.9278411865 c 0.0,0.0 -2.61700439453,2.0938873291 -2.61700439453,2.0938873291 c 0.0,0.0 -41.1884460449,-40.9392852783 -41.1884460449,-40.9392852783 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_airplane_disable_animation_root.xml b/packages/SystemUI/res/anim/ic_signal_airplane_disable_animation_root.xml
new file mode 100644
index 0000000..770c401
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_airplane_disable_animation_root.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="alpha"
+ android:valueFrom="1.0"
+ android:valueTo="0.3"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_airplane_enable_animation_cross_1.xml b/packages/SystemUI/res/anim/ic_signal_airplane_enable_animation_cross_1.xml
new file mode 100644
index 0000000..87cbaec
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_airplane_enable_animation_cross_1.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="pathData"
+ android:valueFrom="M 7.54049682617,3.9430847168 c 0.0,0.0 31.5749816895,31.4499664307 31.5749816895,31.4499664307 "
+ android:valueTo="M 7.54049682617,3.9430847168 c 0.0,0.0 31.5749816895,31.4499664307 31.5749816895,31.4499664307 "
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/linear" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:valueFrom="M 7.54049682617,3.9430847168 c 0.0,0.0 31.5749816895,31.4499664307 31.5749816895,31.4499664307 "
+ android:valueTo="M 7.54049682617,3.9430847168 c 0.0,0.0 0.324981689453,0.399978637695 0.324981689453,0.399978637695 "
+ android:valueType="pathType"
+ android:interpolator="@interpolator/ic_signal_airplane_enable_cross_1_pathdata_interpolator" />
+ </set>
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="533"
+ android:propertyName="strokeAlpha"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:interpolator="@android:interpolator/linear" />
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="strokeAlpha"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:interpolator="@android:interpolator/linear" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_airplane_enable_animation_ic_signal_airplane.xml b/packages/SystemUI/res/anim/ic_signal_airplane_enable_animation_ic_signal_airplane.xml
new file mode 100644
index 0000000..5fdb2a0
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_airplane_enable_animation_ic_signal_airplane.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="alpha"
+ android:valueFrom="0.5"
+ android:valueTo="0.5"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="alpha"
+ android:valueFrom="0.5"
+ android:valueTo="1"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_airplane_enable_animation_mask.xml b/packages/SystemUI/res/anim/ic_signal_airplane_enable_animation_mask.xml
new file mode 100644
index 0000000..56e0d1c
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_airplane_enable_animation_mask.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="pathData"
+ android:valueFrom="M 37.8337860107,-40.3974914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 40.9884796143,40.9278411865 40.9884796143,40.9278411865 c 0.0,0.0 -2.61700439453,2.0938873291 -2.61700439453,2.0938873291 c 0.0,0.0 -41.1884460449,-40.9392852783 -41.1884460449,-40.9392852783 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueTo="M 37.8337860107,-40.3974914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 40.9884796143,40.9278411865 40.9884796143,40.9278411865 c 0.0,0.0 -2.61700439453,2.0938873291 -2.61700439453,2.0938873291 c 0.0,0.0 -41.1884460449,-40.9392852783 -41.1884460449,-40.9392852783 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/linear" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:valueFrom="M 37.8337860107,-40.3974914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 40.9884796143,40.9278411865 40.9884796143,40.9278411865 c 0.0,0.0 -2.61700439453,2.0938873291 -2.61700439453,2.0938873291 c 0.0,0.0 -41.1884460449,-40.9392852783 -41.1884460449,-40.9392852783 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueTo="M 37.8337860107,-40.4599914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 9.55097961426,9.55285644531 9.55097961426,9.55285644531 c 0.0,0.0 -2.61698913574,2.09387207031 -2.61698913574,2.09387207031 c 0.0,0.0 -9.75096130371,-9.56428527832 -9.75096130371,-9.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueType="pathType"
+ android:interpolator="@interpolator/ic_signal_airplane_enable_mask_pathdata_interpolator" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_airplane_enable_animation_root.xml b/packages/SystemUI/res/anim/ic_signal_airplane_enable_animation_root.xml
new file mode 100644
index 0000000..387ca29
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_airplane_enable_animation_root.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="alpha"
+ android:valueFrom="0.3"
+ android:valueTo="1.0"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_flashlight_disable_animation_cross_1.xml b/packages/SystemUI/res/anim/ic_signal_flashlight_disable_animation_cross_1.xml
new file mode 100644
index 0000000..aff3567
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_flashlight_disable_animation_cross_1.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="pathData"
+ android:valueFrom="M 8.34049987793,5.6930847168 c 0.0,0.0 0.274993896484,0.29997253418 0.274993896484,0.29997253418 "
+ android:valueTo="M 8.34049987793,5.6930847168 c 0.0,0.0 0.274993896484,0.29997253418 0.274993896484,0.29997253418 "
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:valueFrom="M 8.34049987793,5.6930847168 c 0.0,0.0 0.274993896484,0.29997253418 0.274993896484,0.29997253418 "
+ android:valueTo="M 8.34049987793,5.6930847168 c 0.0,0.0 29.7749786377,29.7999725342 29.7749786377,29.7999725342 "
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="strokeAlpha"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:interpolator="@android:interpolator/linear" />
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="strokeAlpha"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:interpolator="@android:interpolator/linear" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_flashlight_disable_animation_ic_signal_flashlight.xml b/packages/SystemUI/res/anim/ic_signal_flashlight_disable_animation_ic_signal_flashlight.xml
new file mode 100644
index 0000000..9cc3b8e
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_flashlight_disable_animation_ic_signal_flashlight.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="alpha"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="alpha"
+ android:valueFrom="1"
+ android:valueTo="0.5"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_flashlight_disable_animation_mask.xml b/packages/SystemUI/res/anim/ic_signal_flashlight_disable_animation_mask.xml
new file mode 100644
index 0000000..31583f2
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_flashlight_disable_animation_mask.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="pathData"
+ android:valueFrom="M 37.8337860107,-39.2849731445 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 9.55097961426,9.55285644531 9.55097961426,9.55285644531 c 0.0,0.0 -2.61698913574,2.09387207031 -2.61698913574,2.09387207031 c 0.0,0.0 -9.75096130371,-9.56428527832 -9.75096130371,-9.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueTo="M 37.8337860107,-39.2849731445 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 9.55097961426,9.55285644531 9.55097961426,9.55285644531 c 0.0,0.0 -2.61698913574,2.09387207031 -2.61698913574,2.09387207031 c 0.0,0.0 -9.75096130371,-9.56428527832 -9.75096130371,-9.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:valueFrom="M 37.8337860107,-39.2849731445 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 9.55097961426,9.55285644531 9.55097961426,9.55285644531 c 0.0,0.0 -2.61698913574,2.09387207031 -2.61698913574,2.09387207031 c 0.0,0.0 -9.75096130371,-9.56428527832 -9.75096130371,-9.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueTo="M 37.8337860107,-39.2975769043 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 40.9884796143,40.9278411865 40.9884796143,40.9278411865 c 0.0,0.0 -2.61700439453,2.0938873291 -2.61700439453,2.0938873291 c 0.0,0.0 -41.1884460449,-40.9392852783 -41.1884460449,-40.9392852783 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_flashlight_disable_animation_root.xml b/packages/SystemUI/res/anim/ic_signal_flashlight_disable_animation_root.xml
new file mode 100644
index 0000000..770c401
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_flashlight_disable_animation_root.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="alpha"
+ android:valueFrom="1.0"
+ android:valueTo="0.3"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_flashlight_enable_animation_cross_1.xml b/packages/SystemUI/res/anim/ic_signal_flashlight_enable_animation_cross_1.xml
new file mode 100644
index 0000000..c923015
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_flashlight_enable_animation_cross_1.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="pathData"
+ android:valueFrom="M 8.34049987793,5.6930847168 c 0.0,0.0 29.7749786377,29.7999725342 29.7749786377,29.7999725342 "
+ android:valueTo="M 8.34049987793,5.6930847168 c 0.0,0.0 29.7749786377,29.7999725342 29.7749786377,29.7999725342 "
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/linear" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:valueFrom="M 8.34049987793,5.6930847168 c 0.0,0.0 29.7749786377,29.7999725342 29.7749786377,29.7999725342 "
+ android:valueTo="M 8.34049987793,5.6930847168 c 0.0,0.0 0.274993896484,0.29997253418 0.274993896484,0.29997253418 "
+ android:valueType="pathType"
+ android:interpolator="@interpolator/ic_signal_flashlight_enable_cross_1_pathdata_interpolator" />
+ </set>
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="533"
+ android:propertyName="strokeAlpha"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:interpolator="@android:interpolator/linear" />
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="strokeAlpha"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:interpolator="@android:interpolator/linear" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_flashlight_enable_animation_ic_signal_flashlight.xml b/packages/SystemUI/res/anim/ic_signal_flashlight_enable_animation_ic_signal_flashlight.xml
new file mode 100644
index 0000000..5fdb2a0
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_flashlight_enable_animation_ic_signal_flashlight.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="alpha"
+ android:valueFrom="0.5"
+ android:valueTo="0.5"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="alpha"
+ android:valueFrom="0.5"
+ android:valueTo="1"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_flashlight_enable_animation_mask.xml b/packages/SystemUI/res/anim/ic_signal_flashlight_enable_animation_mask.xml
new file mode 100644
index 0000000..650d89f
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_flashlight_enable_animation_mask.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="pathData"
+ android:valueFrom="M 37.8337860107,-39.2975769043 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 40.9884796143,40.9278411865 40.9884796143,40.9278411865 c 0.0,0.0 -2.61700439453,2.0938873291 -2.61700439453,2.0938873291 c 0.0,0.0 -41.1884460449,-40.9392852783 -41.1884460449,-40.9392852783 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueTo="M 37.8337860107,-39.2975769043 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 40.9884796143,40.9278411865 40.9884796143,40.9278411865 c 0.0,0.0 -2.61700439453,2.0938873291 -2.61700439453,2.0938873291 c 0.0,0.0 -41.1884460449,-40.9392852783 -41.1884460449,-40.9392852783 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/linear" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:valueFrom="M 37.8337860107,-39.2975769043 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 40.9884796143,40.9278411865 40.9884796143,40.9278411865 c 0.0,0.0 -2.61700439453,2.0938873291 -2.61700439453,2.0938873291 c 0.0,0.0 -41.1884460449,-40.9392852783 -41.1884460449,-40.9392852783 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueTo="M 37.8337860107,-39.2849731445 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 9.55097961426,9.55285644531 9.55097961426,9.55285644531 c 0.0,0.0 -2.61698913574,2.09387207031 -2.61698913574,2.09387207031 c 0.0,0.0 -9.75096130371,-9.56428527832 -9.75096130371,-9.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueType="pathType"
+ android:interpolator="@interpolator/ic_signal_flashlight_enable_mask_pathdata_interpolator" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_flashlight_enable_animation_root.xml b/packages/SystemUI/res/anim/ic_signal_flashlight_enable_animation_root.xml
new file mode 100644
index 0000000..387ca29
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_flashlight_enable_animation_root.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="alpha"
+ android:valueFrom="0.3"
+ android:valueTo="1.0"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_location_disable_animation_cross_1.xml b/packages/SystemUI/res/anim/ic_signal_location_disable_animation_cross_1.xml
new file mode 100644
index 0000000..73283a8
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_location_disable_animation_cross_1.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="pathData"
+ android:valueFrom="M 6.44050598145,1.9430847168 c 0.0,0.0 0.374984741211,0.399978637695 0.374984741211,0.399978637695 "
+ android:valueTo="M 6.44050598145,1.9430847168 c 0.0,0.0 0.374984741211,0.399978637695 0.374984741211,0.399978637695 "
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:valueFrom="M 6.44050598145,1.9430847168 c 0.0,0.0 0.374984741211,0.399978637695 0.374984741211,0.399978637695 "
+ android:valueTo="M 6.44050598145,1.9430847168 c 0.0,0.0 33.5749816895,33.4499664307 33.5749816895,33.4499664307 "
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="strokeAlpha"
+ android:valueFrom="0"
+ android:valueTo="0"
+ android:interpolator="@android:interpolator/linear" />
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="strokeAlpha"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:interpolator="@android:interpolator/linear" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_location_disable_animation_ic_signal_location.xml b/packages/SystemUI/res/anim/ic_signal_location_disable_animation_ic_signal_location.xml
new file mode 100644
index 0000000..9cc3b8e
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_location_disable_animation_ic_signal_location.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="alpha"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="alpha"
+ android:valueFrom="1"
+ android:valueTo="0.5"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_location_disable_animation_mask.xml b/packages/SystemUI/res/anim/ic_signal_location_disable_animation_mask.xml
new file mode 100644
index 0000000..7ef7b5b
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_location_disable_animation_mask.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="pathData"
+ android:valueFrom="M 38.8337860107,-40.4599914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 7.3759765625,7.55284118652 7.3759765625,7.55284118652 c 0.0,0.0 -2.61698913574,2.0938873291 -2.61698913574,2.0938873291 c 0.0,0.0 -7.57595825195,-7.56428527832 -7.57595825195,-7.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueTo="M 38.8337860107,-40.4599914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 7.3759765625,7.55284118652 7.3759765625,7.55284118652 c 0.0,0.0 -2.61698913574,2.0938873291 -2.61698913574,2.0938873291 c 0.0,0.0 -7.57595825195,-7.56428527832 -7.57595825195,-7.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:valueFrom="M 38.8337860107,-40.4599914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 7.3759765625,7.55284118652 7.3759765625,7.55284118652 c 0.0,0.0 -2.61698913574,2.0938873291 -2.61698913574,2.0938873291 c 0.0,0.0 -7.57595825195,-7.56428527832 -7.57595825195,-7.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueTo="M 38.8337860107,-40.3974914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 40.9884796143,41.1153411865 40.9884796143,41.1153411865 c 0.0,0.0 -2.61700439453,2.0938873291 -2.61700439453,2.0938873291 c 0.0,0.0 -41.1884460449,-41.1267852783 -41.1884460449,-41.1267852783 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_location_disable_animation_root.xml b/packages/SystemUI/res/anim/ic_signal_location_disable_animation_root.xml
new file mode 100644
index 0000000..770c401
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_location_disable_animation_root.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="alpha"
+ android:valueFrom="1.0"
+ android:valueTo="0.3"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_location_enable_animation_cross_1.xml b/packages/SystemUI/res/anim/ic_signal_location_enable_animation_cross_1.xml
new file mode 100644
index 0000000..f4fc20e
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_location_enable_animation_cross_1.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="pathData"
+ android:valueFrom="M 6.44050598145,1.9430847168 c 0.0,0.0 33.5749816895,33.4499664307 33.5749816895,33.4499664307 "
+ android:valueTo="M 6.44050598145,1.9430847168 c 0.0,0.0 33.5749816895,33.4499664307 33.5749816895,33.4499664307 "
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/linear" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:valueFrom="M 6.44050598145,1.9430847168 c 0.0,0.0 33.5749816895,33.4499664307 33.5749816895,33.4499664307 "
+ android:valueTo="M 6.44050598145,1.9430847168 c 0.0,0.0 0.374984741211,0.399978637695 0.374984741211,0.399978637695 "
+ android:valueType="pathType"
+ android:interpolator="@interpolator/ic_signal_location_enable_cross_1_pathdata_interpolator" />
+ </set>
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="533"
+ android:propertyName="strokeAlpha"
+ android:valueFrom="1"
+ android:valueTo="1"
+ android:interpolator="@android:interpolator/linear" />
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="strokeAlpha"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:interpolator="@android:interpolator/linear" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_location_enable_animation_ic_signal_location.xml b/packages/SystemUI/res/anim/ic_signal_location_enable_animation_ic_signal_location.xml
new file mode 100644
index 0000000..5fdb2a0
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_location_enable_animation_ic_signal_location.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="alpha"
+ android:valueFrom="0.5"
+ android:valueTo="0.5"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="alpha"
+ android:valueFrom="0.5"
+ android:valueTo="1"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_location_enable_animation_mask.xml b/packages/SystemUI/res/anim/ic_signal_location_enable_animation_mask.xml
new file mode 100644
index 0000000..b825eb9
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_location_enable_animation_mask.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <set
+ android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="pathData"
+ android:valueFrom="M 38.8337860107,-40.3974914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 40.9884796143,41.1153411865 40.9884796143,41.1153411865 c 0.0,0.0 -2.61700439453,2.0938873291 -2.61700439453,2.0938873291 c 0.0,0.0 -41.1884460449,-41.1267852783 -41.1884460449,-41.1267852783 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueTo="M 38.8337860107,-40.3974914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 40.9884796143,41.1153411865 40.9884796143,41.1153411865 c 0.0,0.0 -2.61700439453,2.0938873291 -2.61700439453,2.0938873291 c 0.0,0.0 -41.1884460449,-41.1267852783 -41.1884460449,-41.1267852783 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueType="pathType"
+ android:interpolator="@android:interpolator/linear" />
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:valueFrom="M 38.8337860107,-40.3974914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 40.9884796143,41.1153411865 40.9884796143,41.1153411865 c 0.0,0.0 -2.61700439453,2.0938873291 -2.61700439453,2.0938873291 c 0.0,0.0 -41.1884460449,-41.1267852783 -41.1884460449,-41.1267852783 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueTo="M 38.8337860107,-40.4599914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 7.3759765625,7.55284118652 7.3759765625,7.55284118652 c 0.0,0.0 -2.61698913574,2.0938873291 -2.61698913574,2.0938873291 c 0.0,0.0 -7.57595825195,-7.56428527832 -7.57595825195,-7.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z"
+ android:valueType="pathType"
+ android:interpolator="@interpolator/ic_signal_location_enable_mask_pathdata_interpolator" />
+ </set>
+</set>
diff --git a/packages/SystemUI/res/anim/ic_signal_location_enable_animation_root.xml b/packages/SystemUI/res/anim/ic_signal_location_enable_animation_root.xml
new file mode 100644
index 0000000..387ca29
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_signal_location_enable_animation_root.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="alpha"
+ android:valueFrom="0.3"
+ android:valueTo="1.0"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/drawable/ic_invert_colors_disable.xml b/packages/SystemUI/res/drawable/ic_invert_colors_disable.xml
new file mode 100644
index 0000000..f901e86
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_invert_colors_disable.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="root"
+ android:alpha="1.0"
+ android:height="48dp"
+ android:width="48dp"
+ android:viewportHeight="48"
+ android:viewportWidth="48" >
+ <group
+ android:name="icon"
+ android:translateX="21.9995"
+ android:translateY="25.73401" >
+ <group
+ android:name="icon_pivot"
+ android:translateX="-23.21545"
+ android:translateY="-18.86649" >
+ <clip-path
+ android:name="mask"
+ android:pathData="M 37.8337860107,-40.4599914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 9.55097961426,9.55285644531 9.55097961426,9.55285644531 c 0.0,0.0 -2.61698913574,2.09387207031 -2.61698913574,2.09387207031 c 0.0,0.0 -9.75096130371,-9.56428527832 -9.75096130371,-9.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z" />
+ <group
+ android:name="cross" >
+ <path
+ android:name="cross_1"
+ android:pathData="M 7.54049682617,3.9430847168 c 0.0,0.0 0.324981689453,0.399978637695 0.324981689453,0.399978637695 "
+ android:strokeColor="#FFFFFFFF"
+ android:strokeAlpha="0"
+ android:strokeWidth="3.5"
+ android:fillColor="#00000000" />
+ </group>
+ <group
+ android:name="ink_drop"
+ android:translateY="-6.8" >
+ <path
+ android:name="outer_drop"
+ android:pathData="M 35.3000030518,15.8999938965 c 0.0,0.0 -11.3000030518,-11.3999938965 -11.3000030518,-11.3999938965 c 0,0.0 -11.3000030518,11.3999938965 -11.3000030518,11.3999938965 c -6.19999694824,6.20001220703 -6.19999694824,16.4000091553 0.0,22.6000061035 c 3.10000610352,3.10000610352 7.19999694824,4.69999694824 11.3000030518,4.69999694824 c 4.10000610352,0.0 8.19999694824,-1.59999084473 11.3000030518,-4.69999694824 c 6.30000305176,-6.30000305176 6.30000305176,-16.3999938965 0.0,-22.6000061035 Z M 24,39.1999969482 c 0,0.0 0,0.0 0,0.0 c -3.19999694824,0.0 -6.19999694824,-1.19999694824 -8.5,-3.5 c -2.30000305176,-2.29998779297 -3.5,-5.30000305176 -3.5,-8.5 c 0,-3.19999694824 1.19999694824,-6.19999694824 3.5,-8.5 c 0.0,0.0 8.5,-8.5 8.5,-8.5 c 0,0.0 0,29.0 0,29.0 Z"
+ android:fillColor="#FFFFFFFF"
+ android:fillAlpha="1" />
+ </group>
+ </group>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_invert_colors_disable_animation.xml b/packages/SystemUI/res/drawable/ic_invert_colors_disable_animation.xml
new file mode 100644
index 0000000..2e26d42
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_invert_colors_disable_animation.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/ic_invert_colors_disable" >
+ <target
+ android:name="root"
+ android:animation="@anim/ic_invert_colors_disable_animation_root" />
+ <target
+ android:name="icon"
+ android:animation="@anim/ic_invert_colors_disable_animation_icon" />
+ <target
+ android:name="mask"
+ android:animation="@anim/ic_invert_colors_disable_animation_mask" />
+ <target
+ android:name="cross_1"
+ android:animation="@anim/ic_invert_colors_disable_animation_cross_1" />
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_invert_colors_enable.xml b/packages/SystemUI/res/drawable/ic_invert_colors_enable.xml
new file mode 100644
index 0000000..730ecc4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_invert_colors_enable.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="root"
+ android:alpha="0.3"
+ android:height="48dp"
+ android:width="48dp"
+ android:viewportHeight="48"
+ android:viewportWidth="48" >
+ <group
+ android:name="icon"
+ android:translateX="21.9995"
+ android:translateY="25.73401"
+ android:alpha="0.54" >
+ <group
+ android:name="icon_pivot"
+ android:translateX="-23.21545"
+ android:translateY="-18.86649" >
+ <clip-path
+ android:name="mask"
+ android:pathData="M 37.8337860107,-40.3974914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 40.9884796143,40.9278411865 40.9884796143,40.9278411865 c 0.0,0.0 -2.61700439453,2.0938873291 -2.61700439453,2.0938873291 c 0.0,0.0 -41.1884460449,-40.9392852783 -41.1884460449,-40.9392852783 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z" />
+ <group
+ android:name="cross" >
+ <path
+ android:name="cross_1"
+ android:pathData="M 7.54049682617,3.9430847168 c 0.0,0.0 31.5749816895,31.4499664307 31.5749816895,31.4499664307 "
+ android:strokeColor="#FFFFFFFF"
+ android:strokeAlpha="1"
+ android:strokeWidth="3.5"
+ android:fillColor="#00000000" />
+ </group>
+ <group
+ android:name="ink_drop"
+ android:translateY="-6.8" >
+ <path
+ android:name="outer_drop"
+ android:pathData="M 35.3000030518,15.8999938965 c 0.0,0.0 -11.3000030518,-11.3999938965 -11.3000030518,-11.3999938965 c 0,0.0 -11.3000030518,11.3999938965 -11.3000030518,11.3999938965 c -6.19999694824,6.20001220703 -6.19999694824,16.4000091553 0.0,22.6000061035 c 3.10000610352,3.10000610352 7.19999694824,4.69999694824 11.3000030518,4.69999694824 c 4.10000610352,0.0 8.19999694824,-1.59999084473 11.3000030518,-4.69999694824 c 6.30000305176,-6.30000305176 6.30000305176,-16.3999938965 0.0,-22.6000061035 Z M 24,39.1999969482 c 0,0.0 0,0.0 0,0.0 c -3.19999694824,0.0 -6.19999694824,-1.19999694824 -8.5,-3.5 c -2.30000305176,-2.29998779297 -3.5,-5.30000305176 -3.5,-8.5 c 0,-3.19999694824 1.19999694824,-6.19999694824 3.5,-8.5 c 0.0,0.0 8.5,-8.5 8.5,-8.5 c 0,0.0 0,29.0 0,29.0 Z"
+ android:fillColor="#FFFFFFFF"
+ android:fillAlpha="1" />
+ </group>
+ </group>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_invert_colors_enable_animation.xml b/packages/SystemUI/res/drawable/ic_invert_colors_enable_animation.xml
new file mode 100644
index 0000000..a709efb
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_invert_colors_enable_animation.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/ic_invert_colors_enable" >
+ <target
+ android:name="root"
+ android:animation="@anim/ic_invert_colors_enable_animation_root" />
+ <target
+ android:name="icon"
+ android:animation="@anim/ic_invert_colors_enable_animation_icon" />
+ <target
+ android:name="mask"
+ android:animation="@anim/ic_invert_colors_enable_animation_mask" />
+ <target
+ android:name="cross_1"
+ android:animation="@anim/ic_invert_colors_enable_animation_cross_1" />
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_airplane_off.xml b/packages/SystemUI/res/drawable/ic_qs_airplane_off.xml
deleted file mode 100644
index 79a9d409..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_airplane_off.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="64dp"
- android:height="64dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
-
- <path
- android:fillColor="#4DFFFFFF"
- android:pathData="M26.0,18.0L26.0,7.0c0.0,-1.7 -1.3,-3.0 -3.0,-3.0c-1.7,0.0 -3.0,1.3 -3.0,3.0l0.0,7.4L35.7,30.0l6.3,2.0l0.0,-4.0L26.0,18.0zM6.0,10.5l10.0,10.0L4.0,28.0l0.0,4.0l16.0,-5.0l0.0,11.0l-4.0,3.0l0.0,3.0l7.0,-2.0l7.0,2.0l0.0,-3.0l-4.0,-3.0l0.0,-7.5L37.5,42.0l2.5,-2.5L8.5,8.0L6.0,10.5z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_airplane_on.xml b/packages/SystemUI/res/drawable/ic_qs_airplane_on.xml
deleted file mode 100644
index 5d5d257..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_airplane_on.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="64dp"
- android:height="64dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
-
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M20.4,18.0"/>
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M42.0,32.0l0.0,-4.0L26.0,18.0L26.0,7.0c0.0,-1.7 -1.3,-3.0 -3.0,-3.0c-1.7,0.0 -3.0,1.3 -3.0,3.0l0.0,11.0L4.0,28.0l0.0,4.0l16.0,-5.0l0.0,11.0l-4.0,3.0l0.0,3.0l7.0,-2.0l7.0,2.0l0.0,-3.0l-4.0,-3.0L26.0,27.0L42.0,32.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_flashlight_off.xml b/packages/SystemUI/res/drawable/ic_qs_flashlight_off.xml
deleted file mode 100644
index d4bd76f..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_flashlight_off.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="64.0dp"
- android:height="64.0dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
-
- <path
- android:fillColor="#4DFFFFFF"
- android:pathData="M14.708,11.394l14.899,14.9l0.0,-6.771c4.359,-2.353 3.831,-7.489 3.831,-7.489l0.0,-0.64L14.708,11.393998z"/>
- <path
- android:fillColor="#4DFFFFFF"
- android:pathData="M14.568,4.0l18.87,0.0l0.0,3.917l-18.87,0.0z"/>
- <path
- android:fillColor="#4DFFFFFF"
- android:pathData="M38.284,39.427l-29.767,-29.766998 -2.4750004,2.4750004 12.351999,12.351 0.0,19.514 11.213001,0.0 0.0,-8.300999 6.2019978,6.2019997z"/>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_qs_flashlight_on.xml b/packages/SystemUI/res/drawable/ic_qs_flashlight_on.xml
deleted file mode 100644
index 5514b44c..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_flashlight_on.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="64.0dp"
- android:height="64.0dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
-
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M33.438,12.034l0.0,-0.64l-18.87,0.0l0.0,0.64c0.0,0.0 -0.581,5.189 3.826,7.523L18.394,44.0l11.213,0.0L29.606998,19.523C33.966,17.17 33.438,12.034 33.438,12.034zM24.0,27.697c-1.523,0.0 -2.757,-1.234 -2.757,-2.757c0.0,-1.523 1.234,-2.757 2.757,-2.757c1.523,0.0 2.757,1.234 2.757,2.757C26.757,26.462 25.523,27.697 24.0,27.697z"/>
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M14.568,4.0l18.87,0.0l0.0,3.917l-18.87,0.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_inversion_off.xml b/packages/SystemUI/res/drawable/ic_qs_inversion_off.xml
deleted file mode 100644
index 4237b63..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_inversion_off.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="64dp"
- android:height="64dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
-
- <path
- android:fillColor="#4DFFFFFF"
- android:pathData="M41.3,41.7L36.6,37.0L24.0,24.5l-7.1,-7.1L14.0,14.5L8.5,9.0L6.0,11.5l5.6,5.6c-5.1,6.3 -4.7,15.5 1.1,21.4c3.1,3.1 7.2,4.7 11.3,4.7c3.6,0.0 7.1,-1.2 10.1,-3.6l5.4,5.4l2.5,-2.5L41.3,41.7zM24.0,39.2c-3.2,0.0 -6.2,-1.2 -8.5,-3.5c-2.3,-2.3 -3.5,-5.3 -3.5,-8.5c0.0,-2.6 0.9,-5.1 2.4,-7.2l9.6,9.6L24.0,39.2zM24.0,10.2l0.0,9.2l14.5,14.5c2.7,-5.9 1.7,-13.1 -3.2,-18.0L24.0,4.5l0.0,0.0l0.0,0.0L16.6,12.0l2.8,2.8L24.0,10.2z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_inversion_on.xml b/packages/SystemUI/res/drawable/ic_qs_inversion_on.xml
deleted file mode 100644
index 860e769..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_inversion_on.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="64dp"
- android:height="64dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
-
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M35.3,15.9L24.0,4.5l0.0,0.0l0.0,0.0L12.7,15.9c-6.2,6.2 -6.2,16.4 0.0,22.6c3.1,3.1 7.2,4.7 11.3,4.7s8.2,-1.6 11.3,-4.7C41.6,32.2 41.6,22.1 35.3,15.9zM24.0,39.2L24.0,39.2c-3.2,0.0 -6.2,-1.2 -8.5,-3.5c-2.3,-2.3 -3.5,-5.3 -3.5,-8.5s1.2,-6.2 3.5,-8.5l8.5,-8.5L24.0,39.2z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_off.xml b/packages/SystemUI/res/drawable/ic_qs_location_off.xml
deleted file mode 100644
index e0fe12e..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_off.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="64dp"
- android:height="64dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
-
- <path
- android:fillColor="#4DFFFFFF"
- android:pathData="M24.0,13.0c2.8,0.0 5.0,2.2 5.0,5.0c0.0,1.5 -0.7,2.8 -1.7,3.7l7.3,7.3c2.0,-3.7 3.4,-7.6 3.4,-11.0c0.0,-7.7 -6.3,-14.0 -14.0,-14.0c-4.0,0.0 -7.5,1.6 -10.1,4.3l6.4,6.4C21.2,13.6 22.5,13.0 24.0,13.0zM32.7,32.2l-9.3,-9.3l-0.2,-0.2L6.5,6.0L4.0,8.5l6.4,6.4c-0.2,1.0 -0.4,2.0 -0.4,3.1c0.0,10.5 14.0,26.0 14.0,26.0s3.3,-3.7 6.8,-8.7l6.7,6.7l2.5,-2.5L32.7,32.2z"/>
- <path
- android:pathData="M23.5,22.9l0.0,0.0 -0.20000076,-0.19999886z"
- android:fillColor="#4DFFFFFF"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_on.xml b/packages/SystemUI/res/drawable/ic_qs_location_on.xml
deleted file mode 100644
index 6a7cd53..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_on.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="64dp"
- android:height="64dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
-
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M24.0,4.0c-7.7,0.0 -14.0,6.3 -14.0,14.0c0.0,10.5 14.0,26.0 14.0,26.0s14.0,-15.5 14.0,-26.0C38.0,10.3 31.7,4.0 24.0,4.0zM24.0,23.0c-2.8,0.0 -5.0,-2.2 -5.0,-5.0s2.2,-5.0 5.0,-5.0c2.8,0.0 5.0,2.2 5.0,5.0S26.8,23.0 24.0,23.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_airplane_disable.xml b/packages/SystemUI/res/drawable/ic_signal_airplane_disable.xml
new file mode 100644
index 0000000..09a67e1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_signal_airplane_disable.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="root"
+ android:alpha="1.0"
+ android:height="48dp"
+ android:width="48dp"
+ android:viewportHeight="48"
+ android:viewportWidth="48" >
+ <group
+ android:name="ic_signal_airplane"
+ android:translateX="21.9995"
+ android:translateY="25.73401" >
+ <group
+ android:name="ic_signal_airplane_pivot"
+ android:translateX="-23.21545"
+ android:translateY="-18.86649" >
+ <clip-path
+ android:name="mask"
+ android:pathData="M 37.8337860107,-40.4599914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 9.55097961426,9.55285644531 9.55097961426,9.55285644531 c 0.0,0.0 -2.61698913574,2.09387207031 -2.61698913574,2.09387207031 c 0.0,0.0 -9.75096130371,-9.56428527832 -9.75096130371,-9.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z" />
+ <group
+ android:name="cross" >
+ <path
+ android:name="cross_1"
+ android:pathData="M 7.54049682617,3.9430847168 c 0.0,0.0 0.324981689453,0.399978637695 0.324981689453,0.399978637695 "
+ android:strokeColor="#FFFFFFFF"
+ android:strokeAlpha="0"
+ android:strokeWidth="3.5"
+ android:fillColor="#00000000" />
+ </group>
+ <group
+ android:name="plane"
+ android:translateX="23.481"
+ android:translateY="18.71151" >
+ <path
+ android:name="plane_1"
+ android:pathData="M 18.9439849854,7.98849487305 c 0.0,0.0 0.0,-4.0 0.0,-4.0 c 0.0,0.0 -16.0,-10.0 -16.0,-10.0 c 0.0,0.0 0.0,-11.0 0.0,-11.0 c 0.0,-1.70001220703 -1.30000305176,-3.0 -3.0,-3.0 c -1.69999694824,0.0 -3.0,1.29998779297 -3.0,3.0 c 0.0,0.0 0.0,11.0 0.0,11.0 c 0.0,0.0 -16.0,10.0 -16.0,10.0 c 0.0,0.0 0.0,4.0 0.0,4.0 c 0.0,0.0 16.0,-5.0 16.0,-5.0 c 0.0,0.0 0.0,11.0 0.0,11.0 c 0.0,0.0 -4.0,3.0 -4.0,3.0 c 0.0,0.0 0.0,3.0 0.0,3.0 c 0.0,0.0 7.0,-2.0 7.0,-2.0 c 0.0,0.0 7.0,2.0 7.0,2.0 c 0.0,0.0 0.0,-3.0 0.0,-3.0 c 0.0,0.0 -4.0,-3.0 -4.0,-3.0 c 0.0,0.0 0.0,-11.0 0.0,-11.0 c 0.0,0.0 16.0,5.0 16.0,5.0 Z"
+ android:fillColor="#FFFFFFFF"
+ android:fillAlpha="1" />
+ </group>
+ </group>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_airplane_disable_animation.xml b/packages/SystemUI/res/drawable/ic_signal_airplane_disable_animation.xml
new file mode 100644
index 0000000..3e838e2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_signal_airplane_disable_animation.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/ic_signal_airplane_disable" >
+ <target
+ android:name="root"
+ android:animation="@anim/ic_signal_airplane_disable_animation_root" />
+ <target
+ android:name="ic_signal_airplane"
+ android:animation="@anim/ic_signal_airplane_disable_animation_ic_signal_airplane" />
+ <target
+ android:name="mask"
+ android:animation="@anim/ic_signal_airplane_disable_animation_mask" />
+ <target
+ android:name="cross_1"
+ android:animation="@anim/ic_signal_airplane_disable_animation_cross_1" />
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_airplane_enable.xml b/packages/SystemUI/res/drawable/ic_signal_airplane_enable.xml
new file mode 100644
index 0000000..ea3a15b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_signal_airplane_enable.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="root"
+ android:alpha="0.3"
+ android:height="48dp"
+ android:width="48dp"
+ android:viewportHeight="48"
+ android:viewportWidth="48" >
+ <group
+ android:name="ic_signal_airplane"
+ android:translateX="21.9995"
+ android:translateY="25.73401"
+ android:alpha="0.3" >
+ <group
+ android:name="ic_signal_airplane_pivot"
+ android:translateX="-23.21545"
+ android:translateY="-18.86649" >
+ <clip-path
+ android:name="mask"
+ android:pathData="M 37.8337860107,-40.3974914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 40.9884796143,40.9278411865 40.9884796143,40.9278411865 c 0.0,0.0 -2.61700439453,2.0938873291 -2.61700439453,2.0938873291 c 0.0,0.0 -41.1884460449,-40.9392852783 -41.1884460449,-40.9392852783 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z" />
+ <group
+ android:name="cross" >
+ <path
+ android:name="cross_1"
+ android:pathData="M 7.54049682617,3.9430847168 c 0.0,0.0 31.5749816895,31.4499664307 31.5749816895,31.4499664307 "
+ android:strokeColor="#FFFFFFFF"
+ android:strokeAlpha="1"
+ android:strokeWidth="3.5"
+ android:fillColor="#00000000" />
+ </group>
+ <group
+ android:name="plane"
+ android:translateX="23.481"
+ android:translateY="18.71151" >
+ <path
+ android:name="plane_1"
+ android:pathData="M 18.9439849854,7.98849487305 c 0.0,0.0 0.0,-4.0 0.0,-4.0 c 0.0,0.0 -16.0,-10.0 -16.0,-10.0 c 0.0,0.0 0.0,-11.0 0.0,-11.0 c 0.0,-1.70001220703 -1.30000305176,-3.0 -3.0,-3.0 c -1.69999694824,0.0 -3.0,1.29998779297 -3.0,3.0 c 0.0,0.0 0.0,11.0 0.0,11.0 c 0.0,0.0 -16.0,10.0 -16.0,10.0 c 0.0,0.0 0.0,4.0 0.0,4.0 c 0.0,0.0 16.0,-5.0 16.0,-5.0 c 0.0,0.0 0.0,11.0 0.0,11.0 c 0.0,0.0 -4.0,3.0 -4.0,3.0 c 0.0,0.0 0.0,3.0 0.0,3.0 c 0.0,0.0 7.0,-2.0 7.0,-2.0 c 0.0,0.0 7.0,2.0 7.0,2.0 c 0.0,0.0 0.0,-3.0 0.0,-3.0 c 0.0,0.0 -4.0,-3.0 -4.0,-3.0 c 0.0,0.0 0.0,-11.0 0.0,-11.0 c 0.0,0.0 16.0,5.0 16.0,5.0 Z"
+ android:fillColor="#FFFFFFFF"
+ android:fillAlpha="1" />
+ </group>
+ </group>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_airplane_enable_animation.xml b/packages/SystemUI/res/drawable/ic_signal_airplane_enable_animation.xml
new file mode 100644
index 0000000..d64b199
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_signal_airplane_enable_animation.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/ic_signal_airplane_enable" >
+ <target
+ android:name="root"
+ android:animation="@anim/ic_signal_airplane_enable_animation_root" />
+ <target
+ android:name="ic_signal_airplane"
+ android:animation="@anim/ic_signal_airplane_enable_animation_ic_signal_airplane" />
+ <target
+ android:name="mask"
+ android:animation="@anim/ic_signal_airplane_enable_animation_mask" />
+ <target
+ android:name="cross_1"
+ android:animation="@anim/ic_signal_airplane_enable_animation_cross_1" />
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_flashlight_disable.xml b/packages/SystemUI/res/drawable/ic_signal_flashlight_disable.xml
new file mode 100644
index 0000000..35844b7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_signal_flashlight_disable.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="root"
+ android:alpha="1.0"
+ android:height="48dp"
+ android:width="48dp"
+ android:viewportHeight="48"
+ android:viewportWidth="48" >
+ <group
+ android:name="ic_signal_flashlight"
+ android:translateX="21.9995"
+ android:translateY="25.73401" >
+ <group
+ android:name="ic_signal_flashlight_pivot"
+ android:translateX="-23.21545"
+ android:translateY="-18.86649" >
+ <clip-path
+ android:name="mask"
+ android:pathData="M 37.8337860107,-39.2849731445 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 9.55097961426,9.55285644531 9.55097961426,9.55285644531 c 0.0,0.0 -2.61698913574,2.09387207031 -2.61698913574,2.09387207031 c 0.0,0.0 -9.75096130371,-9.56428527832 -9.75096130371,-9.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z" />
+ <group
+ android:name="cross" >
+ <path
+ android:name="cross_1"
+ android:pathData="M 8.34049987793,5.6930847168 c 0.0,0.0 0.274993896484,0.29997253418 0.274993896484,0.29997253418 "
+ android:strokeColor="#FFFFFFFF"
+ android:strokeAlpha="0"
+ android:strokeWidth="3.5"
+ android:fillColor="#00000000" />
+ </group>
+ <group
+ android:name="flashlight"
+ android:translateX="25.06235"
+ android:translateY="22.48294" >
+ <path
+ android:name="light"
+ android:pathData="M -9.40809631348,-23.6970062256 c 0.0,0.0 18.8699951172,0.0 18.8699951172,0.0 c 0.0,0.0 0.0,3.91700744629 0.0,3.91700744629 c 0.0,0.0 -18.8699951172,0.0 -18.8699951172,0.0 c 0.0,0.0 0.0,-3.91700744629 0.0,-3.91700744629 Z M 9.4615020752,-15.6629943848 c 0.0,0.0 0.0,-0.639999389648 0.0,-0.639999389649 c 0.0,0.0 -18.8699951172,0.0 -18.8699951172,0.0 c 0.0,0.0 0.0,0.639999389648 0.0,0.639999389649 c 0.0,0.0 -0.581008911133,5.18899536133 3.82598876953,7.52299499512 c 0.0,0.0 0.0,24.4429931641 0.0,24.4429931641 c 0.0,0.0 11.2129974365,0.0 11.2129974365,0.0 c 0.0,0.0 0.0,-24.4769897461 0.0,-24.4769897461 c 4.35900878906,-2.35301208496 3.83100891113,-7.48899841309 3.83100891113,-7.48899841309 Z M 0.0234985351562,0 c -1.52299499512,0 -2.75700378418,-1.23399353027 -2.75700378418,-2.75700378418 c 0.0,-1.52299499512 1.23400878906,-2.75700378418 2.75700378418,-2.75700378418 c 1.52299499512,0.0 2.75700378418,1.23400878906 2.75700378418,2.75700378418 c 0.0,1.52200317383 -1.23400878906,2.75700378418 -2.75700378418,2.75700378418 Z"
+ android:fillColor="#FFFFFFFF"
+ android:fillAlpha="1" />
+ </group>
+ </group>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_flashlight_disable_animation.xml b/packages/SystemUI/res/drawable/ic_signal_flashlight_disable_animation.xml
new file mode 100644
index 0000000..6d787ab
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_signal_flashlight_disable_animation.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/ic_signal_flashlight_disable" >
+ <target
+ android:name="root"
+ android:animation="@anim/ic_signal_flashlight_disable_animation_root" />
+ <target
+ android:name="ic_signal_flashlight"
+ android:animation="@anim/ic_signal_flashlight_disable_animation_ic_signal_flashlight" />
+ <target
+ android:name="mask"
+ android:animation="@anim/ic_signal_flashlight_disable_animation_mask" />
+ <target
+ android:name="cross_1"
+ android:animation="@anim/ic_signal_flashlight_disable_animation_cross_1" />
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_flashlight_enable.xml b/packages/SystemUI/res/drawable/ic_signal_flashlight_enable.xml
new file mode 100644
index 0000000..93e97ee
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_signal_flashlight_enable.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="root"
+ android:alpha="0.3"
+ android:height="48dp"
+ android:width="48dp"
+ android:viewportHeight="48"
+ android:viewportWidth="48" >
+ <group
+ android:name="ic_signal_flashlight"
+ android:translateX="21.9995"
+ android:translateY="25.73401"
+ android:alpha="0.3" >
+ <group
+ android:name="ic_signal_flashlight_pivot"
+ android:translateX="-23.21545"
+ android:translateY="-18.86649" >
+ <clip-path
+ android:name="mask"
+ android:pathData="M 37.8337860107,-39.2975769043 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 40.9884796143,40.9278411865 40.9884796143,40.9278411865 c 0.0,0.0 -2.61700439453,2.0938873291 -2.61700439453,2.0938873291 c 0.0,0.0 -41.1884460449,-40.9392852783 -41.1884460449,-40.9392852783 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z" />
+ <group
+ android:name="cross" >
+ <path
+ android:name="cross_1"
+ android:pathData="M 8.34049987793,5.6930847168 c 0.0,0.0 29.7749786377,29.7999725342 29.7749786377,29.7999725342 "
+ android:strokeColor="#FFFFFFFF"
+ android:strokeAlpha="1"
+ android:strokeWidth="3.5"
+ android:fillColor="#00000000" />
+ </group>
+ <group
+ android:name="flashlight"
+ android:translateX="25.06235"
+ android:translateY="22.48294" >
+ <path
+ android:name="light"
+ android:pathData="M -9.40809631348,-23.6970062256 c 0.0,0.0 18.8699951172,0.0 18.8699951172,0.0 c 0.0,0.0 0.0,3.91700744629 0.0,3.91700744629 c 0.0,0.0 -18.8699951172,0.0 -18.8699951172,0.0 c 0.0,0.0 0.0,-3.91700744629 0.0,-3.91700744629 Z M 9.4615020752,-15.6629943848 c 0.0,0.0 0.0,-0.639999389648 0.0,-0.639999389649 c 0.0,0.0 -18.8699951172,0.0 -18.8699951172,0.0 c 0.0,0.0 0.0,0.639999389648 0.0,0.639999389649 c 0.0,0.0 -0.581008911133,5.18899536133 3.82598876953,7.52299499512 c 0.0,0.0 0.0,24.4429931641 0.0,24.4429931641 c 0.0,0.0 11.2129974365,0.0 11.2129974365,0.0 c 0.0,0.0 0.0,-24.4769897461 0.0,-24.4769897461 c 4.35900878906,-2.35301208496 3.83100891113,-7.48899841309 3.83100891113,-7.48899841309 Z M 0.0234985351562,0 c -1.52299499512,0 -2.75700378418,-1.23399353027 -2.75700378418,-2.75700378418 c 0.0,-1.52299499512 1.23400878906,-2.75700378418 2.75700378418,-2.75700378418 c 1.52299499512,0.0 2.75700378418,1.23400878906 2.75700378418,2.75700378418 c 0.0,1.52200317383 -1.23400878906,2.75700378418 -2.75700378418,2.75700378418 Z"
+ android:fillColor="#FFFFFFFF"
+ android:fillAlpha="1" />
+ </group>
+ </group>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_flashlight_enable_animation.xml b/packages/SystemUI/res/drawable/ic_signal_flashlight_enable_animation.xml
new file mode 100644
index 0000000..dcfba7d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_signal_flashlight_enable_animation.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/ic_signal_flashlight_enable" >
+ <target
+ android:name="root"
+ android:animation="@anim/ic_signal_flashlight_enable_animation_root" />
+ <target
+ android:name="ic_signal_flashlight"
+ android:animation="@anim/ic_signal_flashlight_enable_animation_ic_signal_flashlight" />
+ <target
+ android:name="mask"
+ android:animation="@anim/ic_signal_flashlight_enable_animation_mask" />
+ <target
+ android:name="cross_1"
+ android:animation="@anim/ic_signal_flashlight_enable_animation_cross_1" />
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_location_disable.xml b/packages/SystemUI/res/drawable/ic_signal_location_disable.xml
new file mode 100644
index 0000000..439851d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_signal_location_disable.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="root"
+ android:alpha="1.0"
+ android:height="48dp"
+ android:width="48dp"
+ android:viewportHeight="48"
+ android:viewportWidth="48" >
+ <group
+ android:name="ic_signal_location"
+ android:translateX="21.9995"
+ android:translateY="25.73401" >
+ <group
+ android:name="ic_signal_location_pivot"
+ android:translateX="-23.21545"
+ android:translateY="-18.86649" >
+ <clip-path
+ android:name="mask"
+ android:pathData="M 38.8337860107,-40.4599914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 7.3759765625,7.55284118652 7.3759765625,7.55284118652 c 0.0,0.0 -2.61698913574,2.0938873291 -2.61698913574,2.0938873291 c 0.0,0.0 -7.57595825195,-7.56428527832 -7.57595825195,-7.56428527832 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z" />
+ <group
+ android:name="cross" >
+ <path
+ android:name="cross_1"
+ android:pathData="M 6.44050598145,1.9430847168 c 0.0,0.0 0.374984741211,0.399978637695 0.374984741211,0.399978637695 "
+ android:strokeColor="#FFFFFFFF"
+ android:strokeAlpha="0"
+ android:strokeWidth="3.5"
+ android:fillColor="#00000000" />
+ </group>
+ <group
+ android:name="location"
+ android:translateX="23.481"
+ android:translateY="18.71151" >
+ <path
+ android:name="pin"
+ android:pathData="M 1.76899719238,-20.011505127 c -7.69999694824,0.0 -14.0,6.30000305176 -14.0,14.0 c 0.0,10.5 14.0,26.0 14.0,26.0 c 0.0,0.0 14.0,-15.5 14.0,-26.0 c 0.0,-7.69999694824 -6.30000305176,-14.0 -14.0,-14.0 Z M 1.76899719238,-1.01150512695 c -2.80000305176,0.0 -5.0,-2.19999694824 -5.0,-5.0 c 0.0,-2.80000305176 2.19999694824,-5.0 5.0,-5.0 c 2.80000305176,0.0 5.0,2.19999694824 5.0,5.0 c 0.0,2.80000305176 -2.19999694824,5.0 -5.0,5.0 Z"
+ android:fillColor="#FFFFFFFF"
+ android:fillAlpha="1" />
+ </group>
+ </group>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_location_disable_animation.xml b/packages/SystemUI/res/drawable/ic_signal_location_disable_animation.xml
new file mode 100644
index 0000000..a219c54
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_signal_location_disable_animation.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/ic_signal_location_disable" >
+ <target
+ android:name="root"
+ android:animation="@anim/ic_signal_location_disable_animation_root" />
+ <target
+ android:name="ic_signal_location"
+ android:animation="@anim/ic_signal_location_disable_animation_ic_signal_location" />
+ <target
+ android:name="mask"
+ android:animation="@anim/ic_signal_location_disable_animation_mask" />
+ <target
+ android:name="cross_1"
+ android:animation="@anim/ic_signal_location_disable_animation_cross_1" />
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_location_enable.xml b/packages/SystemUI/res/drawable/ic_signal_location_enable.xml
new file mode 100644
index 0000000..c800ef4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_signal_location_enable.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="root"
+ android:alpha="0.3"
+ android:height="48dp"
+ android:width="48dp"
+ android:viewportHeight="48"
+ android:viewportWidth="48" >
+ <group
+ android:name="ic_signal_location"
+ android:translateX="21.9995"
+ android:translateY="25.73401"
+ android:alpha="0.3" >
+ <group
+ android:name="ic_signal_location_pivot"
+ android:translateX="-23.21545"
+ android:translateY="-18.86649" >
+ <clip-path
+ android:name="mask"
+ android:pathData="M 38.8337860107,-40.3974914551 c 0.0,0.0 -35.8077850342,31.5523681641 -35.8077850342,31.5523681641 c 0.0,0.0 40.9884796143,41.1153411865 40.9884796143,41.1153411865 c 0.0,0.0 -2.61700439453,2.0938873291 -2.61700439453,2.0938873291 c 0.0,0.0 -41.1884460449,-41.1267852783 -41.1884460449,-41.1267852783 c 0.0,0.0 -34.6200408936,25.4699249268 -34.6200408936,25.4699249268 c 0.0,0.0 55.9664764404,69.742401123 55.9664764404,69.742401123 c 0.0,0.0 73.2448120117,-59.1047973633 73.2448120117,-59.1047973633 c 0.0,0.0 -55.9664916992,-69.7423400879 -55.9664916992,-69.7423400879 Z" />
+ <group
+ android:name="cross" >
+ <path
+ android:name="cross_1"
+ android:pathData="M 6.44050598145,1.9430847168 c 0.0,0.0 33.5749816895,33.4499664307 33.5749816895,33.4499664307 "
+ android:strokeColor="#FFFFFFFF"
+ android:strokeAlpha="1"
+ android:strokeWidth="3.5"
+ android:fillColor="#00000000" />
+ </group>
+ <group
+ android:name="location"
+ android:translateX="23.481"
+ android:translateY="18.71151" >
+ <path
+ android:name="pin"
+ android:pathData="M 1.76899719238,-20.011505127 c -7.69999694824,0.0 -14.0,6.30000305176 -14.0,14.0 c 0.0,10.5 14.0,26.0 14.0,26.0 c 0.0,0.0 14.0,-15.5 14.0,-26.0 c 0.0,-7.69999694824 -6.30000305176,-14.0 -14.0,-14.0 Z M 1.76899719238,-1.01150512695 c -2.80000305176,0.0 -5.0,-2.19999694824 -5.0,-5.0 c 0.0,-2.80000305176 2.19999694824,-5.0 5.0,-5.0 c 2.80000305176,0.0 5.0,2.19999694824 5.0,5.0 c 0.0,2.80000305176 -2.19999694824,5.0 -5.0,5.0 Z"
+ android:fillColor="#FFFFFFFF"
+ android:fillAlpha="1" />
+ </group>
+ </group>
+ </group>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_signal_location_enable_animation.xml b/packages/SystemUI/res/drawable/ic_signal_location_enable_animation.xml
new file mode 100644
index 0000000..bbc1d73
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_signal_location_enable_animation.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/ic_signal_location_enable" >
+ <target
+ android:name="root"
+ android:animation="@anim/ic_signal_location_enable_animation_root" />
+ <target
+ android:name="ic_signal_location"
+ android:animation="@anim/ic_signal_location_enable_animation_ic_signal_location" />
+ <target
+ android:name="mask"
+ android:animation="@anim/ic_signal_location_enable_animation_mask" />
+ <target
+ android:name="cross_1"
+ android:animation="@anim/ic_signal_location_enable_animation_cross_1" />
+</animated-vector>
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml b/packages/SystemUI/res/interpolator/ic_invert_colors_disable_cross_1_pathdata_interpolator.xml
similarity index 79%
copy from tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml
copy to packages/SystemUI/res/interpolator/ic_invert_colors_disable_cross_1_pathdata_interpolator.xml
index 3ebee0b..bc0442f 100644
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml
+++ b/packages/SystemUI/res/interpolator/ic_invert_colors_disable_cross_1_pathdata_interpolator.xml
@@ -1,4 +1,6 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,4 +15,4 @@
limitations under the License.
-->
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.000100000000051,0.0 0.83333333333,1.0 1.0,1.0" />
+ android:pathData="M 0,0 c 0.166666667,0 0.2,1 1,1" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml b/packages/SystemUI/res/interpolator/ic_invert_colors_enable_cross_1_pathdata_interpolator.xml
similarity index 79%
copy from tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml
copy to packages/SystemUI/res/interpolator/ic_invert_colors_enable_cross_1_pathdata_interpolator.xml
index 3ebee0b..bc90d28 100644
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml
+++ b/packages/SystemUI/res/interpolator/ic_invert_colors_enable_cross_1_pathdata_interpolator.xml
@@ -1,4 +1,6 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,4 +15,4 @@
limitations under the License.
-->
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.000100000000051,0.0 0.83333333333,1.0 1.0,1.0" />
+ android:pathData="M 0,0 c 0.8,0 0.833333333,1 1,1" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml b/packages/SystemUI/res/interpolator/ic_invert_colors_enable_mask_pathdata_interpolator.xml
similarity index 80%
rename from tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml
rename to packages/SystemUI/res/interpolator/ic_invert_colors_enable_mask_pathdata_interpolator.xml
index 3ebee0b..f7072f2 100644
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml
+++ b/packages/SystemUI/res/interpolator/ic_invert_colors_enable_mask_pathdata_interpolator.xml
@@ -1,4 +1,6 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,4 +15,4 @@
limitations under the License.
-->
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.000100000000051,0.0 0.83333333333,1.0 1.0,1.0" />
+ android:pathData="M 0,0 c 0.8,0 0.6,1 1,1" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml b/packages/SystemUI/res/interpolator/ic_signal_airplane_disable_cross_1_pathdata_interpolator.xml
similarity index 79%
copy from tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml
copy to packages/SystemUI/res/interpolator/ic_signal_airplane_disable_cross_1_pathdata_interpolator.xml
index 3ebee0b..bc0442f 100644
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml
+++ b/packages/SystemUI/res/interpolator/ic_signal_airplane_disable_cross_1_pathdata_interpolator.xml
@@ -1,4 +1,6 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,4 +15,4 @@
limitations under the License.
-->
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.000100000000051,0.0 0.83333333333,1.0 1.0,1.0" />
+ android:pathData="M 0,0 c 0.166666667,0 0.2,1 1,1" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml b/packages/SystemUI/res/interpolator/ic_signal_airplane_enable_cross_1_pathdata_interpolator.xml
similarity index 79%
copy from tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml
copy to packages/SystemUI/res/interpolator/ic_signal_airplane_enable_cross_1_pathdata_interpolator.xml
index 3ebee0b..bc90d28 100644
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml
+++ b/packages/SystemUI/res/interpolator/ic_signal_airplane_enable_cross_1_pathdata_interpolator.xml
@@ -1,4 +1,6 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,4 +15,4 @@
limitations under the License.
-->
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.000100000000051,0.0 0.83333333333,1.0 1.0,1.0" />
+ android:pathData="M 0,0 c 0.8,0 0.833333333,1 1,1" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml b/packages/SystemUI/res/interpolator/ic_signal_airplane_enable_mask_pathdata_interpolator.xml
similarity index 80%
copy from tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml
copy to packages/SystemUI/res/interpolator/ic_signal_airplane_enable_mask_pathdata_interpolator.xml
index 3ebee0b..f7072f2 100644
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml
+++ b/packages/SystemUI/res/interpolator/ic_signal_airplane_enable_mask_pathdata_interpolator.xml
@@ -1,4 +1,6 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,4 +15,4 @@
limitations under the License.
-->
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.000100000000051,0.0 0.83333333333,1.0 1.0,1.0" />
+ android:pathData="M 0,0 c 0.8,0 0.6,1 1,1" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml b/packages/SystemUI/res/interpolator/ic_signal_flashlight_enable_cross_1_pathdata_interpolator.xml
similarity index 80%
copy from tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml
copy to packages/SystemUI/res/interpolator/ic_signal_flashlight_enable_cross_1_pathdata_interpolator.xml
index 3ebee0b..f7072f2 100644
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml
+++ b/packages/SystemUI/res/interpolator/ic_signal_flashlight_enable_cross_1_pathdata_interpolator.xml
@@ -1,4 +1,6 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,4 +15,4 @@
limitations under the License.
-->
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.000100000000051,0.0 0.83333333333,1.0 1.0,1.0" />
+ android:pathData="M 0,0 c 0.8,0 0.6,1 1,1" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml b/packages/SystemUI/res/interpolator/ic_signal_flashlight_enable_mask_pathdata_interpolator.xml
similarity index 80%
copy from tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml
copy to packages/SystemUI/res/interpolator/ic_signal_flashlight_enable_mask_pathdata_interpolator.xml
index 3ebee0b..f7072f2 100644
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml
+++ b/packages/SystemUI/res/interpolator/ic_signal_flashlight_enable_mask_pathdata_interpolator.xml
@@ -1,4 +1,6 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,4 +15,4 @@
limitations under the License.
-->
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.000100000000051,0.0 0.83333333333,1.0 1.0,1.0" />
+ android:pathData="M 0,0 c 0.8,0 0.6,1 1,1" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml b/packages/SystemUI/res/interpolator/ic_signal_location_enable_cross_1_pathdata_interpolator.xml
similarity index 80%
copy from tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml
copy to packages/SystemUI/res/interpolator/ic_signal_location_enable_cross_1_pathdata_interpolator.xml
index 3ebee0b..f7072f2 100644
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml
+++ b/packages/SystemUI/res/interpolator/ic_signal_location_enable_cross_1_pathdata_interpolator.xml
@@ -1,4 +1,6 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,4 +15,4 @@
limitations under the License.
-->
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.000100000000051,0.0 0.83333333333,1.0 1.0,1.0" />
+ android:pathData="M 0,0 c 0.8,0 0.6,1 1,1" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml b/packages/SystemUI/res/interpolator/ic_signal_location_enable_mask_pathdata_interpolator.xml
similarity index 80%
copy from tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml
copy to packages/SystemUI/res/interpolator/ic_signal_location_enable_mask_pathdata_interpolator.xml
index 3ebee0b..f7072f2 100644
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_3.xml
+++ b/packages/SystemUI/res/interpolator/ic_signal_location_enable_mask_pathdata_interpolator.xml
@@ -1,4 +1,6 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,4 +15,4 @@
limitations under the License.
-->
<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.000100000000051,0.0 0.83333333333,1.0 1.0,1.0" />
+ android:pathData="M 0,0 c 0.8,0 0.6,1 1,1" />
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6e6f302..51ef0c3 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -63,6 +63,8 @@
<!-- thickness (height) of the navigation bar on phones that require it -->
<dimen name="navigation_bar_size">@*android:dimen/navigation_bar_height</dimen>
+ <!-- Minimum swipe distance to catch the swipe gestures to invoke assist or switch tasks. -->
+ <dimen name="navigation_bar_min_swipe_distance">48dp</dimen>
<!-- thickness (height) of the dead zone at the top of the navigation bar,
reducing false presses on navbar buttons; approx 2mm -->
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 54a8414..1ddd352 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -203,7 +203,7 @@
}
}
- private void refreshAllTiles() {
+ public void refreshAllTiles() {
for (TileRecord r : mRecords) {
r.tile.refreshState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index 6b7bdd6..3d0f47c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -63,6 +63,7 @@
private boolean mDual;
private OnClickListener mClickPrimary;
private OnClickListener mClickSecondary;
+ private Drawable mTileBackground;
private RippleDrawable mRipple;
public QSTileView(Context context) {
@@ -75,6 +76,7 @@
mTilePaddingBelowIconPx = res.getDimensionPixelSize(R.dimen.qs_tile_padding_below_icon);
mDualTileVerticalPaddingPx =
res.getDimensionPixelSize(R.dimen.qs_dual_tile_padding_vertical);
+ mTileBackground = newTileBackground();
recreateLabel();
setClipChildren(false);
@@ -175,22 +177,21 @@
if (changed) {
recreateLabel();
}
- Drawable tileBackground = getTileBackground();
- if (tileBackground instanceof RippleDrawable) {
- setRipple((RippleDrawable) tileBackground);
+ if (mTileBackground instanceof RippleDrawable) {
+ setRipple((RippleDrawable) mTileBackground);
}
if (dual) {
mTopBackgroundView.setOnClickListener(mClickPrimary);
setOnClickListener(null);
setClickable(false);
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
- mTopBackgroundView.setBackground(tileBackground);
+ mTopBackgroundView.setBackground(mTileBackground);
} else {
mTopBackgroundView.setOnClickListener(null);
mTopBackgroundView.setClickable(false);
setOnClickListener(mClickPrimary);
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
- setBackground(tileBackground);
+ setBackground(mTileBackground);
}
mTopBackgroundView.setFocusable(dual);
setFocusable(!dual);
@@ -217,7 +218,7 @@
return icon;
}
- private Drawable getTileBackground() {
+ private Drawable newTileBackground() {
final int[] attrs = new int[] { android.R.attr.selectableItemBackgroundBorderless };
final TypedArray ta = mContext.obtainStyledAttributes(attrs);
final Drawable d = ta.getDrawable(0);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index cd44a2b..2dd02a5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -29,6 +29,10 @@
/** Quick settings tile: Airplane mode **/
public class AirplaneModeTile extends QSTile<QSTile.BooleanState> {
+ private final AnimationIcon mEnable =
+ new AnimationIcon(R.drawable.ic_signal_airplane_enable_animation);
+ private final AnimationIcon mDisable =
+ new AnimationIcon(R.drawable.ic_signal_airplane_disable_animation);
private final GlobalSetting mSetting;
private boolean mListening;
@@ -52,6 +56,8 @@
@Override
public void handleClick() {
setEnabled(!mState.value);
+ mEnable.setAllowAnimation(true);
+ mDisable.setAllowAnimation(true);
}
private void setEnabled(boolean enabled) {
@@ -68,11 +74,11 @@
state.visible = true;
state.label = mContext.getString(R.string.quick_settings_airplane_mode_label);
if (airplaneMode) {
- state.icon = ResourceIcon.get(R.drawable.ic_qs_airplane_on);
+ state.icon = mEnable;
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_airplane_on);
} else {
- state.icon = ResourceIcon.get(R.drawable.ic_qs_airplane_off);
+ state.icon = mDisable;
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_airplane_off);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index b24f8bf..a19c29f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -26,6 +26,10 @@
/** Quick settings tile: Invert colors **/
public class ColorInversionTile extends QSTile<QSTile.BooleanState> {
+ private final AnimationIcon mEnable
+ = new AnimationIcon(R.drawable.ic_invert_colors_enable_animation);
+ private final AnimationIcon mDisable
+ = new AnimationIcon(R.drawable.ic_invert_colors_disable_animation);
private final SecureSetting mSetting;
private final UsageTracker mUsageTracker;
@@ -79,6 +83,8 @@
@Override
protected void handleClick() {
mSetting.setValue(mState.value ? 0 : 1);
+ mEnable.setAllowAnimation(true);
+ mDisable.setAllowAnimation(true);
}
@Override
@@ -88,8 +94,7 @@
state.visible = enabled || mUsageTracker.isRecentlyUsed();
state.value = enabled;
state.label = mContext.getString(R.string.quick_settings_inversion_label);
- state.icon = ResourceIcon.get(enabled ? R.drawable.ic_qs_inversion_on
- : R.drawable.ic_qs_inversion_off);
+ state.icon = enabled ? mEnable : mDisable;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 055a6b7..5c1a317 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -31,6 +31,10 @@
* still available because it was recently on. */
private static final long RECENTLY_ON_DURATION_MILLIS = 500;
+ private final AnimationIcon mEnable
+ = new AnimationIcon(R.drawable.ic_signal_flashlight_enable_animation);
+ private final AnimationIcon mDisable
+ = new AnimationIcon(R.drawable.ic_signal_flashlight_disable_animation);
private final FlashlightController mFlashlightController;
private long mWasLastOn;
@@ -66,7 +70,7 @@
}
boolean newState = !mState.value;
mFlashlightController.setFlashlight(newState);
- refreshState(newState);
+ refreshState(newState ? UserBoolean.USER_TRUE : UserBoolean.USER_FALSE);
}
@Override
@@ -75,8 +79,8 @@
mWasLastOn = SystemClock.uptimeMillis();
}
- if (arg instanceof Boolean) {
- state.value = (Boolean) arg;
+ if (arg instanceof UserBoolean) {
+ state.value = ((UserBoolean) arg).value;
}
if (!state.value && mWasLastOn != 0) {
@@ -92,8 +96,9 @@
// the camera is not available while it is being used for the flashlight.
state.visible = mWasLastOn != 0 || mFlashlightController.isAvailable();
state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
- state.icon = ResourceIcon.get(state.value ? R.drawable.ic_qs_flashlight_on
- : R.drawable.ic_qs_flashlight_off);
+ final AnimationIcon icon = state.value ? mEnable : mDisable;
+ icon.setAllowAnimation(arg instanceof UserBoolean && ((UserBoolean) arg).userInitiated);
+ state.icon = icon;
int onOrOffId = state.value
? R.string.accessibility_quick_settings_flashlight_on
: R.string.accessibility_quick_settings_flashlight_off;
@@ -111,12 +116,12 @@
@Override
public void onFlashlightOff() {
- refreshState(false);
+ refreshState(UserBoolean.BACKGROUND_FALSE);
}
@Override
public void onFlashlightError() {
- refreshState(false);
+ refreshState(UserBoolean.BACKGROUND_FALSE);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 81741ce..11ec722 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -25,6 +25,11 @@
/** Quick settings tile: Location **/
public class LocationTile extends QSTile<QSTile.BooleanState> {
+ private final AnimationIcon mEnable =
+ new AnimationIcon(R.drawable.ic_signal_location_enable_animation);
+ private final AnimationIcon mDisable =
+ new AnimationIcon(R.drawable.ic_signal_location_disable_animation);
+
private final LocationController mController;
private final KeyguardMonitor mKeyguard;
private final Callback mCallback = new Callback();
@@ -55,6 +60,8 @@
protected void handleClick() {
final boolean wasEnabled = (Boolean) mState.value;
mController.setLocationEnabled(!wasEnabled);
+ mEnable.setAllowAnimation(true);
+ mDisable.setAllowAnimation(true);
}
@Override
@@ -67,12 +74,12 @@
state.visible = !mKeyguard.isShowing();
state.value = locationEnabled;
if (locationEnabled) {
- state.icon = ResourceIcon.get(R.drawable.ic_qs_location_on);
+ state.icon = mEnable;
state.label = mContext.getString(R.string.quick_settings_location_label);
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_location_on);
} else {
- state.icon = ResourceIcon.get(R.drawable.ic_qs_location_off);
+ state.icon = mDisable;
state.label = mContext.getString(R.string.quick_settings_location_label);
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_location_off);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 49cd14a..f46b9a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -25,14 +25,14 @@
/** Quick settings tile: Rotation **/
public class RotationLockTile extends QSTile<QSTile.BooleanState> {
- private final AnimationIcon PORTRAIT_TO_AUTO
+ private final AnimationIcon mPortraitToAuto
= new AnimationIcon(R.drawable.ic_portrait_to_auto_rotate_animation);
- private final AnimationIcon AUTO_TO_PORTRAIT
+ private final AnimationIcon mAutoToPortrait
= new AnimationIcon(R.drawable.ic_portrait_from_auto_rotate_animation);
- private final AnimationIcon LANDSCAPE_TO_AUTO
+ private final AnimationIcon mLandscapeToAuto
= new AnimationIcon(R.drawable.ic_landscape_to_auto_rotate_animation);
- private final AnimationIcon AUTO_TO_LANDSCAPE
+ private final AnimationIcon mAutoToLandscape
= new AnimationIcon(R.drawable.ic_landscape_from_auto_rotate_animation);
private final RotationLockController mController;
@@ -79,10 +79,10 @@
final int label = portrait ? R.string.quick_settings_rotation_locked_portrait_label
: R.string.quick_settings_rotation_locked_landscape_label;
state.label = mContext.getString(label);
- icon = portrait ? AUTO_TO_PORTRAIT : AUTO_TO_LANDSCAPE;
+ icon = portrait ? mAutoToPortrait : mAutoToLandscape;
} else {
state.label = mContext.getString(R.string.quick_settings_rotation_unlocked_label);
- icon = portrait ? PORTRAIT_TO_AUTO : LANDSCAPE_TO_AUTO;
+ icon = portrait ? mPortraitToAuto : mLandscapeToAuto;
}
icon.setAllowAnimation(userInitiated);
state.icon = icon;
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 2a782cc..4c3460e 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -728,7 +728,7 @@
final ActivityManager am = (ActivityManager)
getContext().getSystemService(Context.ACTIVITY_SERVICE);
if (am != null) {
- am.removeTask(ad.persistentTaskId, ActivityManager.REMOVE_TASK_KILL_PROCESS);
+ am.removeTask(ad.persistentTaskId);
// Accessibility feedback
setContentDescription(
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 76e8181..be7d322 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -54,7 +54,7 @@
import java.util.concurrent.atomic.AtomicBoolean;
/** A proxy implementation for the recents component */
-public class AlternateRecentsComponent {
+public class AlternateRecentsComponent implements ActivityOptions.OnAnimationStartedListener {
final public static String EXTRA_FROM_HOME = "recents.triggeredOverHome";
final public static String EXTRA_FROM_SEARCH_HOME = "recents.triggeredOverSearchHome";
@@ -62,7 +62,9 @@
final public static String EXTRA_FROM_TASK_ID = "recents.activeTaskId";
final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "recents.triggeredFromAltTab";
final public static String EXTRA_TRIGGERED_FROM_HOME_KEY = "recents.triggeredFromHomeKey";
+ final public static String EXTRA_REUSE_TASK_STACK_VIEWS = "recents.reuseTaskStackViews";
+ final public static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
final public static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
final public static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
@@ -77,7 +79,10 @@
Context mContext;
LayoutInflater mInflater;
SystemServicesProxy mSystemServicesProxy;
+ Handler mHandler;
boolean mBootCompleted;
+ boolean mStartAnimationTriggered;
+ boolean mCanReuseTaskStackViews = true;
// Task launching
RecentsConfiguration mConfig;
@@ -103,6 +108,7 @@
mInflater = LayoutInflater.from(context);
mContext = context;
mSystemServicesProxy = new SystemServicesProxy(context);
+ mHandler = new Handler();
mTaskStackBounds = new Rect();
}
@@ -128,7 +134,7 @@
}
// When we start, preload the metadata associated with the previous tasks
- RecentsTaskLoader.getInstance().preload(mContext);
+ RecentsTaskLoader.getInstance().preload(mContext, RecentsTaskLoader.ALL_TASKS);
}
public void onBootCompleted() {
@@ -176,7 +182,9 @@
}
public void onPreloadRecents() {
- // Do nothing
+ // When we start, preload the metadata associated with the previous tasks
+ RecentsTaskLoader.getInstance().preload(mContext,
+ Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
}
public void onCancelPreloadingRecents() {
@@ -186,7 +194,7 @@
void showRelativeAffiliatedTask(boolean showNextTask) {
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
TaskStack stack = loader.getTaskStack(mSystemServicesProxy, mContext.getResources(),
- -1, -1, false, true, null, null);
+ -1, -1, RecentsTaskLoader.ALL_TASKS, false, true, null, null);
// Return early if there are no tasks
if (stack.getTaskCount() == 0) return;
@@ -251,6 +259,8 @@
}
public void onConfigurationChanged(Configuration newConfig) {
+ // Don't reuse task stack views if the configuration changes
+ mCanReuseTaskStackViews = false;
// Reload the header bar layout
reloadHeaderBarLayout();
}
@@ -364,23 +374,28 @@
* Creates the activity options for a unknown state->recents transition.
*/
ActivityOptions getUnknownTransitionActivityOptions() {
+ mStartAnimationTriggered = false;
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_unknown_enter,
- R.anim.recents_from_unknown_exit);
+ R.anim.recents_from_unknown_exit,
+ mHandler, this);
}
/**
* Creates the activity options for a home->recents transition.
*/
ActivityOptions getHomeTransitionActivityOptions(boolean fromSearchHome) {
+ mStartAnimationTriggered = false;
if (fromSearchHome) {
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_search_launcher_enter,
- R.anim.recents_from_search_launcher_exit);
+ R.anim.recents_from_search_launcher_exit,
+ mHandler, this);
}
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_launcher_enter,
- R.anim.recents_from_launcher_exit);
+ R.anim.recents_from_launcher_exit,
+ mHandler, this);
}
/**
@@ -408,9 +423,10 @@
c.setBitmap(null);
}
+ mStartAnimationTriggered = false;
return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mStatusBarView,
thumbnail, toTaskRect.left, toTaskRect.top, toTaskRect.width(),
- toTaskRect.height(), null);
+ toTaskRect.height(), this);
}
// If both the screenshot and thumbnail fails, then just fall back to the default transition
@@ -423,7 +439,7 @@
// Get the stack of tasks that we are animating into
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
TaskStack stack = loader.getTaskStack(mSystemServicesProxy, mContext.getResources(),
- runningTaskId, -1, false, isTopTaskHome, null, null);
+ runningTaskId, -1, RecentsTaskLoader.ALL_TASKS, false, isTopTaskHome, null, null);
if (stack.getTaskCount() == 0) {
return null;
}
@@ -529,11 +545,13 @@
}
intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, mTriggeredFromAltTab);
intent.putExtra(EXTRA_FROM_TASK_ID, (topTask != null) ? topTask.id : -1);
+ intent.putExtra(EXTRA_REUSE_TASK_STACK_VIEWS, mCanReuseTaskStackViews);
if (opts != null) {
mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
} else {
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
}
+ mCanReuseTaskStackViews = true;
}
/** Sets the RecentsComponent callbacks. */
@@ -547,4 +565,42 @@
sRecentsComponentCallbacks.onVisibilityChanged(visible);
}
}
+
+ /**** OnAnimationStartedListener Implementation ****/
+
+ @Override
+ public void onAnimationStarted() {
+ // Notify recents to start the enter animation
+ if (!mStartAnimationTriggered) {
+ // There can be a race condition between the start animation callback and
+ // the start of the new activity (where we register the receiver that listens
+ // to this broadcast, so we add our own receiver and if that gets called, then
+ // we know the activity has not yet started and we can retry sending the broadcast.
+ BroadcastReceiver fallbackReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (getResultCode() == Activity.RESULT_OK) {
+ mStartAnimationTriggered = true;
+ return;
+ }
+
+ // Schedule for the broadcast to be sent again after some time
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ onAnimationStarted();
+ }
+ }, 25);
+ }
+ };
+
+ // Send the broadcast to notify Recents that the animation has started
+ Intent intent = new Intent(ACTION_START_ENTER_ANIMATION);
+ intent.setPackage(mContext.getPackageName());
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
+ Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
+ fallbackReceiver, null, Activity.RESULT_CANCELED, null, null);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index d2c55f7..f8d981f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -27,6 +27,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
+import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -142,6 +143,12 @@
} else if (action.equals(AlternateRecentsComponent.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
// If we are toggling Recents, then first unfilter any filtered stacks first
dismissRecentsToFocusedTaskOrHome(true);
+ } else if (action.equals(AlternateRecentsComponent.ACTION_START_ENTER_ANIMATION)) {
+ // Trigger the enter animation
+ onEnterAnimationTriggered();
+ // Notify the fallback receiver that we have successfully got the broadcast
+ // See AlternateRecentsComponent.onAnimationStarted()
+ setResultCode(Activity.RESULT_OK);
}
}
};
@@ -157,7 +164,8 @@
// When the screen turns off, dismiss Recents to Home
dismissRecentsToHome(false);
// Start preloading some tasks in the background
- RecentsTaskLoader.getInstance().preload(RecentsActivity.this);
+ RecentsTaskLoader.getInstance().preload(RecentsActivity.this,
+ Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
} else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) {
// When the search activity changes, update the Search widget
refreshSearchWidget();
@@ -188,6 +196,8 @@
AlternateRecentsComponent.EXTRA_FROM_TASK_ID, -1);
mConfig.launchedWithAltTab = launchIntent.getBooleanExtra(
AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false);
+ mConfig.launchedReuseTaskStackViews = launchIntent.getBooleanExtra(
+ AlternateRecentsComponent.EXTRA_REUSE_TASK_STACK_VIEWS, false);
// Load all the tasks
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
@@ -397,8 +407,12 @@
// Update if we are getting a configuration change
if (savedInstanceState != null) {
+ // Update RecentsConfiguration
+ mConfig = RecentsConfiguration.reinitialize(this,
+ RecentsTaskLoader.getInstance().getSystemServicesProxy());
mConfig.updateOnConfigurationChange();
- onConfigurationChange();
+ // Trigger the enter animation
+ onEnterAnimationTriggered();
}
// Start listening for widget package changes if there is one bound, post it since we don't
@@ -428,19 +442,6 @@
}
}
- /** Called when the configuration changes. */
- void onConfigurationChange() {
- // Update RecentsConfiguration
- mConfig = RecentsConfiguration.reinitialize(this,
- RecentsTaskLoader.getInstance().getSystemServicesProxy());
-
- // Try and start the enter animation (or restart it on configuration changed)
- ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
- mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(t));
- // Animate the SystemUI scrim views
- mScrimViews.startEnterRecentsAnimation();
- }
-
/** Handles changes to the activity visibility. */
void onRecentsActivityVisibilityChanged(boolean visible) {
if (!visible) {
@@ -474,6 +475,7 @@
IntentFilter filter = new IntentFilter();
filter.addAction(AlternateRecentsComponent.ACTION_HIDE_RECENTS_ACTIVITY);
filter.addAction(AlternateRecentsComponent.ACTION_TOGGLE_RECENTS_ACTIVITY);
+ filter.addAction(AlternateRecentsComponent.ACTION_START_ENTER_ANIMATION);
registerReceiver(mServiceBroadcastReceiver, filter);
// Register any broadcast receivers for the task loader
@@ -492,8 +494,8 @@
protected void onStop() {
super.onStop();
- // Remove all the views
- mRecentsView.removeAllTaskStacks();
+ // Notify the views that we are no longer visible
+ mRecentsView.onRecentsHidden();
// Unregister the RecentsService receiver
unregisterReceiver(mServiceBroadcastReceiver);
@@ -515,8 +517,7 @@
}
}
- @Override
- public void onEnterAnimationComplete() {
+ public void onEnterAnimationTriggered() {
// Try and start the enter animation (or restart it on configuration changed)
ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(t));
@@ -584,7 +585,6 @@
/** Called when debug mode is triggered */
public void onDebugModeTriggered() {
-
if (mConfig.developerOptionsEnabled) {
SharedPreferences settings = getSharedPreferences(getPackageName(), 0);
if (settings.getBoolean(Constants.Values.App.Key_DebugModeEnabled, false)) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index bfea3f7..e0c76b1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -115,8 +115,8 @@
public boolean launchedWithAltTab;
public boolean launchedWithNoRecentTasks;
public boolean launchedFromAppWithThumbnail;
- public boolean launchedFromAppWithScreenshot;
public boolean launchedFromHome;
+ public boolean launchedReuseTaskStackViews;
public int launchedToTaskId;
/** Misc **/
@@ -308,6 +308,7 @@
launchedWithNoRecentTasks = false;
launchedFromAppWithThumbnail = false;
launchedFromHome = false;
+ launchedReuseTaskStackViews = false;
launchedToTaskId = -1;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
index 4456066..735f79f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/DozeTrigger.java
@@ -82,4 +82,9 @@
public boolean hasTriggered() {
return mHasTriggered;
}
+
+ /** Resets the doze trigger state. */
+ public void resetTrigger() {
+ mHasTriggered = false;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 71a3ef1..b661385 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -291,18 +291,18 @@
}
}
- /** Removes the task and kills the process */
- public void removeTask(int taskId, boolean isDocument) {
+ /** Removes the task */
+ public void removeTask(int taskId) {
if (mAm == null) return;
if (Constants.DebugFlags.App.EnableSystemServicesProxy) return;
- // Remove the task, and only kill the process if it is not a document
- mAm.removeTask(taskId, isDocument ? 0 : ActivityManager.REMOVE_TASK_KILL_PROCESS);
+ // Remove the task.
+ mAm.removeTask(taskId);
}
/**
* Returns the activity info for a given component name.
- *
+ *
* @param cn The component name of the activity.
* @param userId The userId of the user that this is for.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index a0dee07..e1179fa 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -17,7 +17,6 @@
package com.android.systemui.recents.misc;
import android.animation.Animator;
-import android.content.Intent;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Rect;
@@ -184,12 +183,6 @@
sPropertyMethod.invoke(null, property, value);
}
- /** Returns whether the specified intent is a document. */
- public static boolean isDocument(Intent intent) {
- int flags = intent.getFlags();
- return (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) == Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
- }
-
/**
* Cancels an animation ensuring that if it has listeners, onCancel and onEnd
* are not called.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index b4f62d5..390507f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -260,6 +260,7 @@
private static final String TAG = "RecentsTaskLoader";
static RecentsTaskLoader sInstance;
+ public static final int ALL_TASKS = -1;
SystemServicesProxy mSystemServicesProxy;
DrawableLruCache mApplicationIconCache;
@@ -326,10 +327,9 @@
/** Gets the list of recent tasks, ordered from back to front. */
private static List<ActivityManager.RecentTaskInfo> getRecentTasks(SystemServicesProxy ssp,
- boolean isTopTaskHome) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
+ int numTasksToLoad, boolean isTopTaskHome) {
List<ActivityManager.RecentTaskInfo> tasks =
- ssp.getRecentTasks(config.maxNumTasksToLoad, UserHandle.CURRENT.getIdentifier(),
+ ssp.getRecentTasks(numTasksToLoad, UserHandle.CURRENT.getIdentifier(),
isTopTaskHome);
Collections.reverse(tasks);
return tasks;
@@ -416,7 +416,8 @@
ArrayList<Task.TaskKey> taskKeys = new ArrayList<Task.TaskKey>();
ArrayList<Task> tasksToLoad = new ArrayList<Task>();
TaskStack stack = getTaskStack(mSystemServicesProxy, context.getResources(),
- -1, preloadCount, true, isTopTaskHome, taskKeys, tasksToLoad);
+ -1, preloadCount, RecentsTaskLoader.ALL_TASKS, true, isTopTaskHome, taskKeys,
+ tasksToLoad);
SpaceNode root = new SpaceNode();
root.setStack(stack);
@@ -428,10 +429,10 @@
}
/** Preloads the set of recent tasks (not including thumbnails). */
- public void preload(Context context) {
+ public void preload(Context context, int numTasksToPreload) {
ArrayList<Task> tasksToLoad = new ArrayList<Task>();
getTaskStack(mSystemServicesProxy, context.getResources(),
- -1, -1, true, true, null, tasksToLoad);
+ -1, -1, numTasksToPreload, true, true, null, tasksToLoad);
// Start the task loader and add all the tasks we need to load
mLoadQueue.addTasks(tasksToLoad);
@@ -440,11 +441,13 @@
/** Creates a lightweight stack of the current recent tasks, without thumbnails and icons. */
public synchronized TaskStack getTaskStack(SystemServicesProxy ssp, Resources res,
- int preloadTaskId, int preloadTaskCount,
+ int preloadTaskId, int preloadTaskCount, int loadTaskCount,
boolean loadTaskThumbnails, boolean isTopTaskHome,
List<Task.TaskKey> taskKeysOut, List<Task> tasksToLoadOut) {
RecentsConfiguration config = RecentsConfiguration.getInstance();
- List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp, isTopTaskHome);
+ List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp,
+ (loadTaskCount == ALL_TASKS ? config.maxNumTasksToLoad : loadTaskCount),
+ isTopTaskHome);
HashMap<Task.ComponentNameKey, ActivityInfoHandle> activityInfoCache =
new HashMap<Task.ComponentNameKey, ActivityInfoHandle>();
ArrayList<Task> tasksToAdd = new ArrayList<Task>();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 6b0d306..81ee839 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -34,7 +34,6 @@
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.RecentsPackageMonitor;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
@@ -96,42 +95,57 @@
/** Set/get the bsp root node */
public void setTaskStacks(ArrayList<TaskStack> stacks) {
- // Remove all TaskStackViews (but leave the search bar)
+ int numStacks = stacks.size();
+
+ // Make a list of the stack view children only
+ ArrayList<TaskStackView> stackViews = new ArrayList<TaskStackView>();
int childCount = getChildCount();
- for (int i = childCount - 1; i >= 0; i--) {
- View v = getChildAt(i);
- if (v != mSearchBar) {
- removeViewAt(i);
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child != mSearchBar) {
+ stackViews.add((TaskStackView) child);
}
}
- // Create and add all the stacks for this partition of space.
+ // Remove all/extra stack views
+ int numTaskStacksToKeep = 0; // Keep no tasks if we are recreating the layout
+ if (mConfig.launchedReuseTaskStackViews) {
+ numTaskStacksToKeep = Math.min(childCount, numStacks);
+ }
+ for (int i = stackViews.size() - 1; i >= numTaskStacksToKeep; i--) {
+ removeView(stackViews.get(i));
+ stackViews.remove(i);
+ }
+
+ // Update the stack views that we are keeping
+ for (int i = 0; i < numTaskStacksToKeep; i++) {
+ stackViews.get(i).setStack(stacks.get(i));
+ }
+
+ // Add remaining/recreate stack views
mStacks = stacks;
- int numStacks = mStacks.size();
- for (int i = 0; i < numStacks; i++) {
- TaskStack stack = mStacks.get(i);
+ for (int i = stackViews.size(); i < numStacks; i++) {
+ TaskStack stack = stacks.get(i);
TaskStackView stackView = new TaskStackView(getContext(), stack);
stackView.setCallbacks(this);
- // Enable debug mode drawing
- if (mConfig.debugModeEnabled) {
- stackView.setDebugOverlay(mDebugOverlay);
- }
addView(stackView);
}
+ // Enable debug mode drawing on all the stacks if necessary
+ if (mConfig.debugModeEnabled) {
+ for (int i = childCount - 1; i >= 0; i--) {
+ View v = getChildAt(i);
+ if (v != mSearchBar) {
+ TaskStackView stackView = (TaskStackView) v;
+ stackView.setDebugOverlay(mDebugOverlay);
+ }
+ }
+ }
+
// Reset the launched state
mAlreadyLaunchingTask = false;
- }
-
- /** Removes all the task stack views from this recents view. */
- public void removeAllTaskStacks() {
- int childCount = getChildCount();
- for (int i = childCount - 1; i >= 0; i--) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- removeViewAt(i);
- }
- }
+ // Trigger a new layout
+ requestLayout();
}
/** Launches the focused task from the first stack if possible */
@@ -522,8 +536,7 @@
loader.deleteTaskData(t, false);
// Remove the old task from activity manager
- RecentsTaskLoader.getInstance().getSystemServicesProxy().removeTask(t.key.id,
- Utilities.isDocument(t.key.baseIntent));
+ RecentsTaskLoader.getInstance().getSystemServicesProxy().removeTask(t.key.id);
}
@Override
@@ -531,6 +544,19 @@
mCb.onAllTaskViewsDismissed();
}
+ /** Final callback after Recents is finally hidden. */
+ public void onRecentsHidden() {
+ // Notify each task stack view
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child != mSearchBar) {
+ TaskStackView stackView = (TaskStackView) child;
+ stackView.onRecentsHidden();
+ }
+ }
+ }
+
@Override
public void onTaskStackFilterTriggered() {
// Hide the search bar
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index dee26e6..9df0db6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -40,6 +40,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
/* The visual representation of a task stack view */
@@ -99,25 +100,11 @@
}
};
- // A convenience runnable to return all views to the pool
- Runnable mReturnAllViewsToPoolRunnable = new Runnable() {
- @Override
- public void run() {
- int childCount = getChildCount();
- for (int i = childCount - 1; i >= 0; i--) {
- TaskView tv = (TaskView) getChildAt(i);
- mViewPool.returnViewToPool(tv);
- // Also hide the view since we don't need it anymore
- tv.setVisibility(View.INVISIBLE);
- }
- }
- };
-
public TaskStackView(Context context, TaskStack stack) {
super(context);
+ // Set the stack first
+ setStack(stack);
mConfig = RecentsConfiguration.getInstance();
- mStack = stack;
- mStack.setCallbacks(this);
mViewPool = new ViewPool<TaskView, Task>(context, this);
mInflater = LayoutInflater.from(context);
mLayoutAlgorithm = new TaskStackViewLayoutAlgorithm(mConfig);
@@ -143,11 +130,62 @@
mCb = cb;
}
+ /** Sets the task stack */
+ void setStack(TaskStack stack) {
+ // Unset the old stack
+ if (mStack != null) {
+ mStack.setCallbacks(null);
+
+ // Return all existing views to the pool
+ reset();
+ // Layout again with the new stack
+ requestLayout();
+ }
+
+ // Set the new stack
+ mStack = stack;
+ if (mStack != null) {
+ mStack.setCallbacks(this);
+ }
+ }
+
/** Sets the debug overlay */
public void setDebugOverlay(DebugOverlayView overlay) {
mDebugOverlay = overlay;
}
+ /** Resets this TaskStackView for reuse. */
+ void reset() {
+ // Return all the views to the pool
+ int childCount = getChildCount();
+ for (int i = childCount - 1; i >= 0; i--) {
+ TaskView tv = (TaskView) getChildAt(i);
+ mViewPool.returnViewToPool(tv);
+ }
+
+ // Mark each task view for relayout
+ if (mViewPool != null) {
+ Iterator<TaskView> iter = mViewPool.poolViewIterator();
+ if (iter != null) {
+ while (iter.hasNext()) {
+ TaskView tv = iter.next();
+ tv.reset();
+ }
+ }
+ }
+
+ // Reset the stack state
+ resetFocusedTask();
+ mStackViewsDirty = true;
+ mStackViewsClipDirty = true;
+ mAwaitingFirstLayout = true;
+ mPrevAccessibilityFocusedIndex = -1;
+ if (mUIDozeTrigger != null) {
+ mUIDozeTrigger.stopDozing();
+ mUIDozeTrigger.resetTrigger();
+ }
+ }
+
/** Requests that the views be synchronized with the model */
void requestSynchronizeStackViewsWithModel() {
requestSynchronizeStackViewsWithModel(0);
@@ -510,6 +548,16 @@
tv.dismissTask();
}
+ /** Resets the focused task. */
+ void resetFocusedTask() {
+ if (mFocusedTaskIndex > -1) {
+ Task t = mStack.getTasks().get(mFocusedTaskIndex);
+ TaskView tv = getChildViewForTask(t);
+ tv.unsetFocusedTask();
+ }
+ mFocusedTaskIndex = -1;
+ }
+
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
@@ -543,6 +591,8 @@
@Override
public void computeScroll() {
+ if (mStack == null) return;
+
mStackScroller.computeScroll();
// Synchronize the views
synchronizeStackViewsWithModel();
@@ -758,9 +808,6 @@
TaskView tv = (TaskView) getChildAt(i);
tv.startExitToHomeAnimation(ctx);
}
-
- // Add a runnable to the post animation ref counter to clear all the views
- ctx.postAnimationTrigger.addLastDecrementRunnable(mReturnAllViewsToPoolRunnable);
}
/** Animates a task view in this stack as it launches. */
@@ -780,6 +827,12 @@
}
}
+ /** Final callback after Recents is finally hidden. */
+ void onRecentsHidden() {
+ reset();
+ setStack(null);
+ }
+
public boolean isTransformedTouchPointInView(float x, float y, View child) {
return isTransformedTouchPointInView(x, y, child, null);
}
@@ -944,20 +997,23 @@
// Reset the view properties
tv.resetViewProperties();
+
+ // Reset the clip state of the task view
+ tv.setClipViewInStack(false);
}
@Override
public void prepareViewToLeavePool(TaskView tv, Task task, boolean isNewView) {
+ // It is possible for a view to be returned to the view pool before it is laid out,
+ // which means that we will need to relayout the view when it is first used next.
+ boolean requiresRelayout = tv.getWidth() <= 0 && !isNewView;
+
// Rebind the task and request that this task's data be filled into the TaskView
tv.onTaskBound(task);
// Load the task data
RecentsTaskLoader.getInstance().loadTaskData(task);
- // Sanity check, the task view should always be clipping against the stack at this point,
- // but just in case, re-enable it here
- tv.setClipViewInStack(true);
-
// If the doze trigger has already fired, then update the state for this task view
if (mUIDozeTrigger.hasTriggered()) {
tv.setNoUserInteractionState();
@@ -985,13 +1041,17 @@
// Add/attach the view to the hierarchy
if (isNewView) {
addView(tv, insertIndex);
-
- // Set the callbacks and listeners for this new view
- tv.setTouchEnabled(true);
- tv.setCallbacks(this);
} else {
attachViewToParent(tv, insertIndex, tv.getLayoutParams());
+ if (requiresRelayout) {
+ tv.requestLayout();
+ }
}
+
+ // Set the new state for this view, including the callbacks and view clipping
+ tv.setCallbacks(this);
+ tv.setTouchEnabled(true);
+ tv.setClipViewInStack(true);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 7b4e10a..790130a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -112,6 +112,14 @@
mCb = cb;
}
+ /** Resets this TaskView for reuse. */
+ void reset() {
+ resetViewProperties();
+ resetNoUserInteractionState();
+ setClipViewInStack(false);
+ setCallbacks(null);
+ }
+
/** Gets the task */
Task getTask() {
return mTask;
@@ -191,6 +199,7 @@
/** Resets this view's properties */
void resetViewProperties() {
setDim(0);
+ setLayerType(View.LAYER_TYPE_NONE, null);
TaskViewTransform.reset(this);
}
@@ -448,6 +457,11 @@
mHeaderView.setNoUserInteractionState();
}
+ /** Resets the state tracking that the user has not interacted with the stack after a certain time. */
+ void resetNoUserInteractionState() {
+ mHeaderView.resetNoUserInteractionState();
+ }
+
/** Dismisses this task. */
void dismissTask() {
// Animate out the view and call the callback
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index ba868f5..5de84bd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -263,6 +263,11 @@
}
}
+ /** Resets the state tracking that the user has not interacted with the stack after a certain time. */
+ void resetNoUserInteractionState() {
+ mDismissButton.setVisibility(View.INVISIBLE);
+ }
+
@Override
protected int[] onCreateDrawableState(int extraSpace) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java b/packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java
index af0094e..12b91af 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/ViewPool.java
@@ -75,4 +75,12 @@
mViewCreator.prepareViewToLeavePool(v, prepareData, isNewView);
return v;
}
+
+ /** Returns an iterator to the list of the views in the pool. */
+ Iterator<V> poolViewIterator() {
+ if (mPool != null) {
+ return mPool.iterator();
+ }
+ return null;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 7bbf9e2..725a1a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -59,7 +59,6 @@
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.Log;
-import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.Display;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
index 628aab8..7ae6764 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
@@ -17,10 +17,11 @@
package com.android.systemui.statusbar;
import android.app.StatusBarManager;
+import android.content.res.Resources;
import android.graphics.RectF;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewConfiguration;
+import com.android.systemui.R;
public class DelegateViewHelper {
private View mDelegateView;
@@ -106,8 +107,8 @@
public void setSourceView(View view) {
mSourceView = view;
if (mSourceView != null) {
- mTriggerThreshhold =
- ViewConfiguration.get(mSourceView.getContext()).getScaledPagingTouchSlop();
+ Resources r = mSourceView.getContext().getResources();
+ mTriggerThreshhold = r.getDimensionPixelSize(R.dimen.navigation_bar_min_swipe_distance);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DismissViewImageButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/DismissViewImageButton.java
index d55b0b3..35fd688 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DismissViewImageButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DismissViewImageButton.java
@@ -61,4 +61,9 @@
outRect.top += translationY;
outRect.bottom += translationY;
}
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index a4e5e74..f8332ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -22,6 +22,7 @@
import android.graphics.drawable.Drawable;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewStub;
import android.view.accessibility.AccessibilityEvent;
@@ -68,6 +69,7 @@
private NotificationGuts mGuts;
private StatusBarNotification mStatusBarNotification;
+ private boolean mIsHeadsUp;
public void setIconAnimationRunning(boolean running) {
setIconAnimationRunning(running, mPublicLayout);
@@ -122,6 +124,10 @@
return mStatusBarNotification;
}
+ public void setHeadsUp(boolean isHeadsUp) {
+ mIsHeadsUp = isHeadsUp;
+ }
+
public interface ExpansionLogger {
public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
}
@@ -147,14 +153,16 @@
mShowingPublicInitialized = false;
mIsSystemExpanded = false;
mExpansionDisabled = false;
- mPublicLayout.reset();
- mPrivateLayout.reset();
+ mPublicLayout.reset(mIsHeadsUp);
+ mPrivateLayout.reset(mIsHeadsUp);
resetHeight();
logExpansionEvent(false, wasExpanded);
}
public void resetHeight() {
- super.resetHeight();
+ if (mIsHeadsUp) {
+ resetActualHeight();
+ }
mMaxExpandHeight = 0;
mWasReset = true;
onHeightReset();
@@ -162,6 +170,11 @@
}
@Override
+ protected boolean filterMotionEvent(MotionEvent event) {
+ return mIsHeadsUp || super.filterMotionEvent(event);
+ }
+
+ @Override
protected void onFinishInflate() {
super.onFinishInflate();
mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index edfbe86..bf1e78e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -103,9 +103,13 @@
}
}
- protected void resetHeight() {
+ /**
+ * Resets the height of the view on the next layout pass
+ */
+ protected void resetActualHeight() {
mActualHeight = 0;
mActualHeightInitialized = false;
+ requestLayout();
}
protected int getInitialHeight() {
@@ -120,7 +124,7 @@
return false;
}
- private boolean filterMotionEvent(MotionEvent event) {
+ protected boolean filterMotionEvent(MotionEvent event) {
return event.getActionMasked() != MotionEvent.ACTION_DOWN
|| event.getY() > mClipTopAmount && event.getY() < mActualHeight;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index ce35e4b..58067c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -146,8 +146,8 @@
try {
long chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
if (chargingTimeRemaining > 0) {
- String chargingTimeFormatted =
- Formatter.formatShortElapsedTime(mContext, chargingTimeRemaining);
+ String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
+ mContext, chargingTimeRemaining);
return mContext.getResources().getString(
R.string.keyguard_indication_charging_time, chargingTimeFormatted);
}
@@ -162,8 +162,9 @@
KeyguardUpdateMonitorCallback mUpdateMonitor = new KeyguardUpdateMonitorCallback() {
@Override
public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
- mPowerPluggedIn = status.status == BatteryManager.BATTERY_STATUS_CHARGING
+ boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
|| status.status == BatteryManager.BATTERY_STATUS_FULL;
+ mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
mPowerCharged = status.isCharged();
updateIndication();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 9b11f9b..99214a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -74,7 +74,7 @@
public NotificationContentView(Context context, AttributeSet attrs) {
super(context, attrs);
mFadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
- reset();
+ reset(true);
}
@Override
@@ -89,7 +89,7 @@
updateVisibility();
}
- public void reset() {
+ public void reset(boolean resetActualHeight) {
if (mContractedChild != null) {
mContractedChild.animate().cancel();
}
@@ -100,8 +100,10 @@
mContractedChild = null;
mExpandedChild = null;
mSmallHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
- mActualHeight = mSmallHeight;
mContractedVisible = true;
+ if (resetActualHeight) {
+ mActualHeight = mSmallHeight;
+ }
}
public View getContractedChild() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index 32fb567..e89e15d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -45,6 +45,7 @@
public static final int MODE_LIGHTS_OUT = 3;
public static final int MODE_TRANSPARENT = 4;
public static final int MODE_WARNING = 5;
+ public static final int MODE_LIGHTS_OUT_TRANSPARENT = 6;
public static final int LIGHTS_IN_DURATION = 250;
public static final int LIGHTS_OUT_DURATION = 750;
@@ -75,6 +76,9 @@
|| mode == MODE_TRANSPARENT)) {
mode = MODE_OPAQUE;
}
+ if (!HIGH_END && (mode == MODE_LIGHTS_OUT_TRANSPARENT)) {
+ mode = MODE_LIGHTS_OUT;
+ }
if (mMode == mode) return;
int oldMode = mMode;
mMode = mode;
@@ -102,6 +106,7 @@
if (mode == MODE_LIGHTS_OUT) return "MODE_LIGHTS_OUT";
if (mode == MODE_TRANSPARENT) return "MODE_TRANSPARENT";
if (mode == MODE_WARNING) return "MODE_WARNING";
+ if (mode == MODE_LIGHTS_OUT_TRANSPARENT) return "MODE_LIGHTS_OUT_TRANSPARENT";
throw new IllegalArgumentException("Unknown mode " + mode);
}
@@ -109,6 +114,10 @@
mBarBackground.finishAnimation();
}
+ protected boolean isLightsOut(int mode) {
+ return mode == MODE_LIGHTS_OUT || mode == MODE_LIGHTS_OUT_TRANSPARENT;
+ }
+
private static class BarBackgroundDrawable extends Drawable {
private final int mOpaque;
private final int mSemiTransparent;
@@ -196,7 +205,7 @@
targetColor = mSemiTransparent;
} else if (mMode == MODE_SEMI_TRANSPARENT) {
targetColor = mSemiTransparent;
- } else if (mMode == MODE_TRANSPARENT) {
+ } else if (mMode == MODE_TRANSPARENT || mMode == MODE_LIGHTS_OUT_TRANSPARENT) {
targetColor = mTransparent;
} else {
targetColor = mOpaque;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index 052b6c6..15f6dc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -84,7 +84,7 @@
applyBackButtonQuiescentAlpha(mode, animate);
// apply to lights out
- applyLightsOut(mode == MODE_LIGHTS_OUT, animate, force);
+ applyLightsOut(isLightsOut(mode), animate, force);
}
private float alphaForMode(int mode) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 9bb52e7..1e4dfb4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -21,6 +21,7 @@
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
+import android.app.ActivityManagerNative;
import android.app.StatusBarManager;
import android.content.Context;
import android.content.res.Configuration;
@@ -30,6 +31,7 @@
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
+import android.os.RemoteException;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
@@ -43,6 +45,7 @@
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
+
import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.DelegateViewHelper;
@@ -332,7 +335,7 @@
mDisabledFlags = disabledFlags;
final boolean disableHome = ((disabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
- final boolean disableRecent = ((disabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0);
+ boolean disableRecent = ((disabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0);
final boolean disableBack = ((disabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0)
&& ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) == 0);
final boolean disableSearch = ((disabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0);
@@ -357,6 +360,11 @@
}
}
}
+ if (inLockTask() && disableRecent && !disableHome) {
+ // Don't hide recents when in lock task, it is used for exiting.
+ // Unless home is hidden, then in DPM locked mode and no exit available.
+ disableRecent = false;
+ }
getBackButton() .setVisibility(disableBack ? View.INVISIBLE : View.VISIBLE);
getHomeButton() .setVisibility(disableHome ? View.INVISIBLE : View.VISIBLE);
@@ -365,6 +373,14 @@
mBarTransitions.applyBackButtonQuiescentAlpha(mBarTransitions.getMode(), true /*animate*/);
}
+ private boolean inLockTask() {
+ try {
+ return ActivityManagerNative.getDefault().isInLockTaskMode();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
private void setVisibleOrGone(View view, boolean visible) {
if (view != null) {
view.setVisibility(visible ? VISIBLE : GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarViewTaskSwitchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarViewTaskSwitchHelper.java
index c253e19..fdfcdfb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarViewTaskSwitchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarViewTaskSwitchHelper.java
@@ -17,9 +17,11 @@
package com.android.systemui.statusbar.phone;
import android.content.Context;
+import android.content.res.Resources;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
+import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
public class NavigationBarViewTaskSwitchHelper extends GestureDetector.SimpleOnGestureListener {
@@ -36,7 +38,8 @@
public NavigationBarViewTaskSwitchHelper(Context context) {
ViewConfiguration configuration = ViewConfiguration.get(context);
- mScrollTouchSlop = 4 * configuration.getScaledTouchSlop();
+ Resources r = context.getResources();
+ mScrollTouchSlop = r.getDimensionPixelSize(R.dimen.navigation_bar_min_swipe_distance);
mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
mTaskSwitcherDetector = new GestureDetector(context, this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index d9e44c3..bb992b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -291,8 +291,8 @@
} else {
setQsExpansion(mQsMinExpansionHeight + mLastOverscroll);
mNotificationStackScroller.setStackHeight(getExpandedHeight());
- updateHeader();
}
+ updateHeader();
mNotificationStackScroller.updateIsSmallScreen(
mHeader.getCollapsedHeight() + mQsPeekHeight);
}
@@ -1329,6 +1329,16 @@
float notificationHeight = mNotificationStackScroller.getHeight()
- mNotificationStackScroller.getEmptyBottomMargin()
- mNotificationStackScroller.getTopPadding();
+
+ // When only empty shade view is visible in QS collapsed state, simulate that we would have
+ // it in expanded QS state as well so we don't run into troubles when fading the view in/out
+ // and expanding/collapsing the whole panel from/to quick settings.
+ if (mNotificationStackScroller.getNotGoneChildCount() == 0
+ && mShadeEmpty) {
+ notificationHeight = mNotificationStackScroller.getEmptyShadeViewHeight()
+ + mNotificationStackScroller.getBottomStackPeekSize()
+ + mNotificationStackScroller.getCollapseSecondCardPadding();
+ }
float totalHeight = Math.max(
mQsMaxExpansionHeight + mNotificationStackScroller.getNotificationTopPadding(),
mClockPositionResult.stackScrollerPadding - mTopPaddingAdjustment)
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 72dfee6..5928845 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -23,6 +23,7 @@
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.windowStateToString;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
@@ -2600,8 +2601,10 @@
}
private int barMode(int vis, int transientFlag, int translucentFlag) {
+ int lightsOutTransparent = View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_TRANSPARENT;
return (vis & transientFlag) != 0 ? MODE_SEMI_TRANSPARENT
: (vis & translucentFlag) != 0 ? MODE_TRANSLUCENT
+ : (vis & lightsOutTransparent) == lightsOutTransparent ? MODE_LIGHTS_OUT_TRANSPARENT
: (vis & View.SYSTEM_UI_TRANSPARENT) != 0 ? MODE_TRANSPARENT
: (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0 ? MODE_LIGHTS_OUT
: MODE_OPAQUE;
@@ -3071,8 +3074,6 @@
}
else if (Intent.ACTION_SCREEN_ON.equals(action)) {
mScreenOn = true;
- // work around problem where mDisplay.getRotation() is not stable while screen is off (bug 7086018)
- repositionNavigationBar();
notifyNavigationBarScreenOn(true);
}
else if (ACTION_DEMO.equals(action)) {
@@ -3597,6 +3598,12 @@
instantCollapseNotificationPanel();
}
updateKeyguardState(staying, false /* fromShadeLocked */);
+
+ // Keyguard state has changed, but QS is not listening anymore. Make sure to update the tile
+ // visibilities so next time we open the panel we know the correct height already.
+ if (mQSPanel != null) {
+ mQSPanel.refreshAllTiles();
+ }
return staying;
}
@@ -3961,6 +3968,8 @@
// long-pressed 'together'
if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
activityManager.stopLockTaskModeOnCurrent();
+ // When exiting refresh disabled flags.
+ mNavigationBarView.setDisabledFlags(mDisabled, true);
} else if ((v.getId() == R.id.back)
&& !mNavigationBarView.getRecentsButton().isPressed()) {
// If we aren't pressing recents right now then they presses
@@ -3976,6 +3985,8 @@
// When in accessibility mode a long press that is recents (not back)
// should stop lock task.
activityManager.stopLockTaskModeOnCurrent();
+ // When exiting refresh disabled flags.
+ mNavigationBarView.setDisabledFlags(mDisabled, true);
}
}
if (sendBackLongPress) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
index 8520f40..fb1addf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
@@ -57,19 +57,19 @@
}
private float getNonBatteryClockAlphaFor(int mode) {
- return mode == MODE_LIGHTS_OUT ? ICON_ALPHA_WHEN_LIGHTS_OUT_NON_BATTERY_CLOCK
+ return isLightsOut(mode) ? ICON_ALPHA_WHEN_LIGHTS_OUT_NON_BATTERY_CLOCK
: !isOpaque(mode) ? ICON_ALPHA_WHEN_NOT_OPAQUE
: mIconAlphaWhenOpaque;
}
private float getBatteryClockAlpha(int mode) {
- return mode == MODE_LIGHTS_OUT ? ICON_ALPHA_WHEN_LIGHTS_OUT_BATTERY_CLOCK
+ return isLightsOut(mode) ? ICON_ALPHA_WHEN_LIGHTS_OUT_BATTERY_CLOCK
: getNonBatteryClockAlphaFor(mode);
}
private boolean isOpaque(int mode) {
return !(mode == MODE_SEMI_TRANSPARENT || mode == MODE_TRANSLUCENT
- || mode == MODE_TRANSPARENT);
+ || mode == MODE_TRANSPARENT || mode == MODE_LIGHTS_OUT_TRANSPARENT);
}
@Override
@@ -94,7 +94,7 @@
animateTransitionTo(mBattery, newAlphaBC),
animateTransitionTo(mClock, newAlphaBC)
);
- if (mode == MODE_LIGHTS_OUT) {
+ if (isLightsOut(mode)) {
anims.setDuration(LIGHTS_OUT_DURATION);
}
anims.start();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index a4db46a..45a1386 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -21,6 +21,7 @@
import android.content.res.Resources;
import android.database.ContentObserver;
import android.net.Uri;
+import android.os.Process;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -108,7 +109,8 @@
mKeyguard = keyguard;
mSecurity = security;
- final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName());
+ final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName(),
+ Process.THREAD_PRIORITY_BACKGROUND);
ht.start();
mLooper = ht.getLooper();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
index 84216a4..11ff272 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
@@ -54,7 +54,6 @@
private EdgeSwipeHelper mEdgeSwipeHelper;
private PhoneStatusBar mBar;
- private ExpandHelper mExpandHelper;
private long mStartTouchTime;
private ViewGroup mContentHolder;
@@ -102,6 +101,7 @@
if (mHeadsUp != null) {
mHeadsUp.row.setSystemExpanded(true);
mHeadsUp.row.setSensitive(false);
+ mHeadsUp.row.setHeadsUp(true);
mHeadsUp.row.setHideSensitive(
false, false /* animated */, 0 /* delay */, 0 /* duration */);
if (mContentHolder == null) {
@@ -205,7 +205,6 @@
int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_max_height);
- mExpandHelper = new ExpandHelper(getContext(), this, minHeight, maxHeight);
mContentHolder = (ViewGroup) findViewById(R.id.content_holder);
mContentHolder.setOutlineProvider(CONTENT_HOLDER_OUTLINE_PROVIDER);
@@ -226,7 +225,6 @@
}
return mEdgeSwipeHelper.onInterceptTouchEvent(ev)
|| mSwipeHelper.onInterceptTouchEvent(ev)
- || mExpandHelper.onInterceptTouchEvent(ev)
|| super.onInterceptTouchEvent(ev);
}
@@ -254,7 +252,6 @@
mBar.resetHeadsUpDecayTimer();
return mEdgeSwipeHelper.onTouchEvent(ev)
|| mSwipeHelper.onTouchEvent(ev)
- || mExpandHelper.onTouchEvent(ev)
|| super.onTouchEvent(ev);
}
@@ -399,15 +396,12 @@
final float dY = ev.getY() - mFirstY;
final float daX = Math.abs(ev.getX() - mFirstX);
final float daY = Math.abs(dY);
- if (!mConsuming && (4f * daX) < daY && daY > mTouchSlop) {
+ if (!mConsuming && daX < daY && daY > mTouchSlop) {
+ releaseAndClose();
if (dY > 0) {
if (DEBUG_EDGE_SWIPE) Log.d(TAG, "found an open");
mBar.animateExpandNotificationsPanel();
}
- if (dY < 0) {
- if (DEBUG_EDGE_SWIPE) Log.d(TAG, "found a close");
- mBar.onHeadsUpDismissed();
- }
mConsuming = true;
}
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 87ce565..d543cff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -2272,6 +2272,10 @@
return height;
}
+ public int getEmptyShadeViewHeight() {
+ return mEmptyShadeView.getHeight();
+ }
+
public float getBottomMostNotificationBottom() {
final int count = getChildCount();
float max = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index c840f17..28ecbf9 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -69,6 +69,7 @@
private static final int TIME_CONDITION_INDEX = 1;
private static final int FIRST_CONDITION_INDEX = 2;
private static final float SILENT_HINT_PULSE_SCALE = 1.1f;
+ private static final long SELECT_DEFAULT_DELAY = 300;
public static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
@@ -373,8 +374,9 @@
if (isDowntime(mSessionExitCondition) && !foundDowntime) {
bind(mSessionExitCondition, null);
}
- // ensure something is selected
- checkForDefault();
+ // ensure something is selected, after waiting for providers to respond
+ mHandler.removeMessages(H.SELECT_DEFAULT);
+ mHandler.sendEmptyMessageDelayed(H.SELECT_DEFAULT, SELECT_DEFAULT_DELAY);
}
private static boolean isDowntime(Condition c) {
@@ -385,7 +387,8 @@
return (ConditionTag) mZenConditions.getChildAt(index).getTag();
}
- private void checkForDefault() {
+ private void handleSelectDefault() {
+ if (!mExpanded) return;
// are we left without anything selected? if so, set a default
for (int i = 0; i < mZenConditions.getChildCount(); i++) {
if (getConditionTagAt(i).rb.isChecked()) {
@@ -638,6 +641,7 @@
private static final int UPDATE_CONDITIONS = 1;
private static final int EXIT_CONDITION_CHANGED = 2;
private static final int UPDATE_ZEN = 3;
+ private static final int SELECT_DEFAULT = 4;
private H() {
super(Looper.getMainLooper());
@@ -651,6 +655,8 @@
handleExitConditionChanged((Condition) msg.obj);
} else if (msg.what == UPDATE_ZEN) {
handleUpdateZen(msg.arg1);
+ } else if (msg.what == SELECT_DEFAULT) {
+ handleSelectDefault();
}
}
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 792712f..3c44e87 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -48,6 +48,7 @@
import android.media.RingtoneManager;
import android.media.session.MediaSessionLegacyHelper;
import android.os.Bundle;
+import android.os.Debug;
import android.os.FactoryTest;
import android.os.Handler;
import android.os.IBinder;
@@ -116,6 +117,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashSet;
+import java.util.List;
import static android.view.WindowManager.LayoutParams.*;
import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
@@ -527,6 +529,9 @@
private boolean mAllowTheaterModeWakeFromLidSwitch;
private boolean mAllowTheaterModeWakeFromWakeGesture;
+ // Whether to go to sleep entering theater mode from power button
+ private boolean mGoToSleepOnButtonPressTheaterMode;
+
// Screenshot trigger states
// Time to volume and power must be pressed within this interval of each other.
private static final long SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS = 150;
@@ -984,7 +989,8 @@
Slog.i(TAG, "Toggling theater mode on.");
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.THEATER_MODE_ON, 1);
- if (interactive) {
+
+ if (mGoToSleepOnButtonPressTheaterMode && interactive) {
mPowerManager.goToSleep(eventTime,
PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
}
@@ -1236,6 +1242,9 @@
mAllowTheaterModeWakeFromWakeGesture = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromGesture);
+ mGoToSleepOnButtonPressTheaterMode = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_goToSleepOnButtonPressTheaterMode);
+
mShortPressOnPowerBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_shortPressOnPowerBehavior);
mLongPressOnPowerBehavior = mContext.getResources().getInteger(
@@ -2099,11 +2108,8 @@
/** {@inheritDoc} */
@Override
public void removeStartingWindow(IBinder appToken, View window) {
- if (DEBUG_STARTING_WINDOW) {
- RuntimeException e = new RuntimeException("here");
- e.fillInStackTrace();
- Log.v(TAG, "Removing starting window for " + appToken + ": " + window, e);
- }
+ if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Removing starting window for " + appToken + ": "
+ + window + " Callers=" + Debug.getCallers(4));
if (window != null) {
WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
@@ -2299,24 +2305,19 @@
boolean goingToNotificationShade) {
if (goingToNotificationShade) {
return AnimationUtils.loadAnimation(mContext, R.anim.lock_screen_behind_enter_fade_in);
- } else if (onWallpaper) {
- Animation a = AnimationUtils.loadAnimation(mContext,
- R.anim.lock_screen_behind_enter_wallpaper);
- AnimationSet set = (AnimationSet) a;
-
- // TODO: Use XML interpolators when we have log interpolators available in XML.
- set.getAnimations().get(0).setInterpolator(mLogDecelerateInterpolator);
- set.getAnimations().get(1).setInterpolator(mLogDecelerateInterpolator);
- return set;
- } else {
- Animation a = AnimationUtils.loadAnimation(mContext,
- R.anim.lock_screen_behind_enter);
- AnimationSet set = (AnimationSet) a;
-
- // TODO: Use XML interpolators when we have log interpolators available in XML.
- set.getAnimations().get(0).setInterpolator(mLogDecelerateInterpolator);
- return set;
}
+
+ AnimationSet set = (AnimationSet) AnimationUtils.loadAnimation(mContext, onWallpaper ?
+ R.anim.lock_screen_behind_enter_wallpaper :
+ R.anim.lock_screen_behind_enter);
+
+ // TODO: Use XML interpolators when we have log interpolators available in XML.
+ final List<Animation> animations = set.getAnimations();
+ for (int i = animations.size() - 1; i >= 0; --i) {
+ animations.get(i).setInterpolator(mLogDecelerateInterpolator);
+ }
+
+ return set;
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index bf67461..5997680 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -23,18 +23,19 @@
import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
import static android.net.ConnectivityManager.TYPE_DUMMY;
import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
-import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
+import static android.net.ConnectivityManager.TYPE_MOBILE_CBS;
import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
-import static android.net.ConnectivityManager.TYPE_MOBILE_IMS;
-import static android.net.ConnectivityManager.TYPE_MOBILE_CBS;
-import static android.net.ConnectivityManager.TYPE_MOBILE_IA;
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+import static android.net.ConnectivityManager.TYPE_MOBILE_IA;
+import static android.net.ConnectivityManager.TYPE_MOBILE_IMS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
+import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
import static android.net.ConnectivityManager.TYPE_NONE;
+import static android.net.ConnectivityManager.TYPE_PROXY;
+import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.TYPE_WIMAX;
-import static android.net.ConnectivityManager.TYPE_PROXY;
import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
@@ -184,7 +185,8 @@
/**
* @hide
*/
-public class ConnectivityService extends IConnectivityManager.Stub {
+public class ConnectivityService extends IConnectivityManager.Stub
+ implements PendingIntent.OnFinished {
private static final String TAG = "ConnectivityService";
private static final boolean DBG = true;
@@ -382,6 +384,19 @@
*/
private static final int EVENT_SYSTEM_READY = 25;
+ /**
+ * used to add a network request with a pending intent
+ * includes a NetworkRequestInfo
+ */
+ private static final int EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT = 26;
+
+ /**
+ * used to remove a pending intent and its associated network request.
+ * arg1 = UID of caller
+ * obj = PendingIntent
+ */
+ private static final int EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT = 27;
+
/** Handler used for internal events. */
final private InternalHandler mHandler;
@@ -395,6 +410,7 @@
private String mNetTransitionWakeLockCausedBy = "";
private int mNetTransitionWakeLockSerialNumber;
private int mNetTransitionWakeLockTimeout;
+ private final PowerManager.WakeLock mPendingIntentWakeLock;
private InetAddress mDefaultDns;
@@ -649,6 +665,7 @@
mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mNetTransitionWakeLockTimeout = mContext.getResources().getInteger(
com.android.internal.R.integer.config_networkTransitionTimeout);
+ mPendingIntentWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mNetTrackers = new NetworkStateTracker[
ConnectivityManager.MAX_NETWORK_TYPE+1];
@@ -687,6 +704,15 @@
// ignore it - leave the entry null
}
}
+
+ // Forcibly add TYPE_VPN as a supported type, if it has not already been added via config.
+ if (mNetConfigs[TYPE_VPN] == null) {
+ // mNetConfigs is used only for "restore time", which isn't applicable to VPNs, so we
+ // don't need to add TYPE_VPN to mNetConfigs.
+ mLegacyTypeTracker.addSupportedType(TYPE_VPN);
+ mNetworksDefined++; // used only in the log() statement below.
+ }
+
if (VDBG) log("mNetworksDefined=" + mNetworksDefined);
mProtectedNetworks = new ArrayList<Integer>();
@@ -2131,11 +2157,40 @@
}
}
+ // If this method proves to be too slow then we can maintain a separate
+ // pendingIntent => NetworkRequestInfo map.
+ // This method assumes that every non-null PendingIntent maps to exactly 1 NetworkRequestInfo.
+ private NetworkRequestInfo findExistingNetworkRequestInfo(PendingIntent pendingIntent) {
+ Intent intent = pendingIntent.getIntent();
+ for (Map.Entry<NetworkRequest, NetworkRequestInfo> entry : mNetworkRequests.entrySet()) {
+ PendingIntent existingPendingIntent = entry.getValue().mPendingIntent;
+ if (existingPendingIntent != null &&
+ existingPendingIntent.getIntent().filterEquals(intent)) {
+ return entry.getValue();
+ }
+ }
+ return null;
+ }
+
+ private void handleRegisterNetworkRequestWithIntent(Message msg) {
+ final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj);
+
+ NetworkRequestInfo existingRequest = findExistingNetworkRequestInfo(nri.mPendingIntent);
+ if (existingRequest != null) { // remove the existing request.
+ if (DBG) log("Replacing " + existingRequest.request + " with "
+ + nri.request + " because their intents matched.");
+ handleReleaseNetworkRequest(existingRequest.request, getCallingUid());
+ }
+ handleRegisterNetworkRequest(msg);
+ }
+
private void handleRegisterNetworkRequest(Message msg) {
final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj);
final NetworkCapabilities newCap = nri.request.networkCapabilities;
int score = 0;
+ mNetworkRequests.put(nri.request, nri);
+
// Check for the best currently alive network that satisfies this request
NetworkAgentInfo bestNetwork = null;
for (NetworkAgentInfo network : mNetworkAgentInfos.values()) {
@@ -2173,7 +2228,7 @@
mLegacyTypeTracker.add(nri.request.legacyType, bestNetwork);
}
}
- mNetworkRequests.put(nri.request, nri);
+
if (nri.isRequest) {
if (DBG) log("sending new NetworkRequest to factories");
for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
@@ -2183,6 +2238,14 @@
}
}
+ private void handleReleaseNetworkRequestWithIntent(PendingIntent pendingIntent,
+ int callingUid) {
+ NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent);
+ if (nri != null) {
+ handleReleaseNetworkRequest(nri.request, callingUid);
+ }
+ }
+
private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid) {
NetworkRequestInfo nri = mNetworkRequests.get(request);
if (nri != null) {
@@ -2218,11 +2281,11 @@
}
}
- // Maintain the illusion. When this request arrived, we might have preteneded
+ // Maintain the illusion. When this request arrived, we might have pretended
// that a network connected to serve it, even though the network was already
// connected. Now that this request has gone away, we might have to pretend
// that the network disconnected. LegacyTypeTracker will generate that
- // phatom disconnect for this type.
+ // phantom disconnect for this type.
NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
if (nai != null) {
mNetworkForRequestId.remove(nri.request.requestId);
@@ -2253,7 +2316,6 @@
@Override
public void handleMessage(Message msg) {
- NetworkInfo info;
switch (msg.what) {
case EVENT_EXPIRE_NET_TRANSITION_WAKELOCK:
case EVENT_CLEAR_NET_TRANSITION_WAKELOCK: {
@@ -2334,6 +2396,14 @@
handleRegisterNetworkRequest(msg);
break;
}
+ case EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT: {
+ handleRegisterNetworkRequestWithIntent(msg);
+ break;
+ }
+ case EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT: {
+ handleReleaseNetworkRequestWithIntent((PendingIntent) msg.obj, msg.arg1);
+ break;
+ }
case EVENT_RELEASE_NETWORK_REQUEST: {
handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.arg1);
break;
@@ -3347,12 +3417,23 @@
static final boolean LISTEN = false;
final NetworkRequest request;
- IBinder mBinder;
+ final PendingIntent mPendingIntent;
+ private final IBinder mBinder;
final int mPid;
final int mUid;
final Messenger messenger;
final boolean isRequest;
+ NetworkRequestInfo(NetworkRequest r, PendingIntent pi, boolean isRequest) {
+ request = r;
+ mPendingIntent = pi;
+ messenger = null;
+ mBinder = null;
+ mPid = getCallingPid();
+ mUid = getCallingUid();
+ this.isRequest = isRequest;
+ }
+
NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder, boolean isRequest) {
super();
messenger = m;
@@ -3361,6 +3442,7 @@
mPid = getCallingPid();
mUid = getCallingUid();
this.isRequest = isRequest;
+ mPendingIntent = null;
try {
mBinder.linkToDeath(this, 0);
@@ -3370,7 +3452,9 @@
}
void unlinkDeathRecipient() {
- mBinder.unlinkToDeath(this, 0);
+ if (mBinder != null) {
+ mBinder.unlinkToDeath(this, 0);
+ }
}
public void binderDied() {
@@ -3381,40 +3465,22 @@
public String toString() {
return (isRequest ? "Request" : "Listen") + " from uid/pid:" + mUid + "/" +
- mPid + " for " + request;
+ mPid + " for " + request +
+ (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
}
}
@Override
public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
Messenger messenger, int timeoutMs, IBinder binder, int legacyType) {
- if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- == false) {
- enforceConnectivityInternalPermission();
- } else {
- enforceChangePermission();
- }
-
networkCapabilities = new NetworkCapabilities(networkCapabilities);
-
- // if UID is restricted, don't allow them to bring up metered APNs
- if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
- == false) {
- final int uidRules;
- final int uid = Binder.getCallingUid();
- synchronized(mRulesLock) {
- uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
- }
- if ((uidRules & RULE_REJECT_METERED) != 0) {
- // we could silently fail or we can filter the available nets to only give
- // them those they have access to. Chose the more useful
- networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
- }
- }
+ enforceNetworkRequestPermissions(networkCapabilities);
+ enforceMeteredApnPolicy(networkCapabilities);
if (timeoutMs < 0 || timeoutMs > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS) {
throw new IllegalArgumentException("Bad timeout specified");
}
+
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
nextNetworkRequestId());
if (DBG) log("requestNetwork for " + networkRequest);
@@ -3429,11 +3495,54 @@
return networkRequest;
}
+ private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities) {
+ if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ == false) {
+ enforceConnectivityInternalPermission();
+ } else {
+ enforceChangePermission();
+ }
+ }
+
+ private void enforceMeteredApnPolicy(NetworkCapabilities networkCapabilities) {
+ // if UID is restricted, don't allow them to bring up metered APNs
+ if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
+ == false) {
+ final int uidRules;
+ final int uid = Binder.getCallingUid();
+ synchronized(mRulesLock) {
+ uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
+ }
+ if ((uidRules & RULE_REJECT_METERED) != 0) {
+ // we could silently fail or we can filter the available nets to only give
+ // them those they have access to. Chose the more useful
+ networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+ }
+ }
+ }
+
@Override
public NetworkRequest pendingRequestForNetwork(NetworkCapabilities networkCapabilities,
PendingIntent operation) {
- // TODO
- return null;
+ checkNotNull(operation, "PendingIntent cannot be null.");
+ networkCapabilities = new NetworkCapabilities(networkCapabilities);
+ enforceNetworkRequestPermissions(networkCapabilities);
+ enforceMeteredApnPolicy(networkCapabilities);
+
+ NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
+ nextNetworkRequestId());
+ if (DBG) log("pendingRequest for " + networkRequest + " to trigger " + operation);
+ NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation,
+ NetworkRequestInfo.REQUEST);
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT,
+ nri));
+ return networkRequest;
+ }
+
+ @Override
+ public void releasePendingNetworkRequest(PendingIntent operation) {
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT,
+ getCallingUid(), 0, operation));
}
@Override
@@ -3701,12 +3810,11 @@
private void updateCapabilities(NetworkAgentInfo networkAgent,
NetworkCapabilities networkCapabilities) {
- // TODO - turn this on in MR1 when we have more dogfooding time.
- // rematchAllNetworksAndRequests();
if (!Objects.equals(networkAgent.networkCapabilities, networkCapabilities)) {
synchronized (networkAgent) {
networkAgent.networkCapabilities = networkCapabilities;
}
+ rematchAllNetworksAndRequests(networkAgent, networkAgent.getCurrentScore());
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
}
@@ -3728,6 +3836,39 @@
}
}
+ private void sendPendingIntentForRequest(NetworkRequestInfo nri, NetworkAgentInfo networkAgent,
+ int notificationType) {
+ if (notificationType == ConnectivityManager.CALLBACK_AVAILABLE) {
+ Intent intent = new Intent();
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST_NETWORK, nri.request);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST_NETWORK_REQUEST,
+ networkAgent.network);
+ sendIntent(nri.mPendingIntent, intent);
+ }
+ // else not handled
+ }
+
+ private void sendIntent(PendingIntent pendingIntent, Intent intent) {
+ mPendingIntentWakeLock.acquire();
+ try {
+ if (DBG) log("Sending " + pendingIntent);
+ pendingIntent.send(mContext, 0, intent, this /* onFinished */, null /* Handler */);
+ } catch (PendingIntent.CanceledException e) {
+ if (DBG) log(pendingIntent + " was not sent, it had been canceled.");
+ mPendingIntentWakeLock.release();
+ releasePendingNetworkRequest(pendingIntent);
+ }
+ // ...otherwise, mPendingIntentWakeLock.release() gets called by onSendFinished()
+ }
+
+ @Override
+ public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
+ String resultData, Bundle resultExtras) {
+ if (DBG) log("Finished sending " + pendingIntent);
+ mPendingIntentWakeLock.release();
+ releasePendingNetworkRequest(pendingIntent);
+ }
+
private void callCallbackForRequest(NetworkRequestInfo nri,
NetworkAgentInfo networkAgent, int notificationType) {
if (nri.messenger == null) return; // Default request has no msgr
@@ -3980,6 +4121,14 @@
}
notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_AVAILABLE);
+
+ // A VPN generally won't get added to the legacy tracker in the "for (nri)" loop above,
+ // because usually there are no NetworkRequests it satisfies (e.g., mDefaultRequest
+ // wants the NOT_VPN capability, so it will never be satisfied by a VPN). So, add the
+ // newNetwork to the tracker explicitly (it's a no-op if it has already been added).
+ if (newNetwork.isVPN()) {
+ mLegacyTypeTracker.add(TYPE_VPN, newNetwork);
+ }
} else if (nascent) {
// Only tear down newly validated networks here. Leave unvalidated to either become
// validated (and get evaluated against peers, one losing here) or
@@ -4138,7 +4287,11 @@
// } else if (nai.networkMonitor.isEvaluating()) {
// notifyType = NetworkCallbacks.callCallbackForRequest(request, nai, notifyType);
// }
- callCallbackForRequest(nri, nai, notifyType);
+ if (nri.mPendingIntent == null) {
+ callCallbackForRequest(nri, nai, notifyType);
+ } else {
+ sendPendingIntentForRequest(nri, nai, notifyType);
+ }
}
private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, boolean connected, int type) {
@@ -4197,7 +4350,11 @@
NetworkRequest nr = networkAgent.networkRequests.valueAt(i);
NetworkRequestInfo nri = mNetworkRequests.get(nr);
if (VDBG) log(" sending notification for " + nr);
- callCallbackForRequest(nri, networkAgent, notifyType);
+ if (nri.mPendingIntent == null) {
+ callCallbackForRequest(nri, networkAgent, notifyType);
+ } else {
+ sendPendingIntentForRequest(nri, networkAgent, notifyType);
+ }
}
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index d9c96e4..be83b9b 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -60,6 +60,8 @@
import android.location.Criteria;
import android.location.GeocoderParams;
import android.location.Geofence;
+import android.location.GpsMeasurementsEvent;
+import android.location.GpsNavigationMessageEvent;
import android.location.IGpsMeasurementsListener;
import android.location.IGpsNavigationMessageListener;
import android.location.IGpsStatusListener;
@@ -1859,8 +1861,8 @@
}
@Override
- public boolean removeGpsMeasurementsListener(IGpsMeasurementsListener listener) {
- return mGpsMeasurementsProvider.removeListener(listener);
+ public void removeGpsMeasurementsListener(IGpsMeasurementsListener listener) {
+ mGpsMeasurementsProvider.removeListener(listener);
}
@Override
@@ -1888,8 +1890,8 @@
}
@Override
- public boolean removeGpsNavigationMessageListener(IGpsNavigationMessageListener listener) {
- return mGpsNavigationMessageProvider.removeListener(listener);
+ public void removeGpsNavigationMessageListener(IGpsNavigationMessageListener listener) {
+ mGpsNavigationMessageProvider.removeListener(listener);
}
@Override
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index b708c3f..11ba8e8 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -18,49 +18,38 @@
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
-import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
import static android.content.Context.USER_SERVICE;
import static android.Manifest.permission.READ_PROFILE;
-import android.database.Cursor;
+
import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.database.sqlite.SQLiteStatement;
import android.os.Binder;
-import android.os.Environment;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.os.storage.IMountService;
import android.os.ServiceManager;
-import android.os.storage.StorageManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.provider.Settings.SettingNotFoundException;
-import android.security.KeyChain;
-import android.security.KeyChain.KeyChainConnection;
import android.security.KeyStore;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
-import com.android.internal.os.BackgroundThread;
import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.ILockSettingsObserver;
import com.android.internal.widget.LockPatternUtils;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -73,27 +62,17 @@
*/
public class LockSettingsService extends ILockSettings.Stub {
- private static final String PERMISSION = "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE";
+ private static final String PERMISSION = ACCESS_KEYGUARD_SECURE_STORAGE;
private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
- private final DatabaseHelper mOpenHelper;
+
private static final String TAG = "LockSettingsService";
- private static final String TABLE = "locksettings";
- private static final String COLUMN_KEY = "name";
- private static final String COLUMN_USERID = "user";
- private static final String COLUMN_VALUE = "value";
-
- private static final String[] COLUMNS_FOR_QUERY = {
- COLUMN_VALUE
- };
-
- private static final String SYSTEM_DIRECTORY = "/system/";
- private static final String LOCK_PATTERN_FILE = "gesture.key";
- private static final String LOCK_PASSWORD_FILE = "password.key";
-
private final Context mContext;
+
+ private final LockSettingsStorage mStorage;
+
private LockPatternUtils mLockPatternUtils;
private boolean mFirstCallToVold;
@@ -102,7 +81,6 @@
public LockSettingsService(Context context) {
mContext = context;
// Open the database
- mOpenHelper = new DatabaseHelper(mContext);
mLockPatternUtils = new LockPatternUtils(context);
mFirstCallToVold = true;
@@ -110,6 +88,18 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_ADDED);
mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
+
+ mStorage = new LockSettingsStorage(context, new LockSettingsStorage.Callback() {
+ @Override
+ public void initialize(SQLiteDatabase db) {
+ // Get the lockscreen default from a system property, if available
+ boolean lockScreenDisable = SystemProperties.getBoolean(
+ "ro.lockscreen.disable.default", false);
+ if (lockScreenDisable) {
+ mStorage.writeKeyValue(db, LockPatternUtils.DISABLE_LOCKSCREEN_KEY, "1", 0);
+ }
+ }
+ });
}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -220,29 +210,31 @@
@Override
public void setBoolean(String key, boolean value, int userId) throws RemoteException {
checkWritePermission(userId);
-
- writeToDb(key, value ? "1" : "0", userId);
+ setStringUnchecked(key, userId, value ? "1" : "0");
}
@Override
public void setLong(String key, long value, int userId) throws RemoteException {
checkWritePermission(userId);
-
- writeToDb(key, Long.toString(value), userId);
+ setStringUnchecked(key, userId, Long.toString(value));
}
@Override
public void setString(String key, String value, int userId) throws RemoteException {
checkWritePermission(userId);
+ setStringUnchecked(key, userId, value);
+ }
- writeToDb(key, value, userId);
+ private void setStringUnchecked(String key, int userId, String value) {
+ mStorage.writeKeyValue(key, value, userId);
+ notifyObservers(key, userId);
}
@Override
public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException {
checkReadPermission(key, userId);
- String value = readFromDb(key, null, userId);
+ String value = mStorage.readKeyValue(key, null, userId);
return TextUtils.isEmpty(value) ?
defaultValue : (value.equals("1") || value.equals("true"));
}
@@ -251,7 +243,7 @@
public long getLong(String key, long defaultValue, int userId) throws RemoteException {
checkReadPermission(key, userId);
- String value = readFromDb(key, null, userId);
+ String value = mStorage.readKeyValue(key, null, userId);
return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value);
}
@@ -259,7 +251,7 @@
public String getString(String key, String defaultValue, int userId) throws RemoteException {
checkReadPermission(key, userId);
- return readFromDb(key, defaultValue, userId);
+ return mStorage.readKeyValue(key, defaultValue, userId);
}
@Override
@@ -308,57 +300,18 @@
}
}
- private int getUserParentOrSelfId(int userId) {
- if (userId != 0) {
- final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
- final UserInfo pi = um.getProfileParent(userId);
- if (pi != null) {
- return pi.id;
- }
- }
- return userId;
- }
-
- private String getLockPatternFilename(int userId) {
- String dataSystemDirectory =
- android.os.Environment.getDataDirectory().getAbsolutePath() +
- SYSTEM_DIRECTORY;
- userId = getUserParentOrSelfId(userId);
- if (userId == 0) {
- // Leave it in the same place for user 0
- return dataSystemDirectory + LOCK_PATTERN_FILE;
- } else {
- return new File(Environment.getUserSystemDirectory(userId), LOCK_PATTERN_FILE)
- .getAbsolutePath();
- }
- }
-
- private String getLockPasswordFilename(int userId) {
- userId = getUserParentOrSelfId(userId);
- String dataSystemDirectory =
- android.os.Environment.getDataDirectory().getAbsolutePath() +
- SYSTEM_DIRECTORY;
- if (userId == 0) {
- // Leave it in the same place for user 0
- return dataSystemDirectory + LOCK_PASSWORD_FILE;
- } else {
- return new File(Environment.getUserSystemDirectory(userId), LOCK_PASSWORD_FILE)
- .getAbsolutePath();
- }
- }
-
@Override
public boolean havePassword(int userId) throws RemoteException {
// Do we need a permissions check here?
- return new File(getLockPasswordFilename(userId)).length() > 0;
+ return mStorage.hasPassword(userId);
}
@Override
public boolean havePattern(int userId) throws RemoteException {
// Do we need a permissions check here?
- return new File(getLockPatternFilename(userId)).length() > 0;
+ return mStorage.hasPattern(userId);
}
private void maybeUpdateKeystore(String password, int userHandle) {
@@ -394,7 +347,7 @@
final byte[] hash = LockPatternUtils.patternToHash(
LockPatternUtils.stringToPattern(pattern));
- writeFile(getLockPatternFilename(userId), hash);
+ mStorage.writePatternHash(hash, userId);
}
@Override
@@ -403,68 +356,46 @@
maybeUpdateKeystore(password, userId);
- writeFile(getLockPasswordFilename(userId),
- mLockPatternUtils.passwordToHash(password, userId));
+ mStorage.writePasswordHash(mLockPatternUtils.passwordToHash(password, userId), userId);
}
@Override
public boolean checkPattern(String pattern, int userId) throws RemoteException {
checkPasswordReadPermission(userId);
- try {
- // Read all the bytes from the file
- RandomAccessFile raf = new RandomAccessFile(getLockPatternFilename(userId), "r");
- final byte[] stored = new byte[(int) raf.length()];
- int got = raf.read(stored, 0, stored.length);
- raf.close();
- if (got <= 0) {
- return true;
- }
- // Compare the hash from the file with the entered pattern's hash
- final byte[] hash = LockPatternUtils.patternToHash(
- LockPatternUtils.stringToPattern(pattern));
- final boolean matched = Arrays.equals(stored, hash);
- if (matched && !TextUtils.isEmpty(pattern)) {
- maybeUpdateKeystore(pattern, userId);
- }
- return matched;
- } catch (FileNotFoundException fnfe) {
- Slog.e(TAG, "Cannot read file " + fnfe);
- } catch (IOException ioe) {
- Slog.e(TAG, "Cannot read file " + ioe);
+ byte[] hash = LockPatternUtils.patternToHash(LockPatternUtils.stringToPattern(pattern));
+ byte[] storedHash = mStorage.readPatternHash(userId);
+
+ if (storedHash == null) {
+ return true;
}
- return true;
+
+ boolean matched = Arrays.equals(hash, storedHash);
+ if (matched && !TextUtils.isEmpty(pattern)) {
+ maybeUpdateKeystore(pattern, userId);
+ }
+ return matched;
}
@Override
public boolean checkPassword(String password, int userId) throws RemoteException {
checkPasswordReadPermission(userId);
- try {
- // Read all the bytes from the file
- RandomAccessFile raf = new RandomAccessFile(getLockPasswordFilename(userId), "r");
- final byte[] stored = new byte[(int) raf.length()];
- int got = raf.read(stored, 0, stored.length);
- raf.close();
- if (got <= 0) {
- return true;
- }
- // Compare the hash from the file with the entered password's hash
- final byte[] hash = mLockPatternUtils.passwordToHash(password, userId);
- final boolean matched = Arrays.equals(stored, hash);
- if (matched && !TextUtils.isEmpty(password)) {
- maybeUpdateKeystore(password, userId);
- }
- return matched;
- } catch (FileNotFoundException fnfe) {
- Slog.e(TAG, "Cannot read file " + fnfe);
- } catch (IOException ioe) {
- Slog.e(TAG, "Cannot read file " + ioe);
+ byte[] hash = mLockPatternUtils.passwordToHash(password, userId);
+ byte[] storedHash = mStorage.readPasswordHash(userId);
+
+ if (storedHash == null) {
+ return true;
}
- return true;
+
+ boolean matched = Arrays.equals(hash, storedHash);
+ if (matched && !TextUtils.isEmpty(password)) {
+ maybeUpdateKeystore(password, userId);
+ }
+ return matched;
}
@Override
- public boolean checkVoldPassword(int userId) throws RemoteException {
+ public boolean checkVoldPassword(int userId) throws RemoteException {
if (!mFirstCallToVold) {
return false;
}
@@ -512,166 +443,14 @@
public void removeUser(int userId) {
checkWritePermission(userId);
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- try {
- final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
- final UserInfo parentInfo = um.getProfileParent(userId);
- if (parentInfo == null) {
- // This user owns its lock settings files - safe to delete them
- File file = new File(getLockPasswordFilename(userId));
- if (file.exists()) {
- file.delete();
- }
- file = new File(getLockPatternFilename(userId));
- if (file.exists()) {
- file.delete();
- }
- }
-
- db.beginTransaction();
- db.delete(TABLE, COLUMN_USERID + "='" + userId + "'", null);
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
+ mStorage.removeUser(userId);
+ notifyObservers(null /* key */, userId);
final KeyStore ks = KeyStore.getInstance();
final int userUid = UserHandle.getUid(userId, Process.SYSTEM_UID);
ks.resetUid(userUid);
}
- private void writeFile(String name, byte[] hash) {
- try {
- // Write the hash to file
- RandomAccessFile raf = new RandomAccessFile(name, "rw");
- // Truncate the file if pattern is null, to clear the lock
- if (hash == null || hash.length == 0) {
- raf.setLength(0);
- } else {
- raf.write(hash, 0, hash.length);
- }
- raf.close();
- } catch (IOException ioe) {
- Slog.e(TAG, "Error writing to file " + ioe);
- }
- }
-
- private void writeToDb(String key, String value, int userId) {
- writeToDb(mOpenHelper.getWritableDatabase(), key, value, userId);
- notifyObservers(key, userId);
- }
-
- private void writeToDb(SQLiteDatabase db, String key, String value, int userId) {
- ContentValues cv = new ContentValues();
- cv.put(COLUMN_KEY, key);
- cv.put(COLUMN_USERID, userId);
- cv.put(COLUMN_VALUE, value);
-
- db.beginTransaction();
- try {
- db.delete(TABLE, COLUMN_KEY + "=? AND " + COLUMN_USERID + "=?",
- new String[] {key, Integer.toString(userId)});
- db.insert(TABLE, null, cv);
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
-
- private String readFromDb(String key, String defaultValue, int userId) {
- Cursor cursor;
- String result = defaultValue;
- SQLiteDatabase db = mOpenHelper.getReadableDatabase();
- if ((cursor = db.query(TABLE, COLUMNS_FOR_QUERY,
- COLUMN_USERID + "=? AND " + COLUMN_KEY + "=?",
- new String[] { Integer.toString(userId), key },
- null, null, null)) != null) {
- if (cursor.moveToFirst()) {
- result = cursor.getString(0);
- }
- cursor.close();
- }
- return result;
- }
-
- class DatabaseHelper extends SQLiteOpenHelper {
- private static final String TAG = "LockSettingsDB";
- private static final String DATABASE_NAME = "locksettings.db";
-
- private static final int DATABASE_VERSION = 2;
-
- public DatabaseHelper(Context context) {
- super(context, DATABASE_NAME, null, DATABASE_VERSION);
- setWriteAheadLoggingEnabled(true);
- }
-
- private void createTable(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE " + TABLE + " (" +
- "_id INTEGER PRIMARY KEY AUTOINCREMENT," +
- COLUMN_KEY + " TEXT," +
- COLUMN_USERID + " INTEGER," +
- COLUMN_VALUE + " TEXT" +
- ");");
- }
-
- @Override
- public void onCreate(SQLiteDatabase db) {
- createTable(db);
- initializeDefaults(db);
- }
-
- private void initializeDefaults(SQLiteDatabase db) {
- // Get the lockscreen default from a system property, if available
- boolean lockScreenDisable = SystemProperties.getBoolean("ro.lockscreen.disable.default",
- false);
- if (lockScreenDisable) {
- writeToDb(db, LockPatternUtils.DISABLE_LOCKSCREEN_KEY, "1", 0);
- }
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) {
- int upgradeVersion = oldVersion;
- if (upgradeVersion == 1) {
- // Set the initial value for {@link LockPatternUtils#LOCKSCREEN_WIDGETS_ENABLED}
- // during upgrade based on whether each user previously had widgets in keyguard.
- maybeEnableWidgetSettingForUsers(db);
- upgradeVersion = 2;
- }
-
- if (upgradeVersion != DATABASE_VERSION) {
- Log.w(TAG, "Failed to upgrade database!");
- }
- }
-
- private void maybeEnableWidgetSettingForUsers(SQLiteDatabase db) {
- final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
- final ContentResolver cr = mContext.getContentResolver();
- final List<UserInfo> users = um.getUsers();
- for (int i = 0; i < users.size(); i++) {
- final int userId = users.get(i).id;
- final boolean enabled = mLockPatternUtils.hasWidgetsEnabledInKeyguard(userId);
- Log.v(TAG, "Widget upgrade uid=" + userId + ", enabled="
- + enabled + ", w[]=" + mLockPatternUtils.getAppWidgets());
- loadSetting(db, LockPatternUtils.LOCKSCREEN_WIDGETS_ENABLED, userId, enabled);
- }
- }
-
- private void loadSetting(SQLiteDatabase db, String key, int userId, boolean value) {
- SQLiteStatement stmt = null;
- try {
- stmt = db.compileStatement(
- "INSERT OR REPLACE INTO locksettings(name,user,value) VALUES(?,?,?);");
- stmt.bindString(1, key);
- stmt.bindLong(2, userId);
- stmt.bindLong(3, value ? 1 : 0);
- stmt.execute();
- } finally {
- if (stmt != null) stmt.close();
- }
- }
- }
-
private static final String[] VALID_SETTINGS = new String[] {
LockPatternUtils.LOCKOUT_PERMANENT_KEY,
LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
diff --git a/services/core/java/com/android/server/LockSettingsStorage.java b/services/core/java/com/android/server/LockSettingsStorage.java
new file mode 100644
index 0000000..acbf8ef
--- /dev/null
+++ b/services/core/java/com/android/server/LockSettingsStorage.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.os.Environment;
+import android.os.UserManager;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+import static android.content.Context.USER_SERVICE;
+
+/**
+ * Storage for the lock settings service.
+ */
+class LockSettingsStorage {
+
+ private static final String TAG = "LockSettingsStorage";
+ private static final String TABLE = "locksettings";
+
+ private static final String COLUMN_KEY = "name";
+ private static final String COLUMN_USERID = "user";
+ private static final String COLUMN_VALUE = "value";
+
+ private static final String[] COLUMNS_FOR_QUERY = {
+ COLUMN_VALUE
+ };
+
+ private static final String SYSTEM_DIRECTORY = "/system/";
+ private static final String LOCK_PATTERN_FILE = "gesture.key";
+ private static final String LOCK_PASSWORD_FILE = "password.key";
+
+ private final DatabaseHelper mOpenHelper;
+ private final Context mContext;
+ private final Object mFileWriteLock = new Object();
+
+ LockSettingsStorage(Context context, Callback callback) {
+ mContext = context;
+ mOpenHelper = new DatabaseHelper(context, callback);
+ }
+
+ void writeKeyValue(String key, String value, int userId) {
+ writeKeyValue(mOpenHelper.getWritableDatabase(), key, value, userId);
+ }
+
+ void writeKeyValue(SQLiteDatabase db, String key, String value, int userId) {
+ ContentValues cv = new ContentValues();
+ cv.put(COLUMN_KEY, key);
+ cv.put(COLUMN_USERID, userId);
+ cv.put(COLUMN_VALUE, value);
+
+ db.beginTransaction();
+ try {
+ db.delete(TABLE, COLUMN_KEY + "=? AND " + COLUMN_USERID + "=?",
+ new String[] {key, Integer.toString(userId)});
+ db.insert(TABLE, null, cv);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+
+ }
+
+ String readKeyValue(String key, String defaultValue, int userId) {
+ Cursor cursor;
+ String result = defaultValue;
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ if ((cursor = db.query(TABLE, COLUMNS_FOR_QUERY,
+ COLUMN_USERID + "=? AND " + COLUMN_KEY + "=?",
+ new String[] { Integer.toString(userId), key },
+ null, null, null)) != null) {
+ if (cursor.moveToFirst()) {
+ result = cursor.getString(0);
+ }
+ cursor.close();
+ }
+ return result;
+ }
+
+ byte[] readPasswordHash(int userId) {
+ final byte[] stored = readFile(getLockPasswordFilename(userId), userId);
+ if (stored != null && stored.length > 0) {
+ return stored;
+ }
+ return null;
+ }
+
+ byte[] readPatternHash(int userId) {
+ final byte[] stored = readFile(getLockPatternFilename(userId), userId);
+ if (stored != null && stored.length > 0) {
+ return stored;
+ }
+ return null;
+ }
+
+ boolean hasPassword(int userId) {
+ return hasFile(getLockPasswordFilename(userId), userId);
+ }
+
+ boolean hasPattern(int userId) {
+ return hasFile(getLockPatternFilename(userId), userId);
+ }
+
+ private boolean hasFile(String name, int userId) {
+ byte[] contents = readFile(name, userId);
+ return contents != null && contents.length > 0;
+ }
+
+ private byte[] readFile(String name, int userId) {
+ RandomAccessFile raf = null;
+ byte[] stored = null;
+ try {
+ raf = new RandomAccessFile(name, "r");
+ stored = new byte[(int) raf.length()];
+ raf.readFully(stored, 0, stored.length);
+ raf.close();
+ } catch (IOException e) {
+ Slog.e(TAG, "Cannot read file " + e);
+ } finally {
+ if (raf != null) {
+ try {
+ raf.close();
+ } catch (IOException e) {
+ Slog.e(TAG, "Error closing file " + e);
+ }
+ }
+ }
+ return stored;
+ }
+
+ private void writeFile(String name, byte[] hash, int userId) {
+ synchronized (mFileWriteLock) {
+ RandomAccessFile raf = null;
+ try {
+ // Write the hash to file
+ raf = new RandomAccessFile(name, "rw");
+ // Truncate the file if pattern is null, to clear the lock
+ if (hash == null || hash.length == 0) {
+ raf.setLength(0);
+ } else {
+ raf.write(hash, 0, hash.length);
+ }
+ raf.close();
+ } catch (IOException e) {
+ Slog.e(TAG, "Error writing to file " + e);
+ } finally {
+ if (raf != null) {
+ try {
+ raf.close();
+ } catch (IOException e) {
+ Slog.e(TAG, "Error closing file " + e);
+ }
+ }
+ }
+ }
+ }
+
+ public void writePatternHash(byte[] hash, int userId) {
+ writeFile(getLockPatternFilename(userId), hash, userId);
+ }
+
+ public void writePasswordHash(byte[] hash, int userId) {
+ writeFile(getLockPasswordFilename(userId), hash, userId);
+ }
+
+
+ private String getLockPatternFilename(int userId) {
+ String dataSystemDirectory =
+ android.os.Environment.getDataDirectory().getAbsolutePath() +
+ SYSTEM_DIRECTORY;
+ userId = getUserParentOrSelfId(userId);
+ if (userId == 0) {
+ // Leave it in the same place for user 0
+ return dataSystemDirectory + LOCK_PATTERN_FILE;
+ } else {
+ return new File(Environment.getUserSystemDirectory(userId), LOCK_PATTERN_FILE)
+ .getAbsolutePath();
+ }
+ }
+
+ private String getLockPasswordFilename(int userId) {
+ userId = getUserParentOrSelfId(userId);
+ String dataSystemDirectory =
+ android.os.Environment.getDataDirectory().getAbsolutePath() +
+ SYSTEM_DIRECTORY;
+ if (userId == 0) {
+ // Leave it in the same place for user 0
+ return dataSystemDirectory + LOCK_PASSWORD_FILE;
+ } else {
+ return new File(Environment.getUserSystemDirectory(userId), LOCK_PASSWORD_FILE)
+ .getAbsolutePath();
+ }
+ }
+
+ private int getUserParentOrSelfId(int userId) {
+ if (userId != 0) {
+ final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
+ final UserInfo pi = um.getProfileParent(userId);
+ if (pi != null) {
+ return pi.id;
+ }
+ }
+ return userId;
+ }
+
+
+ public void removeUser(int userId) {
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+
+ final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
+ final UserInfo parentInfo = um.getProfileParent(userId);
+
+ synchronized (mFileWriteLock) {
+ if (parentInfo == null) {
+ // This user owns its lock settings files - safe to delete them
+ File file = new File(getLockPasswordFilename(userId));
+ if (file.exists()) {
+ file.delete();
+ }
+ file = new File(getLockPatternFilename(userId));
+ if (file.exists()) {
+ file.delete();
+ }
+ }
+ }
+
+ try {
+ db.beginTransaction();
+ db.delete(TABLE, COLUMN_USERID + "='" + userId + "'", null);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+
+ interface Callback {
+ void initialize(SQLiteDatabase db);
+ }
+
+ class DatabaseHelper extends SQLiteOpenHelper {
+ private static final String TAG = "LockSettingsDB";
+ private static final String DATABASE_NAME = "locksettings.db";
+
+ private static final int DATABASE_VERSION = 2;
+
+ private final Callback mCallback;
+
+ public DatabaseHelper(Context context, Callback callback) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ setWriteAheadLoggingEnabled(true);
+ mCallback = callback;
+ }
+
+ private void createTable(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + TABLE + " (" +
+ "_id INTEGER PRIMARY KEY AUTOINCREMENT," +
+ COLUMN_KEY + " TEXT," +
+ COLUMN_USERID + " INTEGER," +
+ COLUMN_VALUE + " TEXT" +
+ ");");
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ createTable(db);
+ mCallback.initialize(db);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) {
+ int upgradeVersion = oldVersion;
+ if (upgradeVersion == 1) {
+ // Previously migrated lock screen widget settings. Now defunct.
+ upgradeVersion = 2;
+ }
+
+ if (upgradeVersion != DATABASE_VERSION) {
+ Log.w(TAG, "Failed to upgrade database!");
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java
index 96f9ab0..8c3b020 100644
--- a/services/core/java/com/android/server/NativeDaemonConnector.java
+++ b/services/core/java/com/android/server/NativeDaemonConnector.java
@@ -176,7 +176,6 @@
if (buffer[i] == 0) {
final String rawEvent = new String(
buffer, start, i - start, StandardCharsets.UTF_8);
- log("RCV <- {" + rawEvent + "}");
boolean releaseWl = false;
try {
@@ -197,7 +196,6 @@
mResponseQueue.add(event.getCmdNumber(), event);
}
} catch (IllegalArgumentException e) {
- log("Problem parsing message: " + rawEvent + " - " + e);
} finally {
if (releaseWl) {
mWakeLock.acquire();
@@ -209,7 +207,6 @@
}
if (start == 0) {
final String rawEvent = new String(buffer, start, count, StandardCharsets.UTF_8);
- log("RCV incomplete <- {" + rawEvent + "}");
}
// We should end at the amount we read. If not, compact then
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 395e365..738917f 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -115,10 +115,10 @@
@Override
public boolean clearScores() {
- // Only the active scorer or the system (who can broadcast BROADCAST_SCORE_NETWORKS) should
- // be allowed to flush all scores.
+ // Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
+ // should be allowed to flush all scores.
if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
- mContext.checkCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS) ==
+ mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
PackageManager.PERMISSION_GRANTED) {
clearInternal();
return true;
@@ -130,16 +130,25 @@
@Override
public boolean setActiveScorer(String packageName) {
- mContext.enforceCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS, TAG);
+ // TODO: For now, since SCORE_NETWORKS requires an app to be privileged, we allow such apps
+ // to directly set the scorer app rather than having to use the consent dialog. The
+ // assumption is that anyone bundling a scorer app with the system is trusted by the OEM to
+ // do the right thing and not enable this feature without explaining it to the user.
+ // In the future, should this API be opened to 3p apps, we will need to lock this down and
+ // figure out another way to streamline the UX.
+
+ // mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
+ mContext.enforceCallingOrSelfPermission(permission.SCORE_NETWORKS, TAG);
+
return setScorerInternal(packageName);
}
@Override
public void disableScoring() {
- // Only the active scorer or the system (who can broadcast BROADCAST_SCORE_NETOWRKS) should
- // be allowed to disable scoring.
+ // Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
+ // should be allowed to disable scoring.
if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
- mContext.checkCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS) ==
+ mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
PackageManager.PERMISSION_GRANTED) {
// The return value is discarded here because at this point, the call should always
// succeed. The only reason for failure is if the new package is not a valid scorer, but
@@ -188,7 +197,7 @@
@Override
public void registerNetworkScoreCache(int networkType, INetworkScoreCache scoreCache) {
- mContext.enforceCallingOrSelfPermission(permission.BROADCAST_SCORE_NETWORKS, TAG);
+ mContext.enforceCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED, TAG);
synchronized (mScoreCaches) {
if (mScoreCaches.containsKey(networkType)) {
throw new IllegalArgumentException(
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 6f378fd..de90aa2 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -72,7 +72,7 @@
private final String mDataBlockFile;
private final Object mLock = new Object();
- private int mAllowedAppId = -1;
+ private int mAllowedUid = -1;
/*
* Separate lock for OEM unlock related operations as they can happen in parallel with regular
* block operations.
@@ -86,11 +86,11 @@
mContext = context;
mDataBlockFile = SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP);
mBlockDeviceSize = -1; // Load lazily
- mAllowedAppId = getAllowedAppId(UserHandle.USER_OWNER);
+ mAllowedUid = getAllowedUid(UserHandle.USER_OWNER);
}
- private int getAllowedAppId(int userHandle) {
+ private int getAllowedUid(int userHandle) {
String allowedPackage = mContext.getResources()
.getString(R.string.config_persistentDataPackageName);
PackageManager pm = mContext.getPackageManager();
@@ -101,7 +101,7 @@
// not expected
Slog.e(TAG, "not able to find package " + allowedPackage, e);
}
- return UserHandle.getAppId(allowedUid);
+ return allowedUid;
}
@Override
@@ -116,11 +116,17 @@
}
private void enforceUid(int callingUid) {
- if (UserHandle.getAppId(callingUid) != mAllowedAppId) {
+ if (callingUid != mAllowedUid) {
throw new SecurityException("uid " + callingUid + " not allowed to access PST");
}
}
+ private void enforceIsOwner() {
+ if (!Binder.getCallingUserHandle().isOwner()) {
+ throw new SecurityException("Only the Owner is allowed to change OEM unlock state");
+ }
+ }
+
private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
int totalDataSize;
int blockId = inputStream.readInt();
@@ -249,6 +255,7 @@
return;
}
enforceOemUnlockPermission();
+ enforceIsOwner();
FileOutputStream outputStream;
try {
outputStream = new FileOutputStream(new File(mDataBlockFile));
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index fcc5339..ba93213 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -596,7 +596,7 @@
+ " phoneId=" + phoneId + " state=" + state);
}
if (((r.events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) &&
- subIdMatch(r.subId, subId)) {
+ idMatch(r.subId, subId, phoneId)) {
try {
if (DBG) {
log("notifyServiceStateForSubscriber: callback.onSSC r=" + r
@@ -641,7 +641,7 @@
+ " phoneId=" + phoneId + " ss=" + signalStrength);
}
if (((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) &&
- subIdMatch(r.subId, subId)) {
+ idMatch(r.subId, subId, phoneId)) {
try {
if (DBG) {
log("notifySignalStrengthForSubscriber: callback.onSsS r=" + r
@@ -654,7 +654,7 @@
}
}
if (((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) &&
- subIdMatch(r.subId, subId)){
+ idMatch(r.subId, subId, phoneId)){
try {
int gsmSignalStrength = signalStrength.getGsmSignalStrength();
int ss = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
@@ -696,7 +696,7 @@
mCellInfo.set(phoneId, cellInfo);
for (Record r : mRecords) {
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) &&
- subIdMatch(r.subId, subId)) {
+ idMatch(r.subId, subId, phoneId)) {
try {
if (DBG_LOC) {
log("notifyCellInfo: mCellInfo=" + cellInfo + " r=" + r);
@@ -751,7 +751,7 @@
mMessageWaiting[phoneId] = mwi;
for (Record r : mRecords) {
if (((r.events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) &&
- subIdMatch(r.subId, subId)) {
+ idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onMessageWaitingIndicatorChanged(mwi);
} catch (RemoteException ex) {
@@ -782,7 +782,7 @@
mCallForwarding[phoneId] = cfi;
for (Record r : mRecords) {
if (((r.events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) &&
- subIdMatch(r.subId, subId)) {
+ idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onCallForwardingIndicatorChanged(cfi);
} catch (RemoteException ex) {
@@ -879,7 +879,7 @@
}
for (Record r : mRecords) {
if (((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) &&
- subIdMatch(r.subId, subId)) {
+ idMatch(r.subId, subId, phoneId)) {
try {
log("Notify data connection state changed on sub: " +
subId);
@@ -965,7 +965,7 @@
mCellLocation[phoneId] = cellLocation;
for (Record r : mRecords) {
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) &&
- subIdMatch(r.subId, subId)) {
+ idMatch(r.subId, subId, phoneId)) {
try {
if (DBG_LOC) {
log("notifyCellLocation: cellLocation=" + cellLocation
@@ -1386,7 +1386,7 @@
@Override
public String toString() {
- return mS + " " + mTime.toString() + " " + mSubId + " " + mPhoneId + " " + mState;
+ return mS + " Time " + mTime.toString() + " mSubId " + mSubId + " mPhoneId " + mPhoneId + " mState " + mState;
}
}
@@ -1429,8 +1429,12 @@
}
}
- boolean subIdMatch(int rSubId, int subId) {
+ boolean idMatch(int rSubId, int subId, int phoneId) {
if(rSubId == SubscriptionManager.DEFAULT_SUB_ID) {
+ if(subId < 0) {
+ // Invalid case, we need compare phoneId with default one.
+ return (mDefaultPhoneId == phoneId);
+ }
return (subId == mDefaultSubId);
} else {
return (rSubId == subId);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9179cc4..d9c1c01 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -952,10 +952,13 @@
*/
private boolean mWentToSleep = false;
+ static final int LOCK_SCREEN_HIDDEN = 0;
+ static final int LOCK_SCREEN_LEAVING = 1;
+ static final int LOCK_SCREEN_SHOWN = 2;
/**
* State of external call telling us if the lock screen is shown.
*/
- private boolean mLockScreenShown = false;
+ int mLockScreenShown = LOCK_SCREEN_HIDDEN;
/**
* Set if we are shutting down the system, similar to sleeping.
@@ -1836,8 +1839,8 @@
ComponentName cn = tr.intent.getComponent();
if (cn != null && cn.getPackageName().equals(packageName)) {
- // If the package name matches, remove the task and kill the process
- removeTaskByIdLocked(tr.taskId, ActivityManager.REMOVE_TASK_KILL_PROCESS);
+ // If the package name matches, remove the task
+ removeTaskByIdLocked(tr.taskId, true);
}
}
}
@@ -1891,9 +1894,7 @@
// Prune all the tasks with removed components from the list of recent tasks
synchronized (ActivityManagerService.this) {
for (int i = tasksToRemove.size() - 1; i >= 0; i--) {
- // Remove the task but don't kill the process (since other components in that
- // package may still be running and in the background)
- removeTaskByIdLocked(tasksToRemove.get(i), 0);
+ removeTaskByIdLocked(tasksToRemove.get(i), false);
}
}
}
@@ -4283,9 +4284,13 @@
// Keep track of the root activity of the task before we finish it
TaskRecord tr = r.task;
ActivityRecord rootR = tr.getRootActivity();
+ if (rootR == null) {
+ Slog.w(TAG, "Finishing task with all activities already finished");
+ }
// Do not allow task to finish in Lock Task mode.
if (tr == mStackSupervisor.mLockTaskModeTask) {
if (rootR == r) {
+ Slog.i(TAG, "Not finishing task in lock task mode");
mStackSupervisor.showLockTaskToast();
return false;
}
@@ -4304,6 +4309,7 @@
}
if (!resumeOK) {
+ Slog.i(TAG, "Not finishing activity because controller resumed");
return false;
}
}
@@ -4313,12 +4319,18 @@
boolean res;
if (finishTask && r == rootR) {
// If requested, remove the task that is associated to this activity only if it
- // was the root activity in the task. The result code and data is ignored because
- // we don't support returning them across task boundaries.
- res = removeTaskByIdLocked(tr.taskId, 0);
+ // was the root activity in the task. The result code and data is ignored
+ // because we don't support returning them across task boundaries.
+ res = removeTaskByIdLocked(tr.taskId, false);
+ if (!res) {
+ Slog.i(TAG, "Removing task failed to finish activity");
+ }
} else {
res = tr.stack.requestFinishActivityLocked(token, resultCode,
resultData, "app-request", true);
+ if (!res) {
+ Slog.i(TAG, "Failed to finish by app-request");
+ }
}
return res;
} finally {
@@ -5142,7 +5154,7 @@
tr.getBaseIntent().getComponent().getPackageName();
if (tr.userId != userId) continue;
if (!taskPackageName.equals(packageName)) continue;
- removeTaskByIdLocked(tr.taskId, 0);
+ removeTaskByIdLocked(tr.taskId, false);
}
}
@@ -6120,9 +6132,8 @@
synchronized (this) {
if (DEBUG_LOCKSCREEN) logLockScreen("");
mWindowManager.keyguardWaitingForActivityDrawn();
- if (mLockScreenShown) {
- mLockScreenShown = false;
- comeOutOfSleepIfNeededLocked();
+ if (mLockScreenShown == LOCK_SCREEN_SHOWN) {
+ mLockScreenShown = LOCK_SCREEN_LEAVING;
}
}
} finally {
@@ -8287,52 +8298,65 @@
return mTaskPersister.getTaskDescriptionIcon(filename);
}
- private void cleanUpRemovedTaskLocked(TaskRecord tr, int flags) {
+ private void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess) {
mRecentTasks.remove(tr);
tr.removedFromRecents(mTaskPersister);
- final boolean killProcesses = (flags&ActivityManager.REMOVE_TASK_KILL_PROCESS) != 0;
- Intent baseIntent = new Intent(
- tr.intent != null ? tr.intent : tr.affinityIntent);
- ComponentName component = baseIntent.getComponent();
+ ComponentName component = tr.getBaseIntent().getComponent();
if (component == null) {
- Slog.w(TAG, "Now component for base intent of task: " + tr);
+ Slog.w(TAG, "No component for base intent of task: " + tr);
return;
}
- // Find any running services associated with this app.
- mServices.cleanUpRemovedTaskLocked(tr, component, baseIntent);
+ if (!killProcess) {
+ return;
+ }
- if (killProcesses) {
- // Find any running processes associated with this app.
- final String pkg = component.getPackageName();
- ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>();
- ArrayMap<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap();
- for (int i=0; i<pmap.size(); i++) {
- SparseArray<ProcessRecord> uids = pmap.valueAt(i);
- for (int j=0; j<uids.size(); j++) {
- ProcessRecord proc = uids.valueAt(j);
- if (proc.userId != tr.userId) {
- continue;
- }
- if (!proc.pkgList.containsKey(pkg)) {
- continue;
- }
- procs.add(proc);
+ // Determine if the process(es) for this task should be killed.
+ final String pkg = component.getPackageName();
+ ArrayList<ProcessRecord> procsToKill = new ArrayList<ProcessRecord>();
+ ArrayMap<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap();
+ for (int i = 0; i < pmap.size(); i++) {
+
+ SparseArray<ProcessRecord> uids = pmap.valueAt(i);
+ for (int j = 0; j < uids.size(); j++) {
+ ProcessRecord proc = uids.valueAt(j);
+ if (proc.userId != tr.userId) {
+ // Don't kill process for a different user.
+ continue;
}
- }
-
- // Kill the running processes.
- for (int i=0; i<procs.size(); i++) {
- ProcessRecord pr = procs.get(i);
- if (pr == mHomeProcess) {
+ if (proc == mHomeProcess) {
// Don't kill the home process along with tasks from the same package.
continue;
}
- if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) {
- pr.kill("remove task", true);
- } else {
- pr.waitingToKill = "remove task";
+ if (!proc.pkgList.containsKey(pkg)) {
+ // Don't kill process that is not associated with this task.
+ continue;
}
+
+ for (int k = 0; k < proc.activities.size(); k++) {
+ TaskRecord otherTask = proc.activities.get(k).task;
+ if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
+ // Don't kill process(es) that has an activity in a different task that is
+ // also in recents.
+ return;
+ }
+ }
+
+ // Add process to kill list.
+ procsToKill.add(proc);
+ }
+ }
+
+ // Find any running services associated with this app and stop if needed.
+ mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent()));
+
+ // Kill the running processes.
+ for (int i = 0; i < procsToKill.size(); i++) {
+ ProcessRecord pr = procsToKill.get(i);
+ if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) {
+ pr.kill("remove task", true);
+ } else {
+ pr.waitingToKill = "remove task";
}
}
}
@@ -8341,37 +8365,37 @@
* Removes the task with the specified task id.
*
* @param taskId Identifier of the task to be removed.
- * @param flags Additional operational flags. May be 0 or
- * {@link ActivityManager#REMOVE_TASK_KILL_PROCESS}.
+ * @param killProcess Kill any process associated with the task if possible.
* @return Returns true if the given task was found and removed.
*/
- private boolean removeTaskByIdLocked(int taskId, int flags) {
+ private boolean removeTaskByIdLocked(int taskId, boolean killProcess) {
TaskRecord tr = recentTaskForIdLocked(taskId);
if (tr != null) {
tr.removeTaskActivitiesLocked();
- cleanUpRemovedTaskLocked(tr, flags);
+ cleanUpRemovedTaskLocked(tr, killProcess);
if (tr.isPersistable) {
notifyTaskPersisterLocked(null, true);
}
return true;
}
+ Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
return false;
}
@Override
- public boolean removeTask(int taskId, int flags) {
+ public boolean removeTask(int taskId) {
synchronized (this) {
enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS,
"removeTask()");
long ident = Binder.clearCallingIdentity();
try {
- return removeTaskByIdLocked(taskId, flags);
+ return removeTaskByIdLocked(taskId, true);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
-
+
/**
* TODO: Add mController hook
*/
@@ -9947,14 +9971,23 @@
Binder.restoreCallingIdentity(origId);
}
+ private String lockScreenShownToString() {
+ switch (mLockScreenShown) {
+ case LOCK_SCREEN_HIDDEN: return "LOCK_SCREEN_HIDDEN";
+ case LOCK_SCREEN_LEAVING: return "LOCK_SCREEN_LEAVING";
+ case LOCK_SCREEN_SHOWN: return "LOCK_SCREEN_SHOWN";
+ default: return "Unknown=" + mLockScreenShown;
+ }
+ }
+
void logLockScreen(String msg) {
if (DEBUG_LOCKSCREEN) Slog.d(TAG, Debug.getCallers(2) + ":" + msg +
- " mLockScreenShown=" + mLockScreenShown + " mWentToSleep=" +
+ " mLockScreenShown=" + lockScreenShownToString() + " mWentToSleep=" +
mWentToSleep + " mSleeping=" + mSleeping);
}
- private void comeOutOfSleepIfNeededLocked() {
- if ((!mWentToSleep && !mLockScreenShown) || mRunningVoice) {
+ void comeOutOfSleepIfNeededLocked() {
+ if ((!mWentToSleep && mLockScreenShown == LOCK_SCREEN_HIDDEN) || mRunningVoice) {
if (mSleeping) {
mSleeping = false;
mStackSupervisor.comeOutOfSleepIfNeededLocked();
@@ -9991,7 +10024,7 @@
long ident = Binder.clearCallingIdentity();
try {
if (DEBUG_LOCKSCREEN) logLockScreen(" shown=" + shown);
- mLockScreenShown = shown;
+ mLockScreenShown = shown ? LOCK_SCREEN_SHOWN : LOCK_SCREEN_HIDDEN;
comeOutOfSleepIfNeededLocked();
} finally {
Binder.restoreCallingIdentity(ident);
@@ -12757,9 +12790,9 @@
}
}
if (dumpPackage == null) {
- if (mSleeping || mWentToSleep || mLockScreenShown) {
+ if (mSleeping || mWentToSleep || mLockScreenShown != LOCK_SCREEN_HIDDEN) {
pw.println(" mSleeping=" + mSleeping + " mWentToSleep=" + mWentToSleep
- + " mLockScreenShown " + mLockScreenShown);
+ + " mLockScreenShown " + lockScreenShownToString());
}
if (mShuttingDown || mRunningVoice) {
pw.print(" mShuttingDown=" + mShuttingDown + " mRunningVoice=" + mRunningVoice);
@@ -19167,16 +19200,9 @@
synchronized (ActivityManagerService.this) {
long origId = Binder.clearCallingIdentity();
try {
- TaskRecord tr = recentTaskForIdLocked(mTaskId);
- if (tr == null) {
+ if (!removeTaskByIdLocked(mTaskId, false)) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
- // Only kill the process if we are not a new document
- int flags = tr.getBaseIntent().getFlags();
- boolean isDocument = (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) ==
- Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
- removeTaskByIdLocked(mTaskId,
- !isDocument ? ActivityManager.REMOVE_TASK_KILL_PROCESS : 0);
} finally {
Binder.restoreCallingIdentity(origId);
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index e1b8278..b955011 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -241,9 +241,6 @@
/** Run all ActivityStacks through this */
final ActivityStackSupervisor mStackSupervisor;
- /** Used to keep resumeTopActivityLocked() from being entered recursively */
- private boolean inResumeTopActivity;
-
static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1;
static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
@@ -835,7 +832,7 @@
prev.task.touchActiveTime();
clearLaunchTime(prev);
final ActivityRecord next = mStackSupervisor.topRunningActivityLocked();
- if (mService.mHasRecents && (next == null || next.noDisplay || next.task != prev.task)) {
+ if (mService.mHasRecents && (next == null || next.noDisplay || next.task != prev.task || uiSleeping)) {
prev.updateThumbnail(screenshotActivities(prev), null);
}
stopFullyDrawnTraceIfNeeded();
@@ -1468,7 +1465,7 @@
}
final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
- if (inResumeTopActivity) {
+ if (mStackSupervisor.inResumeTopActivity) {
// Don't even start recursing.
return false;
}
@@ -1476,10 +1473,14 @@
boolean result = false;
try {
// Protect against recursion.
- inResumeTopActivity = true;
+ mStackSupervisor.inResumeTopActivity = true;
+ if (mService.mLockScreenShown == ActivityManagerService.LOCK_SCREEN_LEAVING) {
+ mService.mLockScreenShown = ActivityManagerService.LOCK_SCREEN_HIDDEN;
+ mService.comeOutOfSleepIfNeededLocked();
+ }
result = resumeTopActivityInnerLocked(prev, options);
} finally {
- inResumeTopActivity = false;
+ mStackSupervisor.inResumeTopActivity = false;
}
return result;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index cc7026a..079df7d 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -274,6 +274,9 @@
final ArrayList<PendingActivityLaunch> mPendingActivityLaunches
= new ArrayList<PendingActivityLaunch>();
+ /** Used to keep resumeTopActivityLocked() from being entered recursively */
+ boolean inResumeTopActivity;
+
/**
* Description of a request to start a new activity, which has been held
* due to app switches being disabled.
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 5b22255..a4aff77 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -177,7 +177,7 @@
// 1280x800 or larger screen with around 1GB RAM. Values are in KB.
private final int[] mOomMinFreeHigh = new int[] {
73728, 92160, 110592,
- 129024, 147456, 184320
+ 129024, 225000, 325000
};
// The actual OOM killer memory levels we are using.
private final int[] mOomMinFree = new int[mOomAdj.length];
@@ -235,22 +235,16 @@
Slog.i("XXXXXX", "minfree_adj=" + minfree_adj + " minfree_abs=" + minfree_abs);
}
- // We've now baked in the increase to the basic oom values above, since
- // they seem to be useful more generally for devices that are tight on
- // memory than just for 64 bit. This should probably have some more
- // tuning done, so not deleting it quite yet...
- final boolean is64bit = false; //Build.SUPPORTED_64_BIT_ABIS.length > 0;
+ if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
+ // Increase the high min-free levels for cached processes for 64-bit
+ mOomMinFreeHigh[4] = 225000;
+ mOomMinFreeHigh[5] = 325000;
+ }
for (int i=0; i<mOomAdj.length; i++) {
int low = mOomMinFreeLow[i];
int high = mOomMinFreeHigh[i];
mOomMinFree[i] = (int)(low + ((high-low)*scale));
- if (is64bit) {
- // On 64 bit devices, we consume more baseline RAM, because 64 bit is cool!
- // To avoid being all pagey and stuff, scale up the memory levels to
- // give us some breathing room.
- mOomMinFree[i] = (3*mOomMinFree[i])/2;
- }
}
if (minfree_abs >= 0) {
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 238402f..debda14 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -191,8 +191,8 @@
}
try {
if (add) {
- mNetd.setPermission(CHANGE_NETWORK_STATE, toIntArray(network));
- mNetd.setPermission(CONNECTIVITY_INTERNAL, toIntArray(system));
+ mNetd.setPermission("NETWORK", toIntArray(network));
+ mNetd.setPermission("SYSTEM", toIntArray(system));
} else {
mNetd.clearPermission(toIntArray(network));
mNetd.clearPermission(toIntArray(system));
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 45d3771..d919bf6 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -21,7 +21,6 @@
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
-import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -120,6 +119,7 @@
// The minimum and maximum screen brightnesses.
private final int mScreenBrightnessRangeMinimum;
private final int mScreenBrightnessRangeMaximum;
+ private final float mDozeScaleFactor;
// Amount of time to delay auto-brightness after screen on while waiting for
// the light sensor to warm-up in milliseconds.
@@ -171,9 +171,12 @@
// The last screen auto-brightness gamma. (For printing in dump() only.)
private float mLastScreenAutoBrightnessGamma = 1.0f;
+ // Are we going to adjust brightness while dozing.
+ private boolean mDozing;
+
public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
- SensorManager sensorManager, Spline autoBrightnessSpline,
- int lightSensorWarmUpTime, int brightnessMin, int brightnessMax) {
+ SensorManager sensorManager, Spline autoBrightnessSpline, int lightSensorWarmUpTime,
+ int brightnessMin, int brightnessMax, float dozeScaleFactor) {
mCallbacks = callbacks;
mTwilight = LocalServices.getService(TwilightManager.class);
mSensorManager = sensorManager;
@@ -181,6 +184,7 @@
mScreenBrightnessRangeMinimum = brightnessMin;
mScreenBrightnessRangeMaximum = brightnessMax;
mLightSensorWarmUpTimeConfig = lightSensorWarmUpTime;
+ mDozeScaleFactor = dozeScaleFactor;
mHandler = new AutomaticBrightnessHandler(looper);
mAmbientLightRingBuffer = new AmbientLightRingBuffer();
@@ -195,11 +199,20 @@
}
public int getAutomaticScreenBrightness() {
+ if (mDozing) {
+ return (int) (mScreenAutoBrightness * mDozeScaleFactor);
+ }
return mScreenAutoBrightness;
}
- public void configure(boolean enable, float adjustment) {
- boolean changed = setLightSensorEnabled(enable);
+ public void configure(boolean enable, float adjustment, boolean dozing) {
+ // While dozing, the application processor may be suspended which will prevent us from
+ // receiving new information from the light sensor. On some devices, we may be able to
+ // switch to a wake-up light sensor instead but for now we will simply disable the sensor
+ // and hold onto the last computed screen auto brightness. We save the dozing flag for
+ // debugging purposes.
+ mDozing = dozing;
+ boolean changed = setLightSensorEnabled(enable && !dozing);
changed |= setScreenAutoBrightnessAdjustment(adjustment);
if (changed) {
updateAutoBrightness(false /*sendUpdate*/);
@@ -230,6 +243,7 @@
pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
pw.println(" mScreenAutoBrightnessAdjustment=" + mScreenAutoBrightnessAdjustment);
pw.println(" mLastScreenAutoBrightnessGamma=" + mLastScreenAutoBrightnessGamma);
+ pw.println(" mDozing=" + mDozing);
}
private boolean setLightSensorEnabled(boolean enable) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 1d8003b..81cd94b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -155,6 +155,9 @@
// True if auto-brightness should be used.
private boolean mUseSoftwareAutoBrightnessConfig;
+ // True if should use light sensor to automatically determine doze screen brightness.
+ private final boolean mAllowAutoBrightnessWhileDozingConfig;
+
// True if we should fade the screen while turning it off, false if we should play
// a stylish color fade animation instead.
private boolean mColorFadeFadesConfig;
@@ -295,6 +298,10 @@
mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
com.android.internal.R.bool.config_automatic_brightness_available);
+
+ mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean(
+ com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing);
+
if (mUseSoftwareAutoBrightnessConfig) {
int[] lux = resources.getIntArray(
com.android.internal.R.array.config_autoBrightnessLevels);
@@ -302,6 +309,9 @@
com.android.internal.R.array.config_autoBrightnessLcdBacklightValues);
int lightSensorWarmUpTimeConfig = resources.getInteger(
com.android.internal.R.integer.config_lightSensorWarmupTime);
+ final float dozeScaleFactor = resources.getFraction(
+ com.android.internal.R.fraction.config_screenAutoBrightnessDozeScaleFactor,
+ 1, 1);
Spline screenAutoBrightnessSpline = createAutoBrightnessSpline(lux, screenBrightness);
if (screenAutoBrightnessSpline == null) {
@@ -326,7 +336,7 @@
mAutomaticBrightnessController = new AutomaticBrightnessController(this,
handler.getLooper(), sensorManager, screenAutoBrightnessSpline,
lightSensorWarmUpTimeConfig, screenBrightnessRangeMinimum,
- mScreenBrightnessRangeMaximum);
+ mScreenBrightnessRangeMaximum, dozeScaleFactor);
}
}
@@ -523,7 +533,9 @@
} else {
state = Display.STATE_DOZE;
}
- brightness = mPowerRequest.dozeScreenBrightness;
+ if (!mAllowAutoBrightnessWhileDozingConfig) {
+ brightness = mPowerRequest.dozeScreenBrightness;
+ }
break;
case DisplayPowerRequest.POLICY_DIM:
case DisplayPowerRequest.POLICY_BRIGHT:
@@ -577,19 +589,16 @@
brightness = PowerManager.BRIGHTNESS_ON;
}
- // Use default brightness when dozing unless overridden.
- if (brightness < 0 && (state == Display.STATE_DOZE
- || state == Display.STATE_DOZE_SUSPEND)) {
- brightness = mScreenBrightnessDozeConfig;
- }
-
// Configure auto-brightness.
boolean autoBrightnessEnabled = false;
if (mAutomaticBrightnessController != null) {
+ final boolean autoBrightnessEnabledInDoze = mAllowAutoBrightnessWhileDozingConfig
+ && (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND);
autoBrightnessEnabled = mPowerRequest.useAutoBrightness
- && state == Display.STATE_ON && brightness < 0;
+ && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
+ && brightness < 0;
mAutomaticBrightnessController.configure(autoBrightnessEnabled,
- mPowerRequest.screenAutoBrightnessAdjustment);
+ mPowerRequest.screenAutoBrightnessAdjustment, state != Display.STATE_ON);
}
// Apply auto-brightness.
@@ -612,6 +621,12 @@
mAppliedAutoBrightness = false;
}
+ // Use default brightness when dozing unless overridden.
+ if (brightness < 0 && (state == Display.STATE_DOZE
+ || state == Display.STATE_DOZE_SUSPEND)) {
+ brightness = mScreenBrightnessDozeConfig;
+ }
+
// Apply manual brightness.
// Use the current brightness setting from the request, which is expected
// provide a nominal default value for the case where auto-brightness
@@ -1024,6 +1039,8 @@
pw.println(" mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
pw.println(" mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig);
+ pw.println(" mAllowAutoBrightnessWhileDozingConfig=" +
+ mAllowAutoBrightnessWhileDozingConfig);
pw.println(" mColorFadeFadesConfig=" + mColorFadeFadesConfig);
mHandler.runWithScissors(new Runnable() {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 6bae761..f0f7973 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1029,6 +1029,7 @@
OneTouchRecordAction action = actions.get(0);
if (action.getRecorderAddress() != message.getSource()) {
announceOneTouchRecordResult(
+ message.getSource(),
HdmiControlManager.ONE_TOUCH_RECORD_PREVIOUS_RECORDING_IN_PROGRESS);
}
return super.handleRecordTvScreen(message);
@@ -1047,20 +1048,20 @@
protected boolean handleTimerClearedStatus(HdmiCecMessage message) {
byte[] params = message.getParams();
int timerClearedStatusData = params[0] & 0xFF;
- announceTimerRecordingResult(timerClearedStatusData);
+ announceTimerRecordingResult(message.getSource(), timerClearedStatusData);
return true;
}
- void announceOneTouchRecordResult(int result) {
- mService.invokeOneTouchRecordResult(result);
+ void announceOneTouchRecordResult(int recorderAddress, int result) {
+ mService.invokeOneTouchRecordResult(recorderAddress, result);
}
- void announceTimerRecordingResult(int result) {
- mService.invokeTimerRecordingResult(result);
+ void announceTimerRecordingResult(int recorderAddress, int result) {
+ mService.invokeTimerRecordingResult(recorderAddress, result);
}
- void announceClearTimerRecordingResult(int result) {
- mService.invokeClearTimerRecordingResult(result);
+ void announceClearTimerRecordingResult(int recorderAddress, int result) {
+ mService.invokeClearTimerRecordingResult(recorderAddress, result);
}
private boolean isMessageForSystemAudio(HdmiCecMessage message) {
@@ -1528,19 +1529,21 @@
assertRunOnServiceThread();
if (!mService.isControlEnabled()) {
Slog.w(TAG, "Can not start one touch record. CEC control is disabled.");
- announceOneTouchRecordResult(ONE_TOUCH_RECORD_CEC_DISABLED);
+ announceOneTouchRecordResult(recorderAddress, ONE_TOUCH_RECORD_CEC_DISABLED);
return Constants.ABORT_NOT_IN_CORRECT_MODE;
}
if (!checkRecorder(recorderAddress)) {
Slog.w(TAG, "Invalid recorder address:" + recorderAddress);
- announceOneTouchRecordResult(ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION);
+ announceOneTouchRecordResult(recorderAddress,
+ ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION);
return Constants.ABORT_NOT_IN_CORRECT_MODE;
}
if (!checkRecordSource(recordSource)) {
Slog.w(TAG, "Invalid record source." + Arrays.toString(recordSource));
- announceOneTouchRecordResult(ONE_TOUCH_RECORD_FAIL_TO_RECORD_DISPLAYED_SCREEN);
+ announceOneTouchRecordResult(recorderAddress,
+ ONE_TOUCH_RECORD_FAIL_TO_RECORD_DISPLAYED_SCREEN);
return Constants.ABORT_UNABLE_TO_DETERMINE;
}
@@ -1555,13 +1558,14 @@
assertRunOnServiceThread();
if (!mService.isControlEnabled()) {
Slog.w(TAG, "Can not stop one touch record. CEC control is disabled.");
- announceOneTouchRecordResult(ONE_TOUCH_RECORD_CEC_DISABLED);
+ announceOneTouchRecordResult(recorderAddress, ONE_TOUCH_RECORD_CEC_DISABLED);
return;
}
if (!checkRecorder(recorderAddress)) {
Slog.w(TAG, "Invalid recorder address:" + recorderAddress);
- announceOneTouchRecordResult(ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION);
+ announceOneTouchRecordResult(recorderAddress,
+ ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION);
return;
}
@@ -1587,13 +1591,14 @@
assertRunOnServiceThread();
if (!mService.isControlEnabled()) {
Slog.w(TAG, "Can not start one touch record. CEC control is disabled.");
- announceTimerRecordingResult(TIMER_RECORDING_RESULT_EXTRA_CEC_DISABLED);
+ announceTimerRecordingResult(recorderAddress,
+ TIMER_RECORDING_RESULT_EXTRA_CEC_DISABLED);
return;
}
if (!checkRecorder(recorderAddress)) {
Slog.w(TAG, "Invalid recorder address:" + recorderAddress);
- announceTimerRecordingResult(
+ announceTimerRecordingResult(recorderAddress,
TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION);
return;
}
@@ -1601,6 +1606,7 @@
if (!checkTimerRecordingSource(sourceType, recordSource)) {
Slog.w(TAG, "Invalid record source." + Arrays.toString(recordSource));
announceTimerRecordingResult(
+ recorderAddress,
TIMER_RECORDING_RESULT_EXTRA_FAIL_TO_RECORD_SELECTED_SOURCE);
return;
}
@@ -1621,26 +1627,29 @@
assertRunOnServiceThread();
if (!mService.isControlEnabled()) {
Slog.w(TAG, "Can not start one touch record. CEC control is disabled.");
- announceClearTimerRecordingResult(CLEAR_TIMER_STATUS_CEC_DISABLE);
+ announceClearTimerRecordingResult(recorderAddress, CLEAR_TIMER_STATUS_CEC_DISABLE);
return;
}
if (!checkRecorder(recorderAddress)) {
Slog.w(TAG, "Invalid recorder address:" + recorderAddress);
- announceClearTimerRecordingResult(CLEAR_TIMER_STATUS_CHECK_RECORDER_CONNECTION);
+ announceClearTimerRecordingResult(recorderAddress,
+ CLEAR_TIMER_STATUS_CHECK_RECORDER_CONNECTION);
return;
}
if (!checkTimerRecordingSource(sourceType, recordSource)) {
Slog.w(TAG, "Invalid record source." + Arrays.toString(recordSource));
- announceClearTimerRecordingResult(CLEAR_TIMER_STATUS_FAIL_TO_CLEAR_SELECTED_SOURCE);
+ announceClearTimerRecordingResult(recorderAddress,
+ CLEAR_TIMER_STATUS_FAIL_TO_CLEAR_SELECTED_SOURCE);
return;
}
sendClearTimerMessage(recorderAddress, sourceType, recordSource);
}
- private void sendClearTimerMessage(int recorderAddress, int sourceType, byte[] recordSource) {
+ private void sendClearTimerMessage(final int recorderAddress, int sourceType,
+ byte[] recordSource) {
HdmiCecMessage message = null;
switch (sourceType) {
case TIMER_RECORDING_TYPE_DIGITAL:
@@ -1657,7 +1666,8 @@
break;
default:
Slog.w(TAG, "Invalid source type:" + recorderAddress);
- announceClearTimerRecordingResult(CLEAR_TIMER_STATUS_FAIL_TO_CLEAR_SELECTED_SOURCE);
+ announceClearTimerRecordingResult(recorderAddress,
+ CLEAR_TIMER_STATUS_FAIL_TO_CLEAR_SELECTED_SOURCE);
return;
}
@@ -1665,7 +1675,7 @@
@Override
public void onSendCompleted(int error) {
if (error != Constants.SEND_RESULT_SUCCESS) {
- announceClearTimerRecordingResult(
+ announceClearTimerRecordingResult(recorderAddress,
CLEAR_TIMER_STATUS_FAIL_TO_CLEAR_SELECTED_SOURCE);
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index d703989..53740fe 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -29,11 +29,16 @@
static final int ERROR_SOURCE = 1;
static final int ERROR_DESTINATION = 2;
static final int ERROR_PARAMETER = 3;
+ static final int ERROR_PARAMETER_SHORT = 4;
private final HdmiControlService mService;
interface ParameterValidator {
- boolean isValid(byte[] params);
+ /**
+ * @return errorCode errorCode can be {@link #OK}, {@link #ERROR_PARAMETER} or
+ * {@link #ERROR_PARAMETER_SHORT}.
+ */
+ int isValid(byte[] params);
}
// Only the direct addressing is allowed.
@@ -74,7 +79,7 @@
addValidationInfo(Constants.MESSAGE_SET_STREAM_PATH,
physicalAddressValidator, DEST_BROADCAST);
addValidationInfo(Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST,
- physicalAddressValidator, DEST_DIRECT);
+ new SystemAudioModeRequestValidator(), DEST_DIRECT);
// Messages have no parameter.
FixedLengthValidator noneValidator = new FixedLengthValidator(0);
@@ -213,9 +218,10 @@
}
// Check the parameter type.
- if (!info.parameterValidator.isValid(message.getParams())) {
+ int errorCode = info.parameterValidator.isValid(message.getParams());
+ if (errorCode != OK) {
HdmiLogger.warning("Unexpected parameters: " + message);
- return ERROR_PARAMETER;
+ return errorCode;
}
return OK;
}
@@ -228,8 +234,10 @@
}
@Override
- public boolean isValid(byte[] params) {
- return params.length == mLength;
+ public int isValid(byte[] params) {
+ // If the length is longer than expected, we assume it's OK since the parameter can be
+ // extended in the future version.
+ return params.length < mLength ? ERROR_PARAMETER_SHORT : OK;
}
}
@@ -243,8 +251,8 @@
}
@Override
- public boolean isValid(byte[] params) {
- return params.length >= mMinLength && params.length <= mMaxLength;
+ public int isValid(byte[] params) {
+ return params.length < mMinLength ? ERROR_PARAMETER_SHORT : OK;
}
}
@@ -270,8 +278,7 @@
* Check if the given type is valid. A valid type is one of the actual logical device types
* defined in the standard ({@link HdmiDeviceInfo#DEVICE_TV},
* {@link HdmiDeviceInfo#DEVICE_PLAYBACK}, {@link HdmiDeviceInfo#DEVICE_TUNER},
- * {@link HdmiDeviceInfo#DEVICE_RECORDER}, and
- * {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM}).
+ * {@link HdmiDeviceInfo#DEVICE_RECORDER}, and {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM}).
*
* @param type device type
* @return true if the given type is valid
@@ -282,33 +289,49 @@
&& type != HdmiDeviceInfo.DEVICE_RESERVED;
}
+ private static int toErrorCode(boolean success) {
+ return success ? OK : ERROR_PARAMETER;
+ }
+
private class PhysicalAddressValidator implements ParameterValidator {
@Override
- public boolean isValid(byte[] params) {
- if (params.length != 2) {
- return false;
+ public int isValid(byte[] params) {
+ if (params.length < 2) {
+ return ERROR_PARAMETER_SHORT;
}
- return isValidPhysicalAddress(params, 0);
+ return toErrorCode(isValidPhysicalAddress(params, 0));
+ }
+ }
+
+ private class SystemAudioModeRequestValidator extends PhysicalAddressValidator {
+ @Override
+ public int isValid(byte[] params) {
+ // TV can send <System Audio Mode Request> with no parameters to terminate system audio.
+ if (params.length == 0) {
+ return OK;
+ }
+ return super.isValid(params);
}
}
private class ReportPhysicalAddressValidator implements ParameterValidator {
@Override
- public boolean isValid(byte[] params) {
- if (params.length != 3) {
- return false;
+ public int isValid(byte[] params) {
+ if (params.length < 3) {
+ return ERROR_PARAMETER_SHORT;
}
- return isValidPhysicalAddress(params, 0) && isValidType(params[2]);
+ return toErrorCode(isValidPhysicalAddress(params, 0) && isValidType(params[2]));
}
}
private class RoutingChangeValidator implements ParameterValidator {
@Override
- public boolean isValid(byte[] params) {
- if (params.length != 4) {
- return false;
+ public int isValid(byte[] params) {
+ if (params.length < 4) {
+ return ERROR_PARAMETER_SHORT;
}
- return isValidPhysicalAddress(params, 0) && isValidPhysicalAddress(params, 2);
+ return toErrorCode(
+ isValidPhysicalAddress(params, 0) && isValidPhysicalAddress(params, 2));
}
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index aeb21db..907a49a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -719,7 +719,8 @@
assertRunOnServiceThread();
int errorCode = mMessageValidator.isValid(message);
if (errorCode != HdmiCecMessageValidator.OK) {
- // We'll not response on the messages with the invalid source or destination.
+ // We'll not response on the messages with the invalid source or destination
+ // or with parameter length shorter than specified in the standard.
if (errorCode == HdmiCecMessageValidator.ERROR_PARAMETER) {
maySendFeatureAbortCommand(message, Constants.ABORT_INVALID_OPERAND);
}
@@ -1661,11 +1662,11 @@
}
}
- void invokeOneTouchRecordResult(int result) {
+ void invokeOneTouchRecordResult(int recorderAddress, int result) {
synchronized (mLock) {
if (mRecordListenerRecord != null) {
try {
- mRecordListenerRecord.mListener.onOneTouchRecordResult(result);
+ mRecordListenerRecord.mListener.onOneTouchRecordResult(recorderAddress, result);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call onOneTouchRecordResult.", e);
}
@@ -1673,11 +1674,11 @@
}
}
- void invokeTimerRecordingResult(int result) {
+ void invokeTimerRecordingResult(int recorderAddress, int result) {
synchronized (mLock) {
if (mRecordListenerRecord != null) {
try {
- mRecordListenerRecord.mListener.onTimerRecordingResult(result);
+ mRecordListenerRecord.mListener.onTimerRecordingResult(recorderAddress, result);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call onTimerRecordingResult.", e);
}
@@ -1685,11 +1686,12 @@
}
}
- void invokeClearTimerRecordingResult(int result) {
+ void invokeClearTimerRecordingResult(int recorderAddress, int result) {
synchronized (mLock) {
if (mRecordListenerRecord != null) {
try {
- mRecordListenerRecord.mListener.onClearTimerRecordingResult(result);
+ mRecordListenerRecord.mListener.onClearTimerRecordingResult(recorderAddress,
+ result);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call onClearTimerRecordingResult.", e);
}
diff --git a/services/core/java/com/android/server/hdmi/OneTouchRecordAction.java b/services/core/java/com/android/server/hdmi/OneTouchRecordAction.java
index 906944b..d80b81f6 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchRecordAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchRecordAction.java
@@ -64,6 +64,7 @@
// if failed to send <Record On>, display error message and finish action.
if (error != Constants.SEND_RESULT_SUCCESS) {
tv().announceOneTouchRecordResult(
+ mRecorderAddress,
ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION);
finish();
return;
@@ -94,7 +95,7 @@
}
int recordStatus = cmd.getParams()[0];
- tv().announceOneTouchRecordResult(recordStatus);
+ tv().announceOneTouchRecordResult(mRecorderAddress, recordStatus);
Slog.i(TAG, "Got record status:" + recordStatus + " from " + cmd.getSource());
// If recording started successfully, change state and keep this action until <Record Off>
@@ -121,7 +122,8 @@
return;
}
- tv().announceOneTouchRecordResult(ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION);
+ tv().announceOneTouchRecordResult(mRecorderAddress,
+ ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION);
finish();
}
diff --git a/services/core/java/com/android/server/hdmi/TimerRecordingAction.java b/services/core/java/com/android/server/hdmi/TimerRecordingAction.java
index 5fcbc91..16fc25f 100644
--- a/services/core/java/com/android/server/hdmi/TimerRecordingAction.java
+++ b/services/core/java/com/android/server/hdmi/TimerRecordingAction.java
@@ -74,7 +74,7 @@
mRecorderAddress, mRecordSource);
break;
default:
- tv().announceTimerRecordingResult(
+ tv().announceTimerRecordingResult(mRecorderAddress,
TIMER_RECORDING_RESULT_EXTRA_FAIL_TO_RECORD_SELECTED_SOURCE);
finish();
return;
@@ -83,7 +83,7 @@
@Override
public void onSendCompleted(int error) {
if (error != Constants.SEND_RESULT_SUCCESS) {
- tv().announceTimerRecordingResult(
+ tv().announceTimerRecordingResult(mRecorderAddress,
TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION);
finish();
return;
@@ -114,7 +114,7 @@
byte[] timerStatusData = cmd.getParams();
// [Timer Status Data] should be one or three bytes.
if (timerStatusData.length == 1 || timerStatusData.length == 3) {
- tv().announceTimerRecordingResult(bytesToInt(timerStatusData));
+ tv().announceTimerRecordingResult(mRecorderAddress, bytesToInt(timerStatusData));
Slog.i(TAG, "Received [Timer Status Data]:" + Arrays.toString(timerStatusData));
} else {
Slog.w(TAG, "Invalid [Timer Status Data]:" + Arrays.toString(timerStatusData));
@@ -138,7 +138,8 @@
}
int reason = params[1] & 0xFF;
Slog.i(TAG, "[Feature Abort] for " + messageType + " reason:" + reason);
- tv().announceTimerRecordingResult(TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION);
+ tv().announceTimerRecordingResult(mRecorderAddress,
+ TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION);
finish();
return true;
}
@@ -163,7 +164,8 @@
return;
}
- tv().announceTimerRecordingResult(TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION);
+ tv().announceTimerRecordingResult(mRecorderAddress,
+ TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION);
finish();
}
}
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index c2cb4b1..86aa516 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -162,6 +162,9 @@
private static final int GPS_CAPABILITY_MSA = 0x0000004;
private static final int GPS_CAPABILITY_SINGLE_SHOT = 0x0000008;
private static final int GPS_CAPABILITY_ON_DEMAND_TIME = 0x0000010;
+ private static final int GPS_CAPABILITY_GEOFENCING = 0x0000020;
+ private static final int GPS_CAPABILITY_MEASUREMENTS = 0x0000040;
+ private static final int GPS_CAPABILITY_NAV_MESSAGES = 0x0000080;
// The AGPS SUPL mode
private static final int AGPS_SUPL_MODE_MSA = 0x02;
@@ -348,20 +351,9 @@
private final ILocationManager mILocationManager;
private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
private Bundle mLocationExtras = new Bundle();
- private GpsStatusListenerHelper mListenerHelper = new GpsStatusListenerHelper() {
- @Override
- protected boolean isSupported() {
- return GpsLocationProvider.isSupported();
- }
-
- @Override
- protected boolean registerWithService() {
- return true;
- }
-
- @Override
- protected void unregisterFromService() {}
- };
+ private final GpsStatusListenerHelper mListenerHelper;
+ private final GpsMeasurementsProvider mGpsMeasurementsProvider;
+ private final GpsNavigationMessageProvider mGpsNavigationMessageProvider;
// Handler for processing events
private Handler mHandler;
@@ -409,41 +401,6 @@
}
};
- private final GpsMeasurementsProvider mGpsMeasurementsProvider = new GpsMeasurementsProvider() {
- @Override
- public boolean isSupported() {
- return native_is_measurement_supported();
- }
-
- @Override
- protected boolean registerWithService() {
- return native_start_measurement_collection();
- }
-
- @Override
- protected void unregisterFromService() {
- native_stop_measurement_collection();
- }
- };
-
- private final GpsNavigationMessageProvider mGpsNavigationMessageProvider =
- new GpsNavigationMessageProvider() {
- @Override
- protected boolean isSupported() {
- return native_is_navigation_message_supported();
- }
-
- @Override
- protected boolean registerWithService() {
- return native_start_navigation_message_collection();
- }
-
- @Override
- protected void unregisterFromService() {
- native_stop_navigation_message_collection();
- }
- };
-
public IGpsStatusProvider getGpsStatusProvider() {
return mGpsStatusProvider;
}
@@ -696,6 +653,62 @@
mHandler.getLooper());
}
});
+
+ mListenerHelper = new GpsStatusListenerHelper(mHandler) {
+ @Override
+ protected boolean isAvailableInPlatform() {
+ return GpsLocationProvider.isSupported();
+ }
+
+ @Override
+ protected boolean isGpsEnabled() {
+ return isEnabled();
+ }
+ };
+
+ mGpsMeasurementsProvider = new GpsMeasurementsProvider(mHandler) {
+ @Override
+ public boolean isAvailableInPlatform() {
+ return native_is_measurement_supported();
+ }
+
+ @Override
+ protected boolean registerWithService() {
+ return native_start_measurement_collection();
+ }
+
+ @Override
+ protected void unregisterFromService() {
+ native_stop_measurement_collection();
+ }
+
+ @Override
+ protected boolean isGpsEnabled() {
+ return isEnabled();
+ }
+ };
+
+ mGpsNavigationMessageProvider = new GpsNavigationMessageProvider(mHandler) {
+ @Override
+ protected boolean isAvailableInPlatform() {
+ return native_is_navigation_message_supported();
+ }
+
+ @Override
+ protected boolean registerWithService() {
+ return native_start_navigation_message_collection();
+ }
+
+ @Override
+ protected void unregisterFromService() {
+ native_stop_navigation_message_collection();
+ }
+
+ @Override
+ protected boolean isGpsEnabled() {
+ return isEnabled();
+ }
+ };
}
private void listenForBroadcasts() {
@@ -1445,7 +1458,9 @@
}
if (wasNavigating != mNavigating) {
- mListenerHelper.onStatusChanged(mNavigating);
+ mListenerHelper.onGpsEnabledChanged(mNavigating);
+ mGpsMeasurementsProvider.onGpsEnabledChanged(mNavigating);
+ mGpsNavigationMessageProvider.onGpsEnabledChanged(mNavigating);
// send an intent to notify that the GPS has been enabled or disabled
Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
@@ -1598,6 +1613,11 @@
mPeriodicTimeInjection = true;
requestUtcTime();
}
+
+ mGpsMeasurementsProvider.onCapabilitiesUpdated(
+ (capabilities & GPS_CAPABILITY_MEASUREMENTS) == GPS_CAPABILITY_MEASUREMENTS);
+ mGpsNavigationMessageProvider.onCapabilitiesUpdated(
+ (capabilities & GPS_CAPABILITY_NAV_MESSAGES) == GPS_CAPABILITY_NAV_MESSAGES);
}
/**
diff --git a/services/core/java/com/android/server/location/GpsMeasurementsProvider.java b/services/core/java/com/android/server/location/GpsMeasurementsProvider.java
index 1c48257..0514e0c 100644
--- a/services/core/java/com/android/server/location/GpsMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/GpsMeasurementsProvider.java
@@ -18,7 +18,9 @@
import android.location.GpsMeasurementsEvent;
import android.location.IGpsMeasurementsListener;
+import android.os.Handler;
import android.os.RemoteException;
+import android.util.Log;
/**
* An base implementation for GPS measurements provider.
@@ -29,8 +31,10 @@
*/
public abstract class GpsMeasurementsProvider
extends RemoteListenerHelper<IGpsMeasurementsListener> {
- public GpsMeasurementsProvider() {
- super("GpsMeasurementsProvider");
+ private static final String TAG = "GpsMeasurementsProvider";
+
+ public GpsMeasurementsProvider(Handler handler) {
+ super(handler, TAG);
}
public void onMeasurementsAvailable(final GpsMeasurementsEvent event) {
@@ -41,7 +45,56 @@
listener.onGpsMeasurementsReceived(event);
}
};
-
foreach(operation);
}
+
+ public void onCapabilitiesUpdated(boolean isGpsMeasurementsSupported) {
+ int status = isGpsMeasurementsSupported ?
+ GpsMeasurementsEvent.STATUS_READY :
+ GpsMeasurementsEvent.STATUS_NOT_SUPPORTED;
+ setSupported(isGpsMeasurementsSupported, new StatusChangedOperation(status));
+ }
+
+ @Override
+ protected ListenerOperation<IGpsMeasurementsListener> getHandlerOperation(int result) {
+ final int status;
+ switch (result) {
+ case RESULT_SUCCESS:
+ status = GpsMeasurementsEvent.STATUS_READY;
+ break;
+ case RESULT_NOT_AVAILABLE:
+ case RESULT_NOT_SUPPORTED:
+ case RESULT_INTERNAL_ERROR:
+ status = GpsMeasurementsEvent.STATUS_NOT_SUPPORTED;
+ break;
+ case RESULT_GPS_LOCATION_DISABLED:
+ status = GpsMeasurementsEvent.STATUS_GPS_LOCATION_DISABLED;
+ break;
+ default:
+ Log.v(TAG, "Unhandled addListener result: " + result);
+ return null;
+ }
+ return new StatusChangedOperation(status);
+ }
+
+ @Override
+ protected void handleGpsEnabledChanged(boolean enabled) {
+ int status = enabled ?
+ GpsMeasurementsEvent.STATUS_READY :
+ GpsMeasurementsEvent.STATUS_GPS_LOCATION_DISABLED;
+ foreach(new StatusChangedOperation(status));
+ }
+
+ private class StatusChangedOperation implements ListenerOperation<IGpsMeasurementsListener> {
+ private final int mStatus;
+
+ public StatusChangedOperation(int status) {
+ mStatus = status;
+ }
+
+ @Override
+ public void execute(IGpsMeasurementsListener listener) throws RemoteException {
+ listener.onStatusChanged(mStatus);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/GpsNavigationMessageProvider.java b/services/core/java/com/android/server/location/GpsNavigationMessageProvider.java
index fca7378..13d22fc 100644
--- a/services/core/java/com/android/server/location/GpsNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/GpsNavigationMessageProvider.java
@@ -18,7 +18,9 @@
import android.location.GpsNavigationMessageEvent;
import android.location.IGpsNavigationMessageListener;
+import android.os.Handler;
import android.os.RemoteException;
+import android.util.Log;
/**
* An base implementation for GPS navigation messages provider.
@@ -29,8 +31,10 @@
*/
public abstract class GpsNavigationMessageProvider
extends RemoteListenerHelper<IGpsNavigationMessageListener> {
- public GpsNavigationMessageProvider() {
- super("GpsNavigationMessageProvider");
+ private static final String TAG = "GpsNavigationMessageProvider";
+
+ public GpsNavigationMessageProvider(Handler handler) {
+ super(handler, TAG);
}
public void onNavigationMessageAvailable(final GpsNavigationMessageEvent event) {
@@ -42,7 +46,57 @@
listener.onGpsNavigationMessageReceived(event);
}
};
-
foreach(operation);
}
+
+ public void onCapabilitiesUpdated(boolean isGpsNavigationMessageSupported) {
+ int status = isGpsNavigationMessageSupported ?
+ GpsNavigationMessageEvent.STATUS_READY :
+ GpsNavigationMessageEvent.STATUS_NOT_SUPPORTED;
+ setSupported(isGpsNavigationMessageSupported, new StatusChangedOperation(status));
+ }
+
+ @Override
+ protected ListenerOperation<IGpsNavigationMessageListener> getHandlerOperation(int result) {
+ final int status;
+ switch (result) {
+ case RESULT_SUCCESS:
+ status = GpsNavigationMessageEvent.STATUS_READY;
+ break;
+ case RESULT_NOT_AVAILABLE:
+ case RESULT_NOT_SUPPORTED:
+ case RESULT_INTERNAL_ERROR:
+ status = GpsNavigationMessageEvent.STATUS_NOT_SUPPORTED;
+ break;
+ case RESULT_GPS_LOCATION_DISABLED:
+ status = GpsNavigationMessageEvent.STATUS_GPS_LOCATION_DISABLED;
+ break;
+ default:
+ Log.v(TAG, "Unhandled addListener result: " + result);
+ return null;
+ }
+ return new StatusChangedOperation(status);
+ }
+
+ @Override
+ protected void handleGpsEnabledChanged(boolean enabled) {
+ int status = enabled ?
+ GpsNavigationMessageEvent.STATUS_READY :
+ GpsNavigationMessageEvent.STATUS_GPS_LOCATION_DISABLED;
+ foreach(new StatusChangedOperation(status));
+ }
+
+ private class StatusChangedOperation
+ implements ListenerOperation<IGpsNavigationMessageListener> {
+ private final int mStatus;
+
+ public StatusChangedOperation(int status) {
+ mStatus = status;
+ }
+
+ @Override
+ public void execute(IGpsNavigationMessageListener listener) throws RemoteException {
+ listener.onStatusChanged(mStatus);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/GpsStatusListenerHelper.java b/services/core/java/com/android/server/location/GpsStatusListenerHelper.java
index 27cf3d8..376b4a5 100644
--- a/services/core/java/com/android/server/location/GpsStatusListenerHelper.java
+++ b/services/core/java/com/android/server/location/GpsStatusListenerHelper.java
@@ -17,14 +17,55 @@
package com.android.server.location;
import android.location.IGpsStatusListener;
+import android.os.Handler;
import android.os.RemoteException;
/**
* Implementation of a handler for {@link IGpsStatusListener}.
*/
abstract class GpsStatusListenerHelper extends RemoteListenerHelper<IGpsStatusListener> {
- public GpsStatusListenerHelper() {
- super("GpsStatusListenerHelper");
+ public GpsStatusListenerHelper(Handler handler) {
+ super(handler, "GpsStatusListenerHelper");
+
+ Operation nullOperation = new Operation() {
+ @Override
+ public void execute(IGpsStatusListener iGpsStatusListener) throws RemoteException {}
+ };
+ setSupported(GpsLocationProvider.isSupported(), nullOperation);
+ }
+
+ @Override
+ protected boolean registerWithService() {
+ return true;
+ }
+
+ @Override
+ protected void unregisterFromService() {}
+
+ @Override
+ protected ListenerOperation<IGpsStatusListener> getHandlerOperation(int result) {
+ return null;
+ }
+
+ @Override
+ protected void handleGpsEnabledChanged(boolean enabled) {
+ Operation operation;
+ if (enabled) {
+ operation = new Operation() {
+ @Override
+ public void execute(IGpsStatusListener listener) throws RemoteException {
+ listener.onGpsStarted();
+ }
+ };
+ } else {
+ operation = new Operation() {
+ @Override
+ public void execute(IGpsStatusListener listener) throws RemoteException {
+ listener.onGpsStopped();
+ }
+ };
+ }
+ foreach(operation);
}
public void onFirstFix(final int timeToFirstFix) {
@@ -34,22 +75,6 @@
listener.onFirstFix(timeToFirstFix);
}
};
-
- foreach(operation);
- }
-
- public void onStatusChanged(final boolean isNavigating) {
- Operation operation = new Operation() {
- @Override
- public void execute(IGpsStatusListener listener) throws RemoteException {
- if (isNavigating) {
- listener.onGpsStarted();
- } else {
- listener.onGpsStopped();
- }
- }
- };
-
foreach(operation);
}
@@ -76,7 +101,6 @@
usedInFixMask);
}
};
-
foreach(operation);
}
@@ -87,7 +111,6 @@
listener.onNmeaReceived(timestamp, nmea);
}
};
-
foreach(operation);
}
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index 451af18..402b601 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -19,35 +19,41 @@
import com.android.internal.util.Preconditions;
import android.annotation.NonNull;
+import android.os.Handler;
import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;
import android.util.Log;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashMap;
/**
* A helper class, that handles operations in remote listeners, and tracks for remote process death.
*/
abstract class RemoteListenerHelper<TListener extends IInterface> {
- private final String mTag;
- private final HashMap<IBinder, LinkedListener> mListenerMap =
- new HashMap<IBinder, LinkedListener>();
+ protected static final int RESULT_SUCCESS = 0;
+ protected static final int RESULT_NOT_AVAILABLE = 1;
+ protected static final int RESULT_NOT_SUPPORTED = 2;
+ protected static final int RESULT_GPS_LOCATION_DISABLED = 3;
+ protected static final int RESULT_INTERNAL_ERROR = 4;
- protected RemoteListenerHelper(String name) {
+ private final Handler mHandler;
+ private final String mTag;
+
+ private final HashMap<IBinder, LinkedListener> mListenerMap = new HashMap<>();
+
+ private boolean mIsRegistered;
+ private boolean mHasIsSupported;
+ private boolean mIsSupported;
+
+ protected RemoteListenerHelper(Handler handler, String name) {
Preconditions.checkNotNull(name);
+ mHandler = handler;
mTag = name;
}
public boolean addListener(@NonNull TListener listener) {
Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener.");
- if (!isSupported()) {
- Log.e(mTag, "Refused to add listener, the feature is not supported.");
- return false;
- }
-
IBinder binder = listener.asBinder();
LinkedListener deathListener = new LinkedListener(listener);
synchronized (mListenerMap) {
@@ -55,77 +61,128 @@
// listener already added
return true;
}
-
try {
binder.linkToDeath(deathListener, 0 /* flags */);
} catch (RemoteException e) {
// if the remote process registering the listener is already death, just swallow the
- // exception and continue
- Log.e(mTag, "Remote listener already died.", e);
+ // exception and return
+ Log.v(mTag, "Remote listener already died.", e);
return false;
}
-
mListenerMap.put(binder, deathListener);
- if (mListenerMap.size() == 1) {
- if (!registerWithService()) {
- Log.e(mTag, "RegisterWithService failed, listener will be removed.");
- removeListener(listener);
- return false;
- }
- }
- }
+ // update statuses we already know about, starting from the ones that will never change
+ int result;
+ if (!isAvailableInPlatform()) {
+ result = RESULT_NOT_AVAILABLE;
+ } else if (mHasIsSupported && !mIsSupported) {
+ result = RESULT_NOT_SUPPORTED;
+ } else if (!isGpsEnabled()) {
+ result = RESULT_GPS_LOCATION_DISABLED;
+ } else if (!tryRegister()) {
+ // only attempt to register if GPS is enabled, otherwise we will register once GPS
+ // becomes available
+ result = RESULT_INTERNAL_ERROR;
+ } else if (mHasIsSupported && mIsSupported) {
+ result = RESULT_SUCCESS;
+ } else {
+ // at this point if the supported flag is not set, the notification will be sent
+ // asynchronously in the future
+ return true;
+ }
+ post(listener, getHandlerOperation(result));
+ }
return true;
}
- public boolean removeListener(@NonNull TListener listener) {
+ public void removeListener(@NonNull TListener listener) {
Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener.");
- if (!isSupported()) {
- Log.e(mTag, "Refused to remove listener, the feature is not supported.");
- return false;
- }
-
IBinder binder = listener.asBinder();
LinkedListener linkedListener;
synchronized (mListenerMap) {
linkedListener = mListenerMap.remove(binder);
- if (mListenerMap.isEmpty() && linkedListener != null) {
- unregisterFromService();
+ if (mListenerMap.isEmpty()) {
+ tryUnregister();
}
}
-
if (linkedListener != null) {
binder.unlinkToDeath(linkedListener, 0 /* flags */);
}
- return true;
}
- protected abstract boolean isSupported();
+ public void onGpsEnabledChanged(boolean enabled) {
+ // handle first the sub-class implementation, so any error in registration can take
+ // precedence
+ handleGpsEnabledChanged(enabled);
+ synchronized (mListenerMap) {
+ if (!enabled) {
+ tryUnregister();
+ return;
+ }
+ if (mListenerMap.isEmpty()) {
+ return;
+ }
+ if (tryRegister()) {
+ // registration was successful, there is no need to update the state
+ return;
+ }
+ ListenerOperation<TListener> operation = getHandlerOperation(RESULT_INTERNAL_ERROR);
+ foreachUnsafe(operation);
+ }
+ }
+
+ protected abstract boolean isAvailableInPlatform();
+ protected abstract boolean isGpsEnabled();
protected abstract boolean registerWithService();
protected abstract void unregisterFromService();
+ protected abstract ListenerOperation<TListener> getHandlerOperation(int result);
+ protected abstract void handleGpsEnabledChanged(boolean enabled);
protected interface ListenerOperation<TListener extends IInterface> {
void execute(TListener listener) throws RemoteException;
}
- protected void foreach(ListenerOperation operation) {
- Collection<LinkedListener> linkedListeners;
+ protected void foreach(ListenerOperation<TListener> operation) {
synchronized (mListenerMap) {
- Collection<LinkedListener> values = mListenerMap.values();
- linkedListeners = new ArrayList<LinkedListener>(values);
+ foreachUnsafe(operation);
}
+ }
- for (LinkedListener linkedListener : linkedListeners) {
- TListener listener = linkedListener.getUnderlyingListener();
- try {
- operation.execute(listener);
- } catch (RemoteException e) {
- Log.e(mTag, "Error in monitored listener.", e);
- removeListener(listener);
- }
+ protected void setSupported(boolean value, ListenerOperation<TListener> notifier) {
+ synchronized (mListenerMap) {
+ mHasIsSupported = true;
+ mIsSupported = value;
+ foreachUnsafe(notifier);
}
}
+ private void foreachUnsafe(ListenerOperation<TListener> operation) {
+ for (LinkedListener linkedListener : mListenerMap.values()) {
+ post(linkedListener.getUnderlyingListener(), operation);
+ }
+ }
+
+ private void post(TListener listener, ListenerOperation<TListener> operation) {
+ if (operation != null) {
+ mHandler.post(new HandlerRunnable(listener, operation));
+ }
+ }
+
+ private boolean tryRegister() {
+ if (!mIsRegistered) {
+ mIsRegistered = registerWithService();
+ }
+ return mIsRegistered;
+ }
+
+ private void tryUnregister() {
+ if (!mIsRegistered) {
+ return;
+ }
+ unregisterFromService();
+ mIsRegistered = false;
+ }
+
private class LinkedListener implements IBinder.DeathRecipient {
private final TListener mListener;
@@ -144,4 +201,23 @@
removeListener(mListener);
}
}
+
+ private class HandlerRunnable implements Runnable {
+ private final TListener mListener;
+ private final ListenerOperation<TListener> mOperation;
+
+ public HandlerRunnable(TListener listener, ListenerOperation<TListener> operation) {
+ mListener = listener;
+ mOperation = operation;
+ }
+
+ @Override
+ public void run() {
+ try {
+ mOperation.execute(mListener);
+ } catch (RemoteException e) {
+ Log.v(mTag, "Error in monitored listener.", e);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 05ad1fe..3ff9ff4 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -52,6 +52,7 @@
private final ArrayList<ConditionRecord> mRecords = new ArrayList<ConditionRecord>();
private final CountdownConditionProvider mCountdown = new CountdownConditionProvider();
private final DowntimeConditionProvider mDowntime = new DowntimeConditionProvider();
+ private final NextAlarmConditionProvider mNextAlarm = new NextAlarmConditionProvider();
private Condition mExitCondition;
private ComponentName mExitConditionComponent;
@@ -99,6 +100,7 @@
}
mCountdown.dump(pw, filter);
mDowntime.dump(pw, filter);
+ mNextAlarm.dump(pw, filter);
}
@Override
@@ -116,6 +118,23 @@
registerService(mDowntime.asInterface(), DowntimeConditionProvider.COMPONENT,
UserHandle.USER_OWNER);
mDowntime.setCallback(new DowntimeCallback());
+ mNextAlarm.attachBase(mContext);
+ registerService(mNextAlarm.asInterface(), NextAlarmConditionProvider.COMPONENT,
+ UserHandle.USER_OWNER);
+ mNextAlarm.setCallback(new NextAlarmConditionProvider.Callback() {
+ @Override
+ public boolean isInDowntime() {
+ return mDowntime.isInDowntime();
+ }
+ });
+ }
+
+ @Override
+ public void onUserSwitched() {
+ super.onUserSwitched();
+ if (mNextAlarm != null) {
+ mNextAlarm.onUserSwitched();
+ }
}
@Override
@@ -534,20 +553,22 @@
private class DowntimeCallback implements DowntimeConditionProvider.Callback {
@Override
- public void onDowntimeChanged(boolean inDowntime) {
+ public void onDowntimeChanged(int downtimeMode) {
final int mode = mZenModeHelper.getZenMode();
final ZenModeConfig config = mZenModeHelper.getConfig();
- // enter downtime
- if (inDowntime && mode == Global.ZEN_MODE_OFF && config != null) {
+ final boolean inDowntime = downtimeMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ || downtimeMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
+ final boolean downtimeCurrent = mDowntime.isDowntimeCondition(mExitCondition);
+ // enter downtime, or update mode if reconfigured during an active downtime
+ if (inDowntime && (mode == Global.ZEN_MODE_OFF || downtimeCurrent) && config != null) {
final Condition condition = mDowntime.createCondition(config.toDowntimeInfo(),
Condition.STATE_TRUE);
- mZenModeHelper.setZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, "downtimeEnter");
+ mZenModeHelper.setZenMode(downtimeMode, "downtimeEnter");
setZenModeCondition(condition, "downtime");
}
// exit downtime
- if (!inDowntime && mDowntime.isDowntimeCondition(mExitCondition)
- && (mode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- || mode == Global.ZEN_MODE_NO_INTERRUPTIONS)) {
+ if (!inDowntime && downtimeCurrent && (mode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ || mode == Global.ZEN_MODE_NO_INTERRUPTIONS)) {
mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "downtimeExit");
}
}
diff --git a/services/core/java/com/android/server/notification/DowntimeConditionProvider.java b/services/core/java/com/android/server/notification/DowntimeConditionProvider.java
index efe47c3..881c9ad 100644
--- a/services/core/java/com/android/server/notification/DowntimeConditionProvider.java
+++ b/services/core/java/com/android/server/notification/DowntimeConditionProvider.java
@@ -24,6 +24,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
+import android.provider.Settings.Global;
import android.service.notification.Condition;
import android.service.notification.ConditionProviderService;
import android.service.notification.IConditionProvider;
@@ -64,7 +65,7 @@
private final ArraySet<Integer> mDays = new ArraySet<Integer>();
private boolean mConnected;
- private boolean mInDowntime;
+ private int mDowntimeMode;
private ZenModeConfig mConfig;
private Callback mCallback;
@@ -75,7 +76,7 @@
public void dump(PrintWriter pw, DumpFilter filter) {
pw.println(" DowntimeConditionProvider:");
pw.print(" mConnected="); pw.println(mConnected);
- pw.print(" mInDowntime="); pw.println(mInDowntime);
+ pw.print(" mDowntimeMode="); pw.println(Global.zenModeToString(mDowntimeMode));
}
public void attachBase(Context base) {
@@ -113,7 +114,7 @@
public void onRequestConditions(int relevance) {
if (DEBUG) Slog.d(TAG, "onRequestConditions relevance=" + relevance);
if ((relevance & Condition.FLAG_RELEVANT_NOW) != 0) {
- if (mInDowntime && mConfig != null) {
+ if (isInDowntime() && mConfig != null) {
notifyCondition(createCondition(mConfig.toDowntimeInfo(), Condition.STATE_TRUE));
}
}
@@ -124,7 +125,7 @@
if (DEBUG) Slog.d(TAG, "onSubscribe conditionId=" + conditionId);
final DowntimeInfo downtime = ZenModeConfig.tryParseDowntimeConditionId(conditionId);
if (downtime != null && mConfig != null) {
- final int state = mConfig.toDowntimeInfo().equals(downtime) && mInDowntime
+ final int state = mConfig.toDowntimeInfo().equals(downtime) && isInDowntime()
? Condition.STATE_TRUE : Condition.STATE_FALSE;
if (DEBUG) Slog.d(TAG, "notify condition state: " + Condition.stateToString(state));
notifyCondition(createCondition(downtime, state));
@@ -146,7 +147,7 @@
}
public boolean isInDowntime() {
- return mInDowntime;
+ return mDowntimeMode != Global.ZEN_MODE_OFF;
}
public Condition createCondition(DowntimeInfo downtime, int state) {
@@ -158,7 +159,8 @@
final long time = getTime(System.currentTimeMillis(), downtime.endHour, downtime.endMinute);
final String formatted = new SimpleDateFormat(pattern, locale).format(new Date(time));
final String summary = mContext.getString(R.string.downtime_condition_summary, formatted);
- return new Condition(id, summary, "", "", 0, state, Condition.FLAG_RELEVANT_NOW);
+ final String line1 = mContext.getString(R.string.downtime_condition_line_one);
+ return new Condition(id, summary, line1, formatted, 0, state, Condition.FLAG_RELEVANT_NOW);
}
public boolean isDowntimeCondition(Condition condition) {
@@ -181,15 +183,18 @@
}
}
- private boolean isInDowntime(long time) {
- if (mConfig == null || mDays.size() == 0) return false;
+ private int computeDowntimeMode(long time) {
+ if (mConfig == null || mDays.size() == 0) return Global.ZEN_MODE_OFF;
final long start = getTime(time, mConfig.sleepStartHour, mConfig.sleepStartMinute);
long end = getTime(time, mConfig.sleepEndHour, mConfig.sleepEndMinute);
- if (start == end) return false;
+ if (start == end) return Global.ZEN_MODE_OFF;
if (end < start) {
end = addDays(end, 1);
}
- return isInDowntime(-1, time, start, end) || isInDowntime(0, time, start, end);
+ final boolean inDowntime = isInDowntime(-1, time, start, end)
+ || isInDowntime(0, time, start, end);
+ return inDowntime ? (mConfig.sleepNone ? Global.ZEN_MODE_NO_INTERRUPTIONS
+ : Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) : Global.ZEN_MODE_OFF;
}
private boolean isInDowntime(int daysOffset, long time, long start, long end) {
@@ -201,18 +206,18 @@
}
private void reevaluateDowntime() {
- final boolean inDowntime = isInDowntime(System.currentTimeMillis());
- if (DEBUG) Slog.d(TAG, "inDowntime=" + inDowntime);
- if (inDowntime == mInDowntime) return;
- Slog.i(TAG, (inDowntime ? "Entering" : "Exiting" ) + " downtime");
- mInDowntime = inDowntime;
- ZenLog.traceDowntime(mInDowntime, getDayOfWeek(System.currentTimeMillis()), mDays);
+ final int downtimeMode = computeDowntimeMode(System.currentTimeMillis());
+ if (DEBUG) Slog.d(TAG, "downtimeMode=" + downtimeMode);
+ if (downtimeMode == mDowntimeMode) return;
+ mDowntimeMode = downtimeMode;
+ Slog.i(TAG, (isInDowntime() ? "Entering" : "Exiting" ) + " downtime");
+ ZenLog.traceDowntime(mDowntimeMode, getDayOfWeek(System.currentTimeMillis()), mDays);
fireDowntimeChanged();
}
private void fireDowntimeChanged() {
if (mCallback != null) {
- mCallback.onDowntimeChanged(mInDowntime);
+ mCallback.onDowntimeChanged(mDowntimeMode);
}
}
@@ -255,7 +260,10 @@
time = addDays(time, 1);
}
final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, requestCode,
- new Intent(action).putExtra(EXTRA_TIME, time), PendingIntent.FLAG_UPDATE_CURRENT);
+ new Intent(action)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ .putExtra(EXTRA_TIME, time),
+ PendingIntent.FLAG_UPDATE_CURRENT);
alarms.cancel(pendingIntent);
if (mConfig.sleepMode != null) {
if (DEBUG) Slog.d(TAG, String.format("Scheduling %s for %s, %s in the future, now=%s",
@@ -289,6 +297,6 @@
};
public interface Callback {
- void onDowntimeChanged(boolean inDowntime);
+ void onDowntimeChanged(int downtimeMode);
}
}
diff --git a/services/core/java/com/android/server/notification/NextAlarmConditionProvider.java b/services/core/java/com/android/server/notification/NextAlarmConditionProvider.java
new file mode 100644
index 0000000..4a29573
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NextAlarmConditionProvider.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import android.app.ActivityManager;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.app.AlarmManager.AlarmClockInfo;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.service.notification.Condition;
+import android.service.notification.ConditionProviderService;
+import android.service.notification.IConditionProvider;
+import android.service.notification.ZenModeConfig;
+import android.util.TimeUtils;
+import android.text.format.DateFormat;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.server.notification.NotificationManagerService.DumpFilter;
+
+import java.io.PrintWriter;
+import java.util.Locale;
+
+/**
+ * Built-in zen condition provider for alarm-clock-based conditions.
+ *
+ * <p>If the user's next alarm is within a lookahead threshold (config, default 12hrs), advertise
+ * it as an exit condition for zen mode (unless the built-in downtime condition is also available).
+ *
+ * <p>When this next alarm is selected as the active exit condition, follow subsequent changes
+ * to the user's next alarm, assuming it remains within the 12-hr window.
+ *
+ * <p>The next alarm is defined as {@link AlarmManager#getNextAlarmClock(int)}, which does not
+ * survive a reboot. Maintain the illusion of a consistent next alarm value by holding on to
+ * a persisted condition until we receive the first value after reboot, or timeout with no value.
+ */
+public class NextAlarmConditionProvider extends ConditionProviderService {
+ private static final String TAG = "NextAlarmConditions";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private static final String ACTION_TRIGGER = TAG + ".trigger";
+ private static final String EXTRA_TRIGGER = "trigger";
+ private static final int REQUEST_CODE = 100;
+ private static final long SECONDS = 1000;
+ private static final long MINUTES = 60 * SECONDS;
+ private static final long HOURS = 60 * MINUTES;
+ private static final long NEXT_ALARM_UPDATE_DELAY = 1 * SECONDS; // treat clear+set as update
+ private static final long EARLY = 5 * SECONDS; // fire early, ensure alarm stream is unmuted
+ private static final long WAIT_AFTER_CONNECT = 5 * MINUTES;// for initial alarm re-registration
+ private static final long WAIT_AFTER_BOOT = 20 * SECONDS; // for initial alarm re-registration
+ private static final String NEXT_ALARM_PATH = "next_alarm";
+ public static final ComponentName COMPONENT =
+ new ComponentName("android", NextAlarmConditionProvider.class.getName());
+
+ private final Context mContext = this;
+ private final H mHandler = new H();
+
+ private long mConnected;
+ private boolean mRegistered;
+ private AlarmManager mAlarmManager;
+ private int mCurrentUserId;
+ private long mLookaheadThreshold;
+ private long mScheduledAlarmTime;
+ private Callback mCallback;
+ private Uri mCurrentSubscription;
+ private PowerManager.WakeLock mWakeLock;
+ private long mBootCompleted;
+
+ public NextAlarmConditionProvider() {
+ if (DEBUG) Slog.d(TAG, "new NextAlarmConditionProvider()");
+ }
+
+ public void dump(PrintWriter pw, DumpFilter filter) {
+ pw.println(" NextAlarmConditionProvider:");
+ pw.print(" mConnected="); pw.println(mConnected);
+ pw.print(" mBootCompleted="); pw.println(mBootCompleted);
+ pw.print(" mRegistered="); pw.println(mRegistered);
+ pw.print(" mCurrentUserId="); pw.println(mCurrentUserId);
+ pw.print(" mScheduledAlarmTime="); pw.println(formatAlarmDebug(mScheduledAlarmTime));
+ pw.print(" mLookaheadThreshold="); pw.print(mLookaheadThreshold);
+ pw.print(" ("); TimeUtils.formatDuration(mLookaheadThreshold, pw); pw.println(")");
+ pw.print(" mCurrentSubscription="); pw.println(mCurrentSubscription);
+ pw.print(" mWakeLock="); pw.println(mWakeLock);
+ }
+
+ public void setCallback(Callback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void onConnected() {
+ if (DEBUG) Slog.d(TAG, "onConnected");
+ mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ final PowerManager p = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = p.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ mLookaheadThreshold = mContext.getResources()
+ .getInteger(R.integer.config_next_alarm_condition_lookahead_threshold_hrs) * HOURS;
+ init();
+ mConnected = System.currentTimeMillis();
+ }
+
+ public void onUserSwitched() {
+ if (DEBUG) Slog.d(TAG, "onUserSwitched");
+ if (mConnected != 0) {
+ init();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (DEBUG) Slog.d(TAG, "onDestroy");
+ if (mRegistered) {
+ mContext.unregisterReceiver(mReceiver);
+ mRegistered = false;
+ }
+ mConnected = 0;
+ }
+
+ @Override
+ public void onRequestConditions(int relevance) {
+ if (mConnected == 0 || (relevance & Condition.FLAG_RELEVANT_NOW) == 0) return;
+
+ final AlarmClockInfo nextAlarm = mAlarmManager.getNextAlarmClock(mCurrentUserId);
+ if (nextAlarm == null) return; // no next alarm
+ if (mCallback != null && mCallback.isInDowntime()) return; // prefer downtime condition
+ if (!isWithinLookaheadThreshold(nextAlarm)) return; // alarm not within window
+
+ // next alarm exists, and is within the configured lookahead threshold
+ notifyCondition(newConditionId(), nextAlarm, Condition.STATE_TRUE, "request");
+ }
+
+ private boolean isWithinLookaheadThreshold(AlarmClockInfo alarm) {
+ if (alarm == null) return false;
+ final long delta = getEarlyTriggerTime(alarm) - System.currentTimeMillis();
+ return delta > 0 && (mLookaheadThreshold <= 0 || delta < mLookaheadThreshold);
+ }
+
+ @Override
+ public void onSubscribe(Uri conditionId) {
+ if (DEBUG) Slog.d(TAG, "onSubscribe " + conditionId);
+ if (!isNextAlarmCondition(conditionId)) {
+ notifyCondition(conditionId, null, Condition.STATE_FALSE, "badCondition");
+ return;
+ }
+ mCurrentSubscription = conditionId;
+ mHandler.postEvaluate(0);
+ }
+
+ private static long getEarlyTriggerTime(AlarmClockInfo alarm) {
+ return alarm != null ? (alarm.getTriggerTime() - EARLY) : 0;
+ }
+
+ private boolean isDoneWaitingAfterBoot(long time) {
+ if (mBootCompleted > 0) return (time - mBootCompleted) > WAIT_AFTER_BOOT;
+ if (mConnected > 0) return (time - mConnected) > WAIT_AFTER_CONNECT;
+ return true;
+ }
+
+ private void handleEvaluate() {
+ final AlarmClockInfo nextAlarm = mAlarmManager.getNextAlarmClock(mCurrentUserId);
+ final long triggerTime = getEarlyTriggerTime(nextAlarm);
+ final boolean withinThreshold = isWithinLookaheadThreshold(nextAlarm);
+ final long now = System.currentTimeMillis();
+ final boolean booted = isDoneWaitingAfterBoot(now);
+ if (DEBUG) Slog.d(TAG, "handleEvaluate mCurrentSubscription=" + mCurrentSubscription
+ + " nextAlarm=" + formatAlarmDebug(triggerTime)
+ + " withinThreshold=" + withinThreshold
+ + " booted=" + booted);
+ if (mCurrentSubscription == null) return; // no one cares
+ if (!booted) {
+ // we don't know yet
+ notifyCondition(mCurrentSubscription, nextAlarm, Condition.STATE_UNKNOWN, "!booted");
+ final long recheckTime = (mBootCompleted > 0 ? mBootCompleted : now) + WAIT_AFTER_BOOT;
+ rescheduleAlarm(recheckTime);
+ return;
+ }
+ if (!withinThreshold) {
+ // triggertime invalid or in the past, condition = false
+ notifyCondition(mCurrentSubscription, nextAlarm, Condition.STATE_FALSE, "!within");
+ mCurrentSubscription = null;
+ return;
+ }
+ // triggertime in the future, condition = true, schedule alarm
+ notifyCondition(mCurrentSubscription, nextAlarm, Condition.STATE_TRUE, "within");
+ rescheduleAlarm(triggerTime);
+ }
+
+ private static String formatDuration(long millis) {
+ final StringBuilder sb = new StringBuilder();
+ TimeUtils.formatDuration(millis, sb);
+ return sb.toString();
+ }
+
+ private void rescheduleAlarm(long time) {
+ if (DEBUG) Slog.d(TAG, "rescheduleAlarm " + time);
+ final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, REQUEST_CODE,
+ new Intent(ACTION_TRIGGER)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ .putExtra(EXTRA_TRIGGER, time),
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ alarms.cancel(pendingIntent);
+ mScheduledAlarmTime = time;
+ if (time > 0) {
+ if (DEBUG) Slog.d(TAG, String.format("Scheduling alarm for %s (in %s)",
+ formatAlarmDebug(time), formatDuration(time - System.currentTimeMillis())));
+ alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
+ }
+ }
+
+ private void notifyCondition(Uri id, AlarmClockInfo alarm, int state, String reason) {
+ final String formattedAlarm = alarm == null ? "" : formatAlarm(alarm.getTriggerTime());
+ if (DEBUG) Slog.d(TAG, "notifyCondition " + Condition.stateToString(state)
+ + " alarm=" + formattedAlarm + " reason=" + reason);
+ notifyCondition(new Condition(id,
+ mContext.getString(R.string.zen_mode_next_alarm_summary, formattedAlarm),
+ mContext.getString(R.string.zen_mode_next_alarm_line_one),
+ formattedAlarm, 0, state, Condition.FLAG_RELEVANT_NOW));
+ }
+
+ @Override
+ public void onUnsubscribe(Uri conditionId) {
+ if (DEBUG) Slog.d(TAG, "onUnsubscribe " + conditionId);
+ if (conditionId != null && conditionId.equals(mCurrentSubscription)) {
+ mCurrentSubscription = null;
+ rescheduleAlarm(0);
+ }
+ }
+
+ public void attachBase(Context base) {
+ attachBaseContext(base);
+ }
+
+ public IConditionProvider asInterface() {
+ return (IConditionProvider) onBind(null);
+ }
+
+ private Uri newConditionId() {
+ return new Uri.Builder().scheme(Condition.SCHEME)
+ .authority(ZenModeConfig.SYSTEM_AUTHORITY)
+ .appendPath(NEXT_ALARM_PATH)
+ .appendPath(Integer.toString(mCurrentUserId))
+ .build();
+ }
+
+ private boolean isNextAlarmCondition(Uri conditionId) {
+ return conditionId != null && conditionId.getScheme().equals(Condition.SCHEME)
+ && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY)
+ && conditionId.getPathSegments().size() == 2
+ && conditionId.getPathSegments().get(0).equals(NEXT_ALARM_PATH)
+ && conditionId.getPathSegments().get(1).equals(Integer.toString(mCurrentUserId));
+ }
+
+ private void init() {
+ if (mRegistered) {
+ mContext.unregisterReceiver(mReceiver);
+ }
+ mCurrentUserId = ActivityManager.getCurrentUser();
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
+ filter.addAction(ACTION_TRIGGER);
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
+ filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ filter.addAction(Intent.ACTION_BOOT_COMPLETED);
+ mContext.registerReceiverAsUser(mReceiver, new UserHandle(mCurrentUserId), filter, null,
+ null);
+ mRegistered = true;
+ mHandler.postEvaluate(0);
+ }
+
+ private String formatAlarm(long time) {
+ return formatAlarm(time, "Hm", "hma");
+ }
+
+ private String formatAlarm(long time, String skeleton24, String skeleton12) {
+ final String skeleton = DateFormat.is24HourFormat(mContext) ? skeleton24 : skeleton12;
+ final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
+ return DateFormat.format(pattern, time).toString();
+ }
+
+ private String formatAlarmDebug(AlarmClockInfo alarm) {
+ return formatAlarmDebug(alarm != null ? alarm.getTriggerTime() : 0);
+ }
+
+ private String formatAlarmDebug(long time) {
+ if (time <= 0) return Long.toString(time);
+ return String.format("%s (%s)", time, formatAlarm(time, "Hms", "hmsa"));
+ }
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (DEBUG) Slog.d(TAG, "onReceive " + action);
+ long delay = 0;
+ if (action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) {
+ delay = NEXT_ALARM_UPDATE_DELAY;
+ if (DEBUG) Slog.d(TAG, String.format(" next alarm for user %s: %s",
+ mCurrentUserId,
+ formatAlarmDebug(mAlarmManager.getNextAlarmClock(mCurrentUserId))));
+ } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
+ mBootCompleted = System.currentTimeMillis();
+ }
+ mHandler.postEvaluate(delay);
+ mWakeLock.acquire(delay + 5000); // stay awake during evaluate
+ }
+ };
+
+ public interface Callback {
+ boolean isInDowntime();
+ }
+
+ private class H extends Handler {
+ private static final int MSG_EVALUATE = 1;
+
+ public void postEvaluate(long delay) {
+ removeMessages(MSG_EVALUATE);
+ sendEmptyMessageDelayed(MSG_EVALUATE, delay);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_EVALUATE) {
+ handleEvaluate();
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index e0b83de..1a3da79 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -75,8 +75,8 @@
append(TYPE_SET_RINGER_MODE, ringerModeToString(ringerMode));
}
- public static void traceDowntime(boolean inDowntime, int day, ArraySet<Integer> days) {
- append(TYPE_DOWNTIME, inDowntime + ",day=" + day + ",days=" + days);
+ public static void traceDowntime(int downtimeMode, int day, ArraySet<Integer> days) {
+ append(TYPE_DOWNTIME, zenModeToString(downtimeMode) + ",day=" + day + ",days=" + days);
}
public static void traceSetZenMode(int mode, String reason) {
diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
index 203d990..6d18531 100644
--- a/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
+++ b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
@@ -141,4 +141,11 @@
return "CrossProfileIntentFilter{0x" + Integer.toHexString(System.identityHashCode(this))
+ " " + Integer.toString(mTargetUserId) + "}";
}
+
+ boolean equalsIgnoreFilter(CrossProfileIntentFilter other) {
+ return mTargetUserId == other.mTargetUserId
+ && mOwnerUserId == other.mOwnerUserId
+ && mOwnerPackage.equals(other.mOwnerPackage)
+ && mFlags == other.mFlags;
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 5f9cf76..a559bdd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -11849,9 +11849,21 @@
return;
}
synchronized (mPackages) {
- CrossProfileIntentFilter filter = new CrossProfileIntentFilter(intentFilter,
+ CrossProfileIntentFilter newFilter = new CrossProfileIntentFilter(intentFilter,
ownerPackage, UserHandle.getUserId(callingUid), targetUserId, flags);
- mSettings.editCrossProfileIntentResolverLPw(sourceUserId).addFilter(filter);
+ CrossProfileIntentResolver resolver =
+ mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
+ ArrayList<CrossProfileIntentFilter> existing = resolver.findFilters(intentFilter);
+ // We have all those whose filter is equal. Now checking if the rest is equal as well.
+ if (existing != null) {
+ int size = existing.size();
+ for (int i = 0; i < size; i++) {
+ if (newFilter.equalsIgnoreFilter(existing.get(i))) {
+ return;
+ }
+ }
+ }
+ resolver.addFilter(newFilter);
scheduleWritePackageRestrictionsLocked(sourceUserId);
}
}
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index b1c918d..b2bcf75 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -27,11 +27,11 @@
import android.content.ServiceConnection;
import android.net.Uri;
import android.os.Binder;
-import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.PatternMatcher;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -218,7 +218,7 @@
}
@Override
- public void onSetTrustAgentFeaturesEnabledCompleted(boolean result, IBinder token) {
+ public void onConfigureCompleted(boolean result, IBinder token) {
if (DEBUG) Slog.v(TAG, "onSetTrustAgentFeaturesEnabledCompleted(result=" + result);
mHandler.obtainMessage(MSG_SET_TRUST_AGENT_FEATURES_COMPLETED,
result ? 1 : 0, 0, token).sendToTarget();
@@ -318,23 +318,19 @@
DevicePolicyManager dpm =
(DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
- if ((dpm.getKeyguardDisabledFeatures(null)
+ if ((dpm.getKeyguardDisabledFeatures(null, mUserId)
& DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0) {
- List<String> features = dpm.getTrustAgentFeaturesEnabled(null, mName);
+ List<PersistableBundle> config = dpm.getTrustAgentConfiguration(
+ null, mName, mUserId);
trustDisabled = true;
- if (DEBUG) Slog.v(TAG, "Detected trust agents disabled. Features = "
- + features);
- if (features != null && features.size() > 0) {
- Bundle bundle = new Bundle();
- bundle.putStringArrayList(TrustAgentService.KEY_FEATURES,
- (ArrayList<String>)features);
+ if (DEBUG) Slog.v(TAG, "Detected trust agents disabled. Config = " + config);
+ if (config != null && config.size() > 0) {
if (DEBUG) {
Slog.v(TAG, "TrustAgent " + mName.flattenToShortString()
- + " disabled until it acknowledges "+ features);
+ + " disabled until it acknowledges "+ config);
}
mSetTrustAgentFeaturesToken = new Binder();
- mTrustAgentService.setTrustAgentFeaturesEnabled(bundle,
- mSetTrustAgentFeaturesToken);
+ mTrustAgentService.onConfigure(config, mSetTrustAgentFeaturesToken);
}
}
final long maxTimeToLock = dpm.getMaximumTimeToLock(null);
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 4437e12..fe5cb33 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -48,6 +48,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -228,10 +229,10 @@
if (!enabledAgents.contains(name)) continue;
if (disableTrustAgents) {
- List<String> features =
- dpm.getTrustAgentFeaturesEnabled(null /* admin */, name);
+ List<PersistableBundle> config =
+ dpm.getTrustAgentConfiguration(null /* admin */, name, userInfo.id);
// Disable agent if no features are enabled.
- if (features == null || features.isEmpty()) continue;
+ if (config == null || config.isEmpty()) continue;
}
AgentInfo agentInfo = new AgentInfo();
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index bfc7659..eeb007c 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -691,8 +691,10 @@
throw new RuntimeException("Invalid thumbnail transition state");
}
- return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight,
- THUMBNAIL_APP_TRANSITION_DURATION, mThumbnailFastOutSlowInInterpolator);
+ int duration = Math.max(THUMBNAIL_APP_TRANSITION_ALPHA_DURATION,
+ THUMBNAIL_APP_TRANSITION_DURATION);
+ return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, duration,
+ mThumbnailFastOutSlowInInterpolator);
}
/**
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 69c9144..bf96ea5 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -58,7 +58,9 @@
// the state changes.
boolean allDrawn;
- // Special surface for thumbnail animation.
+ // Special surface for thumbnail animation. If deferThumbnailDestruction is enabled, then we
+ // will make sure that the thumbnail is destroyed after the other surface is completed. This
+ // requires that the duration of the two animations are the same.
SurfaceControl thumbnail;
int thumbnailTransactionSeq;
int thumbnailX;
@@ -68,13 +70,12 @@
Animation thumbnailAnimation;
final Transformation thumbnailTransformation = new Transformation();
// This flag indicates that the destruction of the thumbnail surface is synchronized with
- // another animation, so do not pre-emptively destroy the thumbnail surface when the animation
- // completes
+ // another animation, so defer the destruction of this thumbnail surface for a single frame
+ // after the secondary animation completes.
boolean deferThumbnailDestruction;
- // This is the thumbnail surface that has been bestowed upon this animator, and when the
- // surface for this animator's animation is complete, we will destroy the thumbnail surface
- // as well. Do not animate or do anything with this surface.
- SurfaceControl deferredThumbnail;
+ // This flag is set if the animator has deferThumbnailDestruction set and has reached the final
+ // frame of animation. It will extend the animation by one frame and then clean up afterwards.
+ boolean deferFinalFrameCleanup;
/** WindowStateAnimator from mAppAnimator.allAppWindows as of last performLayout */
ArrayList<WindowStateAnimator> mAllAppWinAnimators = new ArrayList<WindowStateAnimator>();
@@ -134,9 +135,7 @@
animation = null;
animating = true;
}
- if (!deferThumbnailDestruction) {
- clearThumbnail();
- }
+ clearThumbnail();
if (mAppToken.deferClearAllDrawn) {
mAppToken.allDrawn = false;
mAppToken.deferClearAllDrawn = false;
@@ -148,13 +147,7 @@
thumbnail.destroy();
thumbnail = null;
}
- }
-
- public void clearDeferredThumbnail() {
- if (deferredThumbnail != null) {
- deferredThumbnail.destroy();
- deferredThumbnail = null;
- }
+ deferThumbnailDestruction = false;
}
void updateLayers() {
@@ -223,19 +216,26 @@
return false;
}
transformation.clear();
- final boolean more = animation.getTransformation(currentTime, transformation);
- if (false && WindowManagerService.DEBUG_ANIM) Slog.v(
- TAG, "Stepped animation in " + mAppToken + ": more=" + more + ", xform=" + transformation);
- if (!more) {
- animation = null;
- if (!deferThumbnailDestruction) {
+ boolean hasMoreFrames = animation.getTransformation(currentTime, transformation);
+ if (!hasMoreFrames) {
+ if (deferThumbnailDestruction && !deferFinalFrameCleanup) {
+ // We are deferring the thumbnail destruction, so extend the animation for one more
+ // (dummy) frame before we clean up
+ deferFinalFrameCleanup = true;
+ hasMoreFrames = true;
+ } else {
+ if (false && WindowManagerService.DEBUG_ANIM) Slog.v(
+ TAG, "Stepped animation in " + mAppToken + ": more=" + hasMoreFrames +
+ ", xform=" + transformation);
+ deferFinalFrameCleanup = false;
+ animation = null;
clearThumbnail();
+ if (WindowManagerService.DEBUG_ANIM) Slog.v(
+ TAG, "Finished animation in " + mAppToken + " @ " + currentTime);
}
- if (WindowManagerService.DEBUG_ANIM) Slog.v(
- TAG, "Finished animation in " + mAppToken + " @ " + currentTime);
}
- hasTransformation = more;
- return more;
+ hasTransformation = hasMoreFrames;
+ return hasMoreFrames;
}
// This must be called while inside a transaction.
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 61ea1e8..c002ddf 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -91,6 +91,9 @@
boolean mKeyguardGoingAwayToNotificationShade;
boolean mKeyguardGoingAwayDisableWindowAnimations;
+ /** Use one animation for all entering activities after keyguard is dismissed. */
+ Animation mPostKeyguardExitAnimation;
+
// forceHiding states.
static final int KEYGUARD_NOT_SHOWN = 0;
static final int KEYGUARD_ANIMATING_IN = 1;
@@ -220,9 +223,6 @@
++mAnimTransactionSequence;
final WindowList windows = mService.getWindowListLocked(displayId);
- ArrayList<WindowStateAnimator> unForceHiding = null;
- boolean wallpaperInUnForceHiding = false;
- WindowState wallpaper = null;
if (mKeyguardGoingAway) {
for (int i = windows.size() - 1; i >= 0; i--) {
@@ -261,6 +261,9 @@
final AppWindowToken appShowWhenLocked = winShowWhenLocked == null ?
null : winShowWhenLocked.mAppToken;
+ boolean wallpaperInUnForceHiding = false;
+ ArrayList<WindowStateAnimator> unForceHiding = null;
+ WindowState wallpaper = null;
for (int i = windows.size() - 1; i >= 0; i--) {
WindowState win = windows.get(i);
WindowStateAnimator winAnimator = win.mWinAnimator;
@@ -269,6 +272,7 @@
if (winAnimator.mSurfaceControl != null) {
final boolean wasAnimating = winAnimator.mWasAnimating;
final boolean nowAnimating = winAnimator.stepAnimationLocked(mCurrentTime);
+ mAnimating |= nowAnimating;
if (WindowManagerService.DEBUG_WALLPAPER) {
Slog.v(TAG, win + ": wasAnimating=" + wasAnimating +
@@ -327,40 +331,53 @@
} else if (mPolicy.canBeForceHidden(win, win.mAttrs)) {
final boolean hideWhenLocked = !((win.mIsImWindow && showImeOverKeyguard) ||
(appShowWhenLocked != null && appShowWhenLocked == win.mAppToken));
- final boolean changed;
if (((mForceHiding == KEYGUARD_ANIMATING_IN)
&& (!winAnimator.isAnimating() || hideWhenLocked))
|| ((mForceHiding == KEYGUARD_SHOWN) && hideWhenLocked)) {
- changed = win.hideLw(false, false);
- if ((DEBUG_KEYGUARD || WindowManagerService.DEBUG_VISIBILITY)
- && changed) Slog.v(TAG, "Now policy hidden: " + win);
+ if (!win.hideLw(false, false)) {
+ // Was already hidden
+ continue;
+ }
+ if (DEBUG_KEYGUARD || WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
+ "Now policy hidden: " + win);
} else {
- changed = win.showLw(false, false);
- if ((DEBUG_KEYGUARD || WindowManagerService.DEBUG_VISIBILITY)
- && changed) Slog.v(TAG, "Now policy shown: " + win);
- if (changed) {
- if ((mBulkUpdateParams & SET_FORCE_HIDING_CHANGED) != 0
- && win.isVisibleNow() /*w.isReadyForDisplay()*/) {
- if (unForceHiding == null) {
- unForceHiding = new ArrayList<WindowStateAnimator>();
- }
- unForceHiding.add(winAnimator);
- if ((flags & FLAG_SHOW_WALLPAPER) != 0) {
- wallpaperInUnForceHiding = true;
- }
+ if (!win.showLw(false, false)) {
+ // Was already showing.
+ continue;
+ }
+ final boolean visibleNow = win.isVisibleNow();
+ if (!visibleNow) {
+ // Couldn't really show, must showLw() again when win becomes visible.
+ win.hideLw(false, false);
+ continue;
+ }
+ if (DEBUG_KEYGUARD || WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
+ "Now policy shown: " + win);
+ if ((mBulkUpdateParams & SET_FORCE_HIDING_CHANGED) != 0) {
+ if (unForceHiding == null) {
+ unForceHiding = new ArrayList<>();
}
- final WindowState currentFocus = mService.mCurrentFocus;
- if (currentFocus == null || currentFocus.mLayer < win.mLayer) {
- // We are showing on to of the current
- // focus, so re-evaluate focus to make
- // sure it is correct.
- if (WindowManagerService.DEBUG_FOCUS_LIGHT) Slog.v(TAG,
- "updateWindowsLocked: setting mFocusMayChange true");
- mService.mFocusMayChange = true;
+ unForceHiding.add(winAnimator);
+ if ((flags & FLAG_SHOW_WALLPAPER) != 0) {
+ wallpaperInUnForceHiding = true;
}
+ } else if (mPostKeyguardExitAnimation != null) {
+ // We're already in the middle of an animation. Use the existing
+ // animation to bring in this window.
+ winAnimator.setAnimation(mPostKeyguardExitAnimation);
+ winAnimator.keyguardGoingAwayAnimation = true;
+ }
+ final WindowState currentFocus = mService.mCurrentFocus;
+ if (currentFocus == null || currentFocus.mLayer < win.mLayer) {
+ // We are showing on top of the current
+ // focus, so re-evaluate focus to make
+ // sure it is correct.
+ if (WindowManagerService.DEBUG_FOCUS_LIGHT) Slog.v(TAG,
+ "updateWindowsLocked: setting mFocusMayChange true");
+ mService.mFocusMayChange = true;
}
}
- if (changed && (flags & FLAG_SHOW_WALLPAPER) != 0) {
+ if ((flags & FLAG_SHOW_WALLPAPER) != 0) {
mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;
setPendingLayoutChanges(Display.DEFAULT_DISPLAY,
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
@@ -403,29 +420,20 @@
// If we have windows that are being show due to them no longer
// being force-hidden, apply the appropriate animation to them.
if (unForceHiding != null) {
- boolean startKeyguardExit = true;
- for (int i=unForceHiding.size()-1; i>=0; i--) {
- Animation a = null;
- if (!mKeyguardGoingAwayDisableWindowAnimations) {
- a = mPolicy.createForceHideEnterAnimation(wallpaperInUnForceHiding,
- mKeyguardGoingAwayToNotificationShade);
- if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: created anim=" + a
- + " for win=" + unForceHiding.get(i));
- } else {
- if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: skipping anim for win="
- + unForceHiding.get(i));
- }
- if (a != null) {
+ // This only happens the first time that we detect the keyguard is animating out.
+ if (mKeyguardGoingAwayDisableWindowAnimations) {
+ if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: skipping anim for windows");
+ } else {
+ if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: created anim for windows="
+ + unForceHiding);
+ mPostKeyguardExitAnimation = mPolicy.createForceHideEnterAnimation(
+ wallpaperInUnForceHiding, mKeyguardGoingAwayToNotificationShade);
+ }
+ if (mPostKeyguardExitAnimation != null) {
+ for (int i=unForceHiding.size()-1; i>=0; i--) {
final WindowStateAnimator winAnimator = unForceHiding.get(i);
- winAnimator.setAnimation(a);
+ winAnimator.setAnimation(mPostKeyguardExitAnimation);
winAnimator.keyguardGoingAwayAnimation = true;
- if (startKeyguardExit && mKeyguardGoingAway) {
- // Do one time only.
- mPolicy.startKeyguardExitAnimation(mCurrentTime + a.getStartOffset(),
- a.getDuration());
- mKeyguardGoingAway = false;
- startKeyguardExit = false;
- }
}
}
@@ -436,11 +444,23 @@
Animation a = mPolicy.createForceHideWallpaperExitAnimation(
mKeyguardGoingAwayToNotificationShade);
if (a != null) {
- WindowStateAnimator animator = wallpaper.mWinAnimator;
- animator.setAnimation(a);
+ wallpaper.mWinAnimator.setAnimation(a);
}
}
}
+
+ if (mPostKeyguardExitAnimation != null) {
+ // We're in the midst of a keyguard exit animation.
+ if (mKeyguardGoingAway) {
+ mPolicy.startKeyguardExitAnimation(mCurrentTime +
+ mPostKeyguardExitAnimation.getStartOffset(),
+ mPostKeyguardExitAnimation.getDuration());
+ mKeyguardGoingAway = false;
+ } else if (mPostKeyguardExitAnimation.hasEnded()) {
+ // Done with the animation, reset.
+ mPostKeyguardExitAnimation = null;
+ }
+ }
}
private void updateWallpaperLocked(int displayId) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b4e2778..968b35c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -9244,12 +9244,6 @@
topClosingLayer);
openingAppAnimator.deferThumbnailDestruction =
!mAppTransition.isNextThumbnailTransitionScaleUp();
- if (openingAppAnimator.deferThumbnailDestruction) {
- if (closingAppAnimator != null &&
- closingAppAnimator.animation != null) {
- closingAppAnimator.deferredThumbnail = surfaceControl;
- }
- }
} else {
anim = mAppTransition.createThumbnailScaleAnimationLocked(
displayInfo.appWidth, displayInfo.appHeight, transit);
@@ -9783,7 +9777,7 @@
if (!w.isDrawnLw()) {
Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceControl
+ " pv=" + w.mPolicyVisibility
- + " mDrawState=" + winAnimator.mDrawState
+ + " mDrawState=" + winAnimator.drawStateToString()
+ " ah=" + w.mAttachedHidden
+ " th=" + atoken.hiddenRequested
+ " a=" + winAnimator.mAnimating);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index fc3f2c2..819ca50 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -167,14 +167,14 @@
private static final int SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN =
View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- static String drawStateToString(int state) {
- switch (state) {
+ String drawStateToString() {
+ switch (mDrawState) {
case NO_SURFACE: return "NO_SURFACE";
case DRAW_PENDING: return "DRAW_PENDING";
case COMMIT_DRAW_PENDING: return "COMMIT_DRAW_PENDING";
case READY_TO_SHOW: return "READY_TO_SHOW";
case HAS_DRAWN: return "HAS_DRAWN";
- default: return Integer.toString(state);
+ default: return Integer.toString(mDrawState);
}
}
int mDrawState;
@@ -486,20 +486,23 @@
}
boolean finishDrawingLocked() {
- if (DEBUG_STARTING_WINDOW &&
- mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
+ final boolean startingWindow =
+ mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+ if (DEBUG_STARTING_WINDOW && startingWindow) {
Slog.v(TAG, "Finishing drawing window " + mWin + ": mDrawState="
- + drawStateToString(mDrawState));
+ + drawStateToString());
}
if (mDrawState == DRAW_PENDING) {
if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || SHOW_TRANSACTIONS || DEBUG_ORIENTATION)
Slog.v(TAG, "finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING " + this + " in "
+ mSurfaceControl);
- if (DEBUG_STARTING_WINDOW &&
- mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
+ if (DEBUG_STARTING_WINDOW && startingWindow) {
Slog.v(TAG, "Draw state now committed in " + mWin);
}
mDrawState = COMMIT_DRAW_PENDING;
+ if (startingWindow) {
+ mService.notifyActivityDrawnForKeyguard();
+ }
return true;
}
return false;
@@ -510,18 +513,17 @@
if (DEBUG_STARTING_WINDOW &&
mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
Slog.i(TAG, "commitFinishDrawingLocked: " + mWin + " cur mDrawState="
- + drawStateToString(mDrawState));
+ + drawStateToString());
}
- if (mDrawState != COMMIT_DRAW_PENDING) {
+ if (mDrawState != COMMIT_DRAW_PENDING && mDrawState != READY_TO_SHOW) {
return false;
}
if (DEBUG_SURFACE_TRACE || DEBUG_ANIM) {
Slog.i(TAG, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW " + mSurfaceControl);
}
mDrawState = READY_TO_SHOW;
- final boolean starting = mWin.mAttrs.type == TYPE_APPLICATION_STARTING;
final AppWindowToken atoken = mWin.mAppToken;
- if (atoken == null || atoken.allDrawn || starting) {
+ if (atoken == null || atoken.allDrawn || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
performShowLocked();
}
return true;
@@ -976,11 +978,6 @@
mWin.mHasSurface = false;
mDrawState = NO_SURFACE;
}
-
- // Destroy any deferred thumbnail surfaces
- if (mAppAnimator != null) {
- mAppAnimator.clearDeferredThumbnail();
- }
}
void destroyDeferredSurfaceLocked() {
@@ -1868,7 +1865,7 @@
if (dumpAll) {
pw.print(prefix); pw.print("mSurface="); pw.println(mSurfaceControl);
pw.print(prefix); pw.print("mDrawState=");
- pw.print(drawStateToString(mDrawState));
+ pw.print(drawStateToString());
pw.print(" mLastHidden="); pw.println(mLastHidden);
}
pw.print(prefix); pw.print("Surface: shown="); pw.print(mSurfaceShown);
diff --git a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
index 6958087..8183321 100644
--- a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -1195,6 +1195,7 @@
usedInFixSetterMethod,
(flags & GPS_MEASUREMENT_HAS_USED_IN_FIX) && measurement->used_in_fix);
+ env->DeleteLocalRef(gpsMeasurementClass);
return gpsMeasurementObject;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 308fcd8..2c6a222 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -60,6 +60,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.IPowerManager;
+import android.os.PersistableBundle;
import android.os.PowerManager;
import android.os.Process;
import android.os.RecoverySystem;
@@ -96,6 +97,7 @@
import com.android.org.conscrypt.TrustedCertificateStore;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
import org.xmlpull.v1.XmlPullParser;
@@ -322,7 +324,7 @@
= "permitted-accessiblity-services";
private static final String TAG_ENCRYPTION_REQUESTED = "encryption-requested";
private static final String TAG_MANAGE_TRUST_AGENT_FEATURES = "manage-trust-agent-features";
- private static final String TAG_TRUST_AGENT_FEATURE = "feature";
+ private static final String TAG_TRUST_AGENT_COMPONENT_OPTIONS = "trust-agent-component-options";
private static final String TAG_TRUST_AGENT_COMPONENT = "component";
private static final String TAG_PASSWORD_EXPIRATION_DATE = "password-expiration-date";
private static final String TAG_PASSWORD_EXPIRATION_TIMEOUT = "password-expiration-timeout";
@@ -389,6 +391,7 @@
long passwordExpirationDate = DEF_PASSWORD_EXPIRATION_DATE;
static final int DEF_KEYGUARD_FEATURES_DISABLED = 0; // none
+
int disabledKeyguardFeatures = DEF_KEYGUARD_FEATURES_DISABLED;
boolean encryptionRequested = false;
@@ -397,6 +400,13 @@
boolean disableScreenCapture = false; // Can only be set by a device/profile owner.
boolean requireAutoTime = false; // Can only be set by a device owner.
+ static class TrustAgentInfo {
+ public PersistableBundle options;
+ TrustAgentInfo(PersistableBundle bundle) {
+ options = bundle;
+ }
+ }
+
Set<String> accountTypesWithManagementDisabled = new HashSet<String>();
// The list of permitted accessibility services package namesas set by a profile
@@ -413,7 +423,8 @@
boolean specifiesGlobalProxy = false;
String globalProxySpec = null;
String globalProxyExclusionList = null;
- HashMap<String, List<String>> trustAgentFeatures = new HashMap<String, List<String>>();
+
+ HashMap<String, TrustAgentInfo> trustAgentInfos = new HashMap<String, TrustAgentInfo>();
List<String> crossProfileWidgetProviders;
@@ -551,16 +562,21 @@
}
out.endTag(null, TAG_DISABLE_ACCOUNT_MANAGEMENT);
}
- if (!trustAgentFeatures.isEmpty()) {
- Set<Entry<String, List<String>>> set = trustAgentFeatures.entrySet();
+ if (!trustAgentInfos.isEmpty()) {
+ Set<Entry<String, TrustAgentInfo>> set = trustAgentInfos.entrySet();
out.startTag(null, TAG_MANAGE_TRUST_AGENT_FEATURES);
- for (Entry<String, List<String>> component : set) {
+ for (Entry<String, TrustAgentInfo> entry : set) {
+ TrustAgentInfo trustAgentInfo = entry.getValue();
out.startTag(null, TAG_TRUST_AGENT_COMPONENT);
- out.attribute(null, ATTR_VALUE, component.getKey());
- for (String feature : component.getValue()) {
- out.startTag(null, TAG_TRUST_AGENT_FEATURE);
- out.attribute(null, ATTR_VALUE, feature);
- out.endTag(null, TAG_TRUST_AGENT_FEATURE);
+ out.attribute(null, ATTR_VALUE, entry.getKey());
+ if (trustAgentInfo.options != null) {
+ out.startTag(null, TAG_TRUST_AGENT_COMPONENT_OPTIONS);
+ try {
+ trustAgentInfo.options.saveToXml(out);
+ } catch (XmlPullParserException e) {
+ Log.e(LOG_TAG, "Failed to save TrustAgent options", e);
+ }
+ out.endTag(null, TAG_TRUST_AGENT_COMPONENT_OPTIONS);
}
out.endTag(null, TAG_TRUST_AGENT_COMPONENT);
}
@@ -679,7 +695,7 @@
} else if (TAG_DISABLE_ACCOUNT_MANAGEMENT.equals(tag)) {
accountTypesWithManagementDisabled = readDisableAccountInfo(parser, tag);
} else if (TAG_MANAGE_TRUST_AGENT_FEATURES.equals(tag)) {
- trustAgentFeatures = getAllTrustAgentFeatures(parser, tag);
+ trustAgentInfos = getAllTrustAgentInfos(parser, tag);
} else if (TAG_CROSS_PROFILE_WIDGET_PROVIDERS.equals(tag)) {
crossProfileWidgetProviders = getCrossProfileWidgetProviders(parser, tag);
} else if (TAG_PERMITTED_ACCESSIBILITY_SERVICES.equals(tag)) {
@@ -738,11 +754,11 @@
return result;
}
- private HashMap<String, List<String>> getAllTrustAgentFeatures(XmlPullParser parser,
- String tag) throws XmlPullParserException, IOException {
+ private HashMap<String, TrustAgentInfo> getAllTrustAgentInfos(
+ XmlPullParser parser, String tag) throws XmlPullParserException, IOException {
int outerDepthDAM = parser.getDepth();
int typeDAM;
- HashMap<String, List<String>> result = new HashMap<String, List<String>>();
+ HashMap<String, TrustAgentInfo> result = new HashMap<String, TrustAgentInfo>();
while ((typeDAM=parser.next()) != END_DOCUMENT
&& (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) {
if (typeDAM == END_TAG || typeDAM == TEXT) {
@@ -751,7 +767,8 @@
String tagDAM = parser.getName();
if (TAG_TRUST_AGENT_COMPONENT.equals(tagDAM)) {
final String component = parser.getAttributeValue(null, ATTR_VALUE);
- result.put(component, getTrustAgentFeatures(parser, tag));
+ final TrustAgentInfo trustAgentInfo = getTrustAgentInfo(parser, tag);
+ result.put(component, trustAgentInfo);
} else {
Slog.w(LOG_TAG, "Unknown tag under " + tag + ": " + tagDAM);
}
@@ -759,20 +776,21 @@
return result;
}
- private List<String> getTrustAgentFeatures(XmlPullParser parser, String tag)
+ private TrustAgentInfo getTrustAgentInfo(XmlPullParser parser, String tag)
throws XmlPullParserException, IOException {
int outerDepthDAM = parser.getDepth();
int typeDAM;
- ArrayList<String> result = new ArrayList<String>();
+ TrustAgentInfo result = new TrustAgentInfo(null);
while ((typeDAM=parser.next()) != END_DOCUMENT
&& (typeDAM != END_TAG || parser.getDepth() > outerDepthDAM)) {
if (typeDAM == END_TAG || typeDAM == TEXT) {
continue;
}
String tagDAM = parser.getName();
- if (TAG_TRUST_AGENT_FEATURE.equals(tagDAM)) {
- final String feature = parser.getAttributeValue(null, ATTR_VALUE);
- result.add(feature);
+ if (TAG_TRUST_AGENT_COMPONENT_OPTIONS.equals(tagDAM)) {
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.restoreFromXml(parser);
+ result.options = bundle;
} else {
Slog.w(LOG_TAG, "Unknown tag under " + tag + ": " + tagDAM);
}
@@ -1174,7 +1192,7 @@
int userHandle) {
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo ui : profiles) {
- int id = ui.getUserHandle().getIdentifier();
+ int id = ui.id;
sendAdminCommandLocked(action, reqPolicy, id);
}
}
@@ -1591,7 +1609,7 @@
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo ui : profiles) {
- int profileUserHandle = ui.getUserHandle().getIdentifier();
+ int profileUserHandle = ui.id;
final DevicePolicyData policy = getUserData(profileUserHandle);
final int count = policy.mAdminList.size();
if (count > 0) {
@@ -1878,7 +1896,7 @@
// Return strictest policy for this user and profiles that are visible from this user.
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ DevicePolicyData policy = getUserData(userInfo.id);
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -1925,7 +1943,7 @@
// Return strictest policy for this user and profiles that are visible from this user.
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ DevicePolicyData policy = getUserData(userInfo.id);
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -1972,7 +1990,7 @@
// Return strictest policy for this user and profiles that are visible from this user.
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ DevicePolicyData policy = getUserData(userInfo.id);
final int N = policy.mAdminList.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2033,7 +2051,7 @@
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ DevicePolicyData policy = getUserData(userInfo.id);
final int N = policy.mAdminList.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2131,7 +2149,7 @@
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ DevicePolicyData policy = getUserData(userInfo.id);
final int N = policy.mAdminList.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2188,7 +2206,7 @@
// Return strictest policy for this user and profiles that are visible from this user.
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ DevicePolicyData policy = getUserData(userInfo.id);
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2232,7 +2250,7 @@
// Return strictest policy for this user and profiles that are visible from this user.
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ DevicePolicyData policy = getUserData(userInfo.id);
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2279,7 +2297,7 @@
// Return strictest policy for this user and profiles that are visible from this user.
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ DevicePolicyData policy = getUserData(userInfo.id);
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2326,7 +2344,7 @@
// Return strictest policy for this user and profiles that are visible from this user.
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ DevicePolicyData policy = getUserData(userInfo.id);
final int N = policy.mAdminList.size();
for (int i = 0; i < N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2373,7 +2391,7 @@
// Return strictest policy for this user and profiles that are visible from this user.
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ DevicePolicyData policy = getUserData(userInfo.id);
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2420,7 +2438,7 @@
// Return strictest policy for this user and profiles that are visible from this user.
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ DevicePolicyData policy = getUserData(userInfo.id);
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -2526,7 +2544,7 @@
int count = 0;
ActiveAdmin strictestAdmin = null;
for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) {
- DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ DevicePolicyData policy = getUserData(userInfo.id);
for (ActiveAdmin admin : policy.mAdminList) {
if (admin.maximumFailedPasswordsForWipe ==
ActiveAdmin.DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE) {
@@ -2738,7 +2756,7 @@
// Return strictest policy for this user and profiles that are visible from this user.
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ DevicePolicyData policy = getUserData(userInfo.id);
final int N = policy.mAdminList.size();
for (int i=0; i<N; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
@@ -3055,7 +3073,7 @@
private void updatePasswordExpirationsLocked(int userHandle) {
List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
for (UserInfo userInfo : profiles) {
- int profileId = userInfo.getUserHandle().getIdentifier();
+ int profileId = userInfo.id;
DevicePolicyData policy = getUserData(profileId);
final int N = policy.mAdminList.size();
if (N > 0) {
@@ -4106,13 +4124,13 @@
}
}
- public void setTrustAgentFeaturesEnabled(ComponentName admin, ComponentName agent,
- List<String>features, int userHandle) {
+ public void setTrustAgentConfiguration(ComponentName admin, ComponentName agent,
+ PersistableBundle args, int userHandle) {
if (!mHasFeature) {
return;
}
enforceCrossUserPermission(userHandle);
- enforceNotManagedProfile(userHandle, "manage trust agent features");
+ enforceNotManagedProfile(userHandle, "set trust agent configuration");
synchronized (this) {
if (admin == null) {
throw new NullPointerException("admin is null");
@@ -4122,57 +4140,68 @@
}
ActiveAdmin ap = getActiveAdminForCallerLocked(admin,
DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES);
- ap.trustAgentFeatures.put(agent.flattenToString(), features);
+ ap.trustAgentInfos.put(agent.flattenToString(), new TrustAgentInfo(args));
saveSettingsLocked(userHandle);
syncDeviceCapabilitiesLocked(getUserData(userHandle));
}
}
- public List<String> getTrustAgentFeaturesEnabled(ComponentName admin, ComponentName agent,
- int userHandle) {
+ public List<PersistableBundle> getTrustAgentConfiguration(ComponentName admin,
+ ComponentName agent, int userHandle) {
if (!mHasFeature) {
return null;
}
enforceCrossUserPermission(userHandle);
+ if (agent == null) {
+ throw new NullPointerException("agent is null");
+ }
+
synchronized (this) {
- if (agent == null) {
- throw new NullPointerException("agent is null");
- }
final String componentName = agent.flattenToString();
if (admin != null) {
final ActiveAdmin ap = getActiveAdminUncheckedLocked(admin, userHandle);
- return (ap != null) ? ap.trustAgentFeatures.get(componentName) : null;
+ if (ap == null) return null;
+ TrustAgentInfo trustAgentInfo = ap.trustAgentInfos.get(componentName);
+ if (trustAgentInfo == null || trustAgentInfo.options == null) return null;
+ List<PersistableBundle> result = new ArrayList<PersistableBundle>();
+ result.add(trustAgentInfo.options);
+ return result;
}
// Return strictest policy for this user and profiles that are visible from this user.
- List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
- List<String> result = null;
+ final List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+ List<PersistableBundle> result = null;
+
+ // Search through all admins that use KEYGUARD_DISABLE_TRUST_AGENTS and keep track
+ // of the options. If any admin doesn't have options, discard options for the rest
+ // and return null.
+ boolean allAdminsHaveOptions = true;
for (UserInfo userInfo : profiles) {
- DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ DevicePolicyData policy = getUserData(userInfo.id);
final int N = policy.mAdminList.size();
- for (int i=0; i<N; i++) {
- ActiveAdmin ap = policy.mAdminList.get(i);
- // Compute the intersection of all features for active admins that disable
- // trust agents:
- if ((ap.disabledKeyguardFeatures
- & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0) {
- final List<String> features = ap.trustAgentFeatures.get(componentName);
- if (result == null) {
- if (features == null || features.size() == 0) {
- result = new ArrayList<String>();
- Slog.w(LOG_TAG, "admin " + ap.info.getPackageName()
- + " has null trust agent feature set; all will be disabled");
- } else {
- result = new ArrayList<String>(features.size());
- result.addAll(features);
+ for (int i=0; i < N; i++) {
+ final ActiveAdmin active = policy.mAdminList.get(i);
+ final boolean disablesTrust = (active.disabledKeyguardFeatures
+ & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0;
+ final TrustAgentInfo info = active.trustAgentInfos.get(componentName);
+ if (info != null && info.options != null && !info.options.isEmpty()) {
+ if (disablesTrust) {
+ if (result == null) {
+ result = new ArrayList<PersistableBundle>();
}
+ result.add(info.options);
} else {
- result.retainAll(features);
+ Log.w(LOG_TAG, "Ignoring admin " + active.info
+ + " because it has trust options but doesn't declare "
+ + "KEYGUARD_DISABLE_TRUST_AGENTS");
}
+ } else if (disablesTrust) {
+ allAdminsHaveOptions = false;
+ break;
}
}
}
- return result;
+ return allAdminsHaveOptions ? result : null;
}
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index 11da380..098b3ef 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -119,6 +119,7 @@
if (!files.valueAt(start).getBaseFile().getName().endsWith("-c")) {
break;
}
+ start++;
}
if (start == fileCount - 1) {
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 003d5cd..6480a8a 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -207,6 +207,13 @@
}
/**
+ * @return The {@link DisconnectCause} for this connection.
+ */
+ public final DisconnectCause getDisconnectCause() {
+ return mDisconnectCause;
+ }
+
+ /**
* Sets the capabilities of a conference. See {@link PhoneCapabilities} for valid values.
*
* @param capabilities A bitmask of the {@code PhoneCapabilities} of the conference call.
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 9bdbba8..34d0660 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -974,6 +974,15 @@
public void onDisconnect() {}
/**
+ * Notifies this Connection of a request to disconnect a participant of the conference managed
+ * by the connection.
+ *
+ * @param endpoint the {@link Uri} of the participant to disconnect.
+ * @hide
+ */
+ public void onDisconnectConferenceParticipant(Uri endpoint) {}
+
+ /**
* Notifies this Connection of a request to separate from its parent conference.
*/
public void onSeparate() {}
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 206046d..73bcd0c 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -58,6 +58,11 @@
public static final int RESTRICTED = 8;
/** Disconnected for reason not described by other disconnect codes. */
public static final int OTHER = 9;
+ /**
+ * Disconnected because the connection manager did not support the call. The call will be tried
+ * again without a connection manager. See {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}.
+ */
+ public static final int CONNECTION_MANAGER_NOT_SUPPORTED = 10;
private int mDisconnectCode;
private CharSequence mDisconnectLabel;
@@ -220,7 +225,10 @@
@Override
public String toString() {
String code = "";
- switch (getCode()) {
+ switch (mDisconnectCode) {
+ case UNKNOWN:
+ code = "UNKNOWN";
+ break;
case ERROR:
code = "ERROR";
break;
@@ -230,6 +238,9 @@
case REMOTE:
code = "REMOTE";
break;
+ case CANCELED:
+ code = "CANCELED";
+ break;
case MISSED:
code = "MISSED";
break;
@@ -245,9 +256,12 @@
case OTHER:
code = "OTHER";
break;
- case UNKNOWN:
+ case CONNECTION_MANAGER_NOT_SUPPORTED:
+ code = "CONNECTION_MANAGER_NOT_SUPPORTED";
+ break;
default:
- code = "UNKNOWN";
+ code = "invalid code: " + mDisconnectCode;
+ break;
}
String label = mDisconnectLabel == null ? "" : mDisconnectLabel.toString();
String description = mDisconnectDescription == null
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index d98a255..ecf6005 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -338,8 +338,6 @@
* @param uriScheme The URI scheme.
* @return The {@link PhoneAccountHandle} corresponding to the user-chosen default for outgoing
* phone calls for a specified URI scheme.
- *
- * @hide
*/
public PhoneAccountHandle getDefaultOutgoingPhoneAccount(String uriScheme) {
try {
@@ -459,8 +457,6 @@
*
* @param uriScheme The URI scheme.
* @return A list of {@code PhoneAccountHandle} objects supporting the URI scheme.
- *
- * @hide
*/
public List<PhoneAccountHandle> getPhoneAccountsSupportingScheme(String uriScheme) {
try {
diff --git a/telephony/java/android/telephony/SubInfoRecord.java b/telephony/java/android/telephony/SubInfoRecord.java
index 0966ddb..8ab69cc 100644
--- a/telephony/java/android/telephony/SubInfoRecord.java
+++ b/telephony/java/android/telephony/SubInfoRecord.java
@@ -222,7 +222,7 @@
int id = source.readInt();
String iccId = source.readString();
int simSlotIndex = source.readInt();
- String displayName = source.readString();
+ CharSequence displayName = source.readCharSequence();
int nameSource = source.readInt();
int color = source.readInt();
String number = source.readString();
@@ -247,10 +247,10 @@
dest.writeInt(mId);
dest.writeString(mIccId);
dest.writeInt(mSimSlotIndex);
- dest.writeString(mDisplayName.toString());
+ dest.writeCharSequence(mDisplayName);
dest.writeInt(mNameSource);
dest.writeInt(mColor);
- dest.writeString(mNumber.toString());
+ dest.writeString(mNumber);
dest.writeInt(mDataRoaming);
dest.writeIntArray(mSimIconRes);
dest.writeInt(mMcc);
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
index 6837d22..94874c8 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/ActivityTestMain.java
@@ -127,7 +127,7 @@
@Override
public boolean onLongClick(View v) {
if (task.id >= 0 && thumbs != null) {
- mAm.removeTask(task.id, ActivityManager.REMOVE_TASK_KILL_PROCESS);
+ mAm.removeTask(task.id);
buildUi();
return true;
}
diff --git a/tests/VectorDrawableTest/res/anim/ic_open_animation_ball_start.xml b/tests/VectorDrawableTest/res/anim/ic_open_animation_ball_start.xml
deleted file mode 100644
index c26c8ed..0000000
--- a/tests/VectorDrawableTest/res/anim/ic_open_animation_ball_start.xml
+++ /dev/null
@@ -1,86 +0,0 @@
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="833"
- android:propertyName="translateX"
- android:valueFrom="144"
- android:valueTo="144"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="100"
- android:propertyName="translateX"
- android:valueFrom="144"
- android:valueTo="147.411817411"
- android:interpolator="@interpolator/ic_open_ball_start_translatex_interpolator" />
- </set>
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="167"
- android:propertyName="translateY"
- android:valueFrom="144.58457376"
- android:valueTo="144.58457376"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="333"
- android:propertyName="translateY"
- android:valueFrom="144.58457376"
- android:valueTo="196.11111456"
- android:interpolator="@interpolator/ic_open_ball_start_translatey_interpolator_1" />
- <objectAnimator
- android:duration="333"
- android:propertyName="translateY"
- android:valueFrom="196.11111456"
- android:valueTo="196.11111456"
- android:interpolator="@interpolator/ic_open_ball_start_translatey_interpolator_2" />
- <objectAnimator
- android:duration="100"
- android:propertyName="translateY"
- android:valueFrom="196.11111456"
- android:valueTo="129.468428513"
- android:interpolator="@interpolator/ic_open_ball_start_translatey_interpolator_3" />
- </set>
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="167"
- android:propertyName="scaleX"
- android:valueFrom="0"
- android:valueTo="0.71187984"
- android:interpolator="@interpolator/ic_open_ball_start_scalex_interpolator_1" />
- <objectAnimator
- android:duration="667"
- android:propertyName="scaleX"
- android:valueFrom="0.71187984"
- android:valueTo="0.71187984"
- android:interpolator="@interpolator/ic_open_ball_start_scalex_interpolator_2" />
- <objectAnimator
- android:duration="100"
- android:propertyName="scaleX"
- android:valueFrom="0.71187984"
- android:valueTo="0.586201598553"
- android:interpolator="@interpolator/ic_open_ball_start_scalex_interpolator_3" />
- </set>
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="167"
- android:propertyName="scaleY"
- android:valueFrom="0"
- android:valueTo="0.71187984"
- android:interpolator="@interpolator/ic_open_ball_start_scaley_interpolator_1" />
- <objectAnimator
- android:duration="667"
- android:propertyName="scaleY"
- android:valueFrom="0.71187984"
- android:valueTo="0.71187984"
- android:interpolator="@interpolator/ic_open_ball_start_scaley_interpolator_2" />
- <objectAnimator
- android:duration="100"
- android:propertyName="scaleY"
- android:valueFrom="0.71187984"
- android:valueTo="0.586201598553"
- android:interpolator="@interpolator/ic_open_ball_start_scaley_interpolator_3" />
- </set>
-</set>
diff --git a/tests/VectorDrawableTest/res/anim/ic_open_animation_ball_swoop.xml b/tests/VectorDrawableTest/res/anim/ic_open_animation_ball_swoop.xml
deleted file mode 100644
index 5096514..0000000
--- a/tests/VectorDrawableTest/res/anim/ic_open_animation_ball_swoop.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="933"
- android:propertyName="rotation"
- android:valueFrom="-90"
- android:valueTo="-90"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="1367"
- android:propertyName="rotation"
- android:valueFrom="-90"
- android:valueTo="-87"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/tests/VectorDrawableTest/res/anim/ic_open_animation_path_1.xml b/tests/VectorDrawableTest/res/anim/ic_open_animation_path_1.xml
deleted file mode 100644
index ef8496e..0000000
--- a/tests/VectorDrawableTest/res/anim/ic_open_animation_path_1.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="500"
- android:propertyName="pathData"
- android:valueFrom="M 0,-17.3838043213 c -9.625,0.0 -17.0795440674,8.28025817871 -17.0795440674,17.4052734375 c 0.0,9.75645446777 7.51704406738,17.6295623779 17.0795440674,17.353515625 c 11.5579986572,-0.333648681641 17.2784118652,-8.72853088379 17.2784118652,-17.353515625 c 0.0,-8.62501525879 -7.65341186523,-17.4052734375 -17.2784118652,-17.4052734375 Z"
- android:valueTo="M 0,-17.3838043213 c -9.625,0.0 -17.0795440674,8.28025817871 -17.0795440674,17.4052734375 c 0.0,9.75645446777 7.51704406738,17.6295623779 17.0795440674,17.353515625 c 11.5579986572,-0.333648681641 17.2784118652,-8.72853088379 17.2784118652,-17.353515625 c 0.0,-8.62501525879 -7.65341186523,-17.4052734375 -17.2784118652,-17.4052734375 Z"
- android:valueType="pathType"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="267"
- android:propertyName="pathData"
- android:valueFrom="M 0,-17.3838043213 c -9.625,0.0 -17.0795440674,8.28025817871 -17.0795440674,17.4052734375 c 0.0,9.75645446777 7.51704406738,17.6295623779 17.0795440674,17.353515625 c 11.5579986572,-0.333648681641 17.2784118652,-8.72853088379 17.2784118652,-17.353515625 c 0.0,-8.62501525879 -7.65341186523,-17.4052734375 -17.2784118652,-17.4052734375 Z"
- android:valueTo="M 0,-6 c -9.625,0 -23.5648803711,6.97859191895 -23.5648803711,16.1035919189 c 0.0,8.875 6.62738037109,8.39640808105 23.5648803711,8.39640808105 c 17.0625,0.0 23.8825073242,0.375 23.8825073242,-8.25 c 0.0,-8.625 -14.2574920654,-16.25 -23.8825073242,-16.25 Z"
- android:valueType="pathType"
- android:interpolator="@interpolator/ic_open_path_1_pathdata_interpolator_1" />
- <objectAnimator
- android:duration="33"
- android:propertyName="pathData"
- android:valueFrom="M 0,-6 c -9.625,0 -23.5648803711,6.97859191895 -23.5648803711,16.1035919189 c 0.0,8.875 6.62738037109,8.39640808105 23.5648803711,8.39640808105 c 17.0625,0.0 23.8825073242,0.375 23.8825073242,-8.25 c 0.0,-8.625 -14.2574920654,-16.25 -23.8825073242,-16.25 Z"
- android:valueTo="M 0,-6 c -9.625,0 -23.5648803711,6.97859191895 -23.5648803711,16.1035919189 c 0.0,8.875 6.62738037109,8.39640808105 23.5648803711,8.39640808105 c 17.0625,0.0 23.8825073242,0.375 23.8825073242,-8.25 c 0.0,-8.625 -14.2574920654,-16.25 -23.8825073242,-16.25 Z"
- android:valueType="pathType"
- android:interpolator="@interpolator/ic_open_path_1_pathdata_interpolator_2" />
- <objectAnimator
- android:duration="67"
- android:propertyName="pathData"
- android:valueFrom="M 0,-6 c -9.625,0 -23.5648803711,6.97859191895 -23.5648803711,16.1035919189 c 0.0,8.875 6.62738037109,8.39640808105 23.5648803711,8.39640808105 c 17.0625,0.0 23.8825073242,0.375 23.8825073242,-8.25 c 0.0,-8.625 -14.2574920654,-16.25 -23.8825073242,-16.25 Z"
- android:valueTo="M 0,-17.3838043213 c -9.625,0.0 -17.0795440674,8.28025817871 -17.0795440674,17.4052734375 c 0.0,9.75645446777 7.51704406738,17.6295623779 17.0795440674,17.353515625 c 11.5579986572,-0.333648681641 17.2784118652,-8.72853088379 17.2784118652,-17.353515625 c 0.0,-8.62501525879 -7.65341186523,-17.4052734375 -17.2784118652,-17.4052734375 Z"
- android:valueType="pathType"
- android:interpolator="@interpolator/ic_open_path_1_pathdata_interpolator_3" />
- </set>
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="933"
- android:propertyName="fillAlpha"
- android:valueFrom="1"
- android:valueTo="1"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="17"
- android:propertyName="fillAlpha"
- android:valueFrom="1"
- android:valueTo="0"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/tests/VectorDrawableTest/res/anim/ic_open_animation_path_1_1.xml b/tests/VectorDrawableTest/res/anim/ic_open_animation_path_1_1.xml
deleted file mode 100644
index 4961204..0000000
--- a/tests/VectorDrawableTest/res/anim/ic_open_animation_path_1_1.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="917"
- android:propertyName="strokeAlpha"
- android:valueFrom="0"
- android:valueTo="0"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="17"
- android:propertyName="strokeAlpha"
- android:valueFrom="0"
- android:valueTo="1"
- android:interpolator="@android:interpolator/linear" />
- </set>
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="917"
- android:propertyName="strokeWidth"
- android:valueFrom="0"
- android:valueTo="0"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="17"
- android:propertyName="strokeWidth"
- android:valueFrom="0"
- android:valueTo="20"
- android:interpolator="@android:interpolator/linear" />
- </set>
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="933"
- android:propertyName="trimPathStart"
- android:valueFrom="0.06"
- android:valueTo="0.06"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="383"
- android:propertyName="trimPathStart"
- android:valueFrom="0.06"
- android:valueTo="0.19231"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="983"
- android:propertyName="trimPathStart"
- android:valueFrom="0.19231"
- android:valueTo="0.999"
- android:interpolator="@interpolator/ic_open_path_1_1_trimpathstart_interpolator_2" />
- </set>
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="933"
- android:propertyName="trimPathEnd"
- android:valueFrom="0.061"
- android:valueTo="0.061"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="300"
- android:propertyName="trimPathEnd"
- android:valueFrom="0.061"
- android:valueTo="0.19231"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="1067"
- android:propertyName="trimPathEnd"
- android:valueFrom="0.19231"
- android:valueTo="1"
- android:interpolator="@interpolator/ic_open_path_1_1_trimpathend_interpolator_2" />
- </set>
-</set>
diff --git a/tests/VectorDrawableTest/res/drawable/ic_open.xml b/tests/VectorDrawableTest/res/drawable/ic_open.xml
deleted file mode 100644
index ad96094..0000000
--- a/tests/VectorDrawableTest/res/drawable/ic_open.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="288dp"
- android:width="288dp"
- android:viewportHeight="288"
- android:viewportWidth="288" >
- <group
- android:name="ball_start"
- android:translateX="147.411817411"
- android:translateY="129.468428513"
- android:scaleX="0.586201598553"
- android:scaleY="0.586201598553" >
- <group
- android:name="shape_1"
- android:scaleX="0.76"
- android:scaleY="0.748114397321" >
- <path
- android:name="path_1"
- android:pathData="M 0,-17.3838043213 c -9.625,0.0 -17.0795440674,8.28025817871 -17.0795440674,17.4052734375 c 0.0,9.75645446777 7.51704406738,17.6295623779 17.0795440674,17.353515625 c 11.5579986572,-0.333648681641 17.2784118652,-8.72853088379 17.2784118652,-17.353515625 c 0.0,-8.62501525879 -7.65341186523,-17.4052734375 -17.2784118652,-17.4052734375 Z"
- android:fillColor="#FFFF9000"
- android:fillAlpha="0" />
- </group>
- </group>
- <group
- android:name="ball_swoop"
- android:translateX="144"
- android:translateY="144"
- android:scaleX="0.752248"
- android:scaleY="0.752248"
- android:rotation="-87.6585365854" >
- <path
- android:name="path_1_1"
- android:pathData="M -56.7679443359,1.03857421875 c 0.0,0.0 191.916503906,-13.9097290039 191.916503906,88.0704345703 c 0.0,58.4487304688 -83.6709594727,90.1372070312 -137.004882812,90.1372070312 c -82.1782226562,0.0 -177.867431641,-63.5512695312 -177.867431641,-178.207641602 c 0.0,-115.985717773 98.7650146484,-178.160949707 177.986938477,-178.160949707 c 76.2376251221,0.0 178.1640625,60.6796875 178.1640625,178.185058594 "
- android:strokeColor="#FFFF9000"
- android:strokeAlpha="1"
- android:strokeWidth="20"
- android:strokeLineCap="round"
- android:trimPathStart="0.93025"
- android:trimPathEnd="0.96248"
- android:trimPathOffset="0"
- android:fillColor="#00000000" />
- </group>
-</vector>
diff --git a/tests/VectorDrawableTest/res/drawable/ic_open_animation.xml b/tests/VectorDrawableTest/res/drawable/ic_open_animation.xml
deleted file mode 100644
index 83ee90b..0000000
--- a/tests/VectorDrawableTest/res/drawable/ic_open_animation.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/ic_open" >
- <target
- android:name="ball_start"
- android:animation="@anim/ic_open_animation_ball_start" />
- <target
- android:name="path_1"
- android:animation="@anim/ic_open_animation_path_1" />
- <target
- android:name="ball_swoop"
- android:animation="@anim/ic_open_animation_ball_swoop" />
- <target
- android:name="path_1_1"
- android:animation="@anim/ic_open_animation_path_1_1" />
-</animated-vector>
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_scalex_interpolator_1.xml b/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_scalex_interpolator_1.xml
deleted file mode 100644
index 601cfc6..0000000
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_scalex_interpolator_1.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.33333333,0.0 0.202777547991,1.0 1.0,1.0" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_scalex_interpolator_2.xml b/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_scalex_interpolator_2.xml
deleted file mode 100644
index 5011ef9..0000000
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_scalex_interpolator_2.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.0103034467173,0.0 0.701918866569,1.0 1.0,1.0" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_scalex_interpolator_3.xml b/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_scalex_interpolator_3.xml
deleted file mode 100644
index 7b0af97..0000000
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_scalex_interpolator_3.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.649706648701,0.0 0.884285938423,1.92358061843 1.0,1.0" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_scaley_interpolator_1.xml b/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_scaley_interpolator_1.xml
deleted file mode 100644
index 601cfc6..0000000
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_scaley_interpolator_1.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.33333333,0.0 0.202777547991,1.0 1.0,1.0" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_scaley_interpolator_2.xml b/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_scaley_interpolator_2.xml
deleted file mode 100644
index 5011ef9..0000000
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_scaley_interpolator_2.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.0103034467173,0.0 0.701918866569,1.0 1.0,1.0" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_scaley_interpolator_3.xml b/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_scaley_interpolator_3.xml
deleted file mode 100644
index 7b0af97..0000000
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_scaley_interpolator_3.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.649706648701,0.0 0.884285938423,1.92358061843 1.0,1.0" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_translatex_interpolator.xml b/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_translatex_interpolator.xml
deleted file mode 100644
index ea11d1f..0000000
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_translatex_interpolator.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.353876322169,0.0 0.686452288267,-1.02094740172 1.0,1.0" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_translatey_interpolator_1.xml b/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_translatey_interpolator_1.xml
deleted file mode 100644
index 7bd5c49..0000000
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_translatey_interpolator_1.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.754478769148,0.0 0.97,1.0 1.0,1.0" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_translatey_interpolator_2.xml b/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_translatey_interpolator_2.xml
deleted file mode 100644
index b0ab6e8..0000000
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_translatey_interpolator_2.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.33333333,0.0 0.83333333333,1.0 1.0,1.0" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_translatey_interpolator_3.xml b/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_translatey_interpolator_3.xml
deleted file mode 100644
index 61060a6..0000000
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_ball_start_translatey_interpolator_3.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.324310863613,0.0 0.735625629425,-0.0161527278292 1.0,1.0" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_1_trimpathend_interpolator_2.xml b/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_1_trimpathend_interpolator_2.xml
deleted file mode 100644
index 7e19ef6..0000000
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_1_trimpathend_interpolator_2.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.744517024668,0.120263649138 0.135947812437,0.994319475209 1.0,1.0" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_1_trimpathstart_interpolator_2.xml b/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_1_trimpathstart_interpolator_2.xml
deleted file mode 100644
index 1280715..0000000
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_1_trimpathstart_interpolator_2.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.781904890372,0.126303002187 0.188007240906,0.953418294755 1.0,1.0" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_1.xml b/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_1.xml
deleted file mode 100644
index ddf966e..0000000
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_1.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 0.000100000000012,0.0 0.0,1.0 1.0,1.0" />
diff --git a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_2.xml b/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_2.xml
deleted file mode 100644
index 624e304..0000000
--- a/tests/VectorDrawableTest/res/interpolator/ic_open_path_1_pathdata_interpolator_2.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
- android:pathData="M 0 0 c 9.99999999007e-05,0.0 0.0,1.0 1.0,1.0" />
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
index 3045839..c4dfb84 100644
--- a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
@@ -26,7 +26,6 @@
private static final String LOGCAT = "AnimatedVectorDrawableTest";
protected int[] icon = {
- R.drawable.ic_open_animation,
R.drawable.ic_rotate_2_portrait_v2_animation,
R.drawable.ic_signal_airplane_v2_animation,
R.drawable.ic_hourglass_animation,
diff --git a/tools/aapt/AaptConfig.cpp b/tools/aapt/AaptConfig.cpp
index f447462..848d9a1 100644
--- a/tools/aapt/AaptConfig.cpp
+++ b/tools/aapt/AaptConfig.cpp
@@ -794,4 +794,23 @@
return a.diff(b) == axisMask;
}
+bool isDensityOnly(const ResTable_config& config) {
+ if (config.density == ResTable_config::DENSITY_NONE) {
+ return false;
+ }
+
+ if (config.density == ResTable_config::DENSITY_ANY) {
+ if (config.sdkVersion != SDK_L) {
+ // Someone modified the sdkVersion from the default, this is not safe to assume.
+ return false;
+ }
+ } else if (config.sdkVersion != SDK_DONUT) {
+ return false;
+ }
+
+ const uint32_t mask = ResTable_config::CONFIG_DENSITY | ResTable_config::CONFIG_VERSION;
+ const ConfigDescription nullConfig;
+ return (nullConfig.diff(config) & ~mask) == 0;
+}
+
} // namespace AaptConfig
diff --git a/tools/aapt/AaptConfig.h b/tools/aapt/AaptConfig.h
index 2963539..f73a5081 100644
--- a/tools/aapt/AaptConfig.h
+++ b/tools/aapt/AaptConfig.h
@@ -80,6 +80,12 @@
*/
bool isSameExcept(const android::ResTable_config& a, const android::ResTable_config& b, int configMask);
+/**
+ * Returns true if the configuration only has the density specified. In the case
+ * of 'anydpi', the version is ignored.
+ */
+bool isDensityOnly(const android::ResTable_config& config);
+
} // namespace AaptConfig
#endif // __AAPT_CONFIG_H
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index b9bd03a..0d8db13 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -4,6 +4,7 @@
// Build resource files from raw assets.
//
#include "AaptAssets.h"
+#include "AaptUtil.h"
#include "AaptXml.h"
#include "CacheUpdater.h"
#include "CrunchCache.h"
@@ -13,9 +14,12 @@
#include "Main.h"
#include "ResourceTable.h"
#include "StringPool.h"
+#include "Symbol.h"
#include "WorkQueue.h"
#include "XMLNode.h"
+#include <algorithm>
+
#if HAVE_PRINTF_ZD
# define ZD "%zd"
# define ZD_TYPE ssize_t
@@ -1550,6 +1554,7 @@
// Re-flatten because we may have added new resource IDs
// --------------------------------------------------------------
+
ResTable finalResTable;
sp<AaptFile> resFile;
@@ -1560,6 +1565,13 @@
return err;
}
+ KeyedVector<Symbol, Vector<SymbolDefinition> > densityVaryingResources;
+ if (builder->getSplits().size() > 1) {
+ // Only look for density varying resources if we're generating
+ // splits.
+ table.getDensityVaryingResources(densityVaryingResources);
+ }
+
Vector<sp<ApkSplit> >& splits = builder->getSplits();
const size_t numSplits = splits.size();
for (size_t i = 0; i < numSplits; i++) {
@@ -1583,6 +1595,63 @@
return err;
}
} else {
+ ResTable resTable;
+ err = resTable.add(flattenedTable->getData(), flattenedTable->getSize());
+ if (err != NO_ERROR) {
+ fprintf(stderr, "Generated resource table for split '%s' is corrupt.\n",
+ split->getPrintableName().string());
+ return err;
+ }
+
+ bool hasError = false;
+ const std::set<ConfigDescription>& splitConfigs = split->getConfigs();
+ for (std::set<ConfigDescription>::const_iterator iter = splitConfigs.begin();
+ iter != splitConfigs.end();
+ ++iter) {
+ const ConfigDescription& config = *iter;
+ if (AaptConfig::isDensityOnly(config)) {
+ // Each density only split must contain all
+ // density only resources.
+ Res_value val;
+ resTable.setParameters(&config);
+ const size_t densityVaryingResourceCount = densityVaryingResources.size();
+ for (size_t k = 0; k < densityVaryingResourceCount; k++) {
+ const Symbol& symbol = densityVaryingResources.keyAt(k);
+ ssize_t block = resTable.getResource(symbol.id, &val, true);
+ if (block < 0) {
+ // Maybe it's in the base?
+ finalResTable.setParameters(&config);
+ block = finalResTable.getResource(symbol.id, &val, true);
+ }
+
+ if (block < 0) {
+ hasError = true;
+ SourcePos().error("%s has no definition for density split '%s'",
+ symbol.toString().string(), config.toString().string());
+
+ if (bundle->getVerbose()) {
+ const Vector<SymbolDefinition>& defs = densityVaryingResources[k];
+ const size_t defCount = std::min(size_t(5), defs.size());
+ for (size_t d = 0; d < defCount; d++) {
+ const SymbolDefinition& def = defs[d];
+ def.source.error("%s has definition for %s",
+ symbol.toString().string(), def.config.toString().string());
+ }
+
+ if (defCount < defs.size()) {
+ SourcePos().error("and %d more ...", (int) (defs.size() - defCount));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (hasError) {
+ return UNKNOWN_ERROR;
+ }
+
+ // Generate the AndroidManifest for this split.
sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"),
AaptGroupEntry(), String8());
err = generateAndroidManifestForSplit(bundle, assets, split,
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 0f94f85..beff604 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -6,6 +6,7 @@
#include "ResourceTable.h"
+#include "AaptUtil.h"
#include "XMLNode.h"
#include "ResourceFilter.h"
#include "ResourceIdCache.h"
@@ -4486,3 +4487,34 @@
return NO_ERROR;
}
+
+void ResourceTable::getDensityVaryingResources(KeyedVector<Symbol, Vector<SymbolDefinition> >& resources) {
+ const ConfigDescription nullConfig;
+
+ const size_t packageCount = mOrderedPackages.size();
+ for (size_t p = 0; p < packageCount; p++) {
+ const Vector<sp<Type> >& types = mOrderedPackages[p]->getOrderedTypes();
+ const size_t typeCount = types.size();
+ for (size_t t = 0; t < typeCount; t++) {
+ const Vector<sp<ConfigList> >& configs = types[t]->getOrderedConfigs();
+ const size_t configCount = configs.size();
+ for (size_t c = 0; c < configCount; c++) {
+ const DefaultKeyedVector<ConfigDescription, sp<Entry> >& configEntries = configs[c]->getEntries();
+ const size_t configEntryCount = configEntries.size();
+ for (size_t ce = 0; ce < configEntryCount; ce++) {
+ const ConfigDescription& config = configEntries.keyAt(ce);
+ if (AaptConfig::isDensityOnly(config)) {
+ // This configuration only varies with regards to density.
+ const Symbol symbol(mOrderedPackages[p]->getName(),
+ types[t]->getName(),
+ configs[c]->getName(),
+ getResId(mOrderedPackages[p], types[t], configs[c]->getEntryIndex()));
+
+ const sp<Entry>& entry = configEntries.valueAt(ce);
+ AaptUtil::appendValue(resources, symbol, SymbolDefinition(symbol, config, entry->getPos()));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index eac5dd3..db392c89 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -7,15 +7,16 @@
#ifndef RESOURCE_TABLE_H
#define RESOURCE_TABLE_H
-#include "ConfigDescription.h"
-#include "StringPool.h"
-#include "SourcePos.h"
-#include "ResourceFilter.h"
-
#include <map>
#include <queue>
#include <set>
+#include "ConfigDescription.h"
+#include "ResourceFilter.h"
+#include "SourcePos.h"
+#include "StringPool.h"
+#include "Symbol.h"
+
using namespace std;
class XMLNode;
@@ -543,6 +544,8 @@
DefaultKeyedVector<String16, uint32_t> mKeyStringsMapping;
};
+ void getDensityVaryingResources(KeyedVector<Symbol, Vector<SymbolDefinition> >& resources);
+
private:
void writePublicDefinitions(const String16& package, FILE* fp, bool pub);
sp<Package> getPackage(const String16& package);
diff --git a/tools/aapt/SourcePos.cpp b/tools/aapt/SourcePos.cpp
index ae25047..3864320 100644
--- a/tools/aapt/SourcePos.cpp
+++ b/tools/aapt/SourcePos.cpp
@@ -141,6 +141,12 @@
}
bool
+SourcePos::operator<(const SourcePos& rhs) const
+{
+ return (file < rhs.file) || (line < rhs.line);
+}
+
+bool
SourcePos::hasErrors()
{
return g_errors.size() > 0;
diff --git a/tools/aapt/SourcePos.h b/tools/aapt/SourcePos.h
index 4ce817f..13cfb9d 100644
--- a/tools/aapt/SourcePos.h
+++ b/tools/aapt/SourcePos.h
@@ -21,6 +21,8 @@
void warning(const char* fmt, ...) const;
void printf(const char* fmt, ...) const;
+ bool operator<(const SourcePos& rhs) const;
+
static bool hasErrors();
static void printErrors(FILE* to);
};
diff --git a/tools/aapt/Symbol.h b/tools/aapt/Symbol.h
new file mode 100644
index 0000000..e157541
--- /dev/null
+++ b/tools/aapt/Symbol.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_SYMBOL_H
+#define AAPT_SYMBOL_H
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+
+#include "ConfigDescription.h"
+#include "SourcePos.h"
+
+/**
+ * A resource symbol, not attached to any configuration or context.
+ */
+struct Symbol {
+ inline Symbol();
+ inline Symbol(const android::String16& p, const android::String16& t, const android::String16& n, uint32_t i);
+ inline android::String8 toString() const;
+ inline bool operator<(const Symbol& rhs) const;
+
+ android::String16 package;
+ android::String16 type;
+ android::String16 name;
+ uint32_t id;
+
+};
+
+/**
+ * A specific defintion of a symbol, defined with a configuration and a definition site.
+ */
+struct SymbolDefinition {
+ inline SymbolDefinition();
+ inline SymbolDefinition(const Symbol& s, const ConfigDescription& c, const SourcePos& src);
+ inline bool operator<(const SymbolDefinition& rhs) const;
+
+ Symbol symbol;
+ ConfigDescription config;
+ SourcePos source;
+};
+
+//
+// Implementations
+//
+
+Symbol::Symbol() {
+}
+
+Symbol::Symbol(const android::String16& p, const android::String16& t, const android::String16& n, uint32_t i)
+ : package(p)
+ , type(t)
+ , name(n)
+ , id(i) {
+}
+
+android::String8 Symbol::toString() const {
+ return android::String8::format("%s:%s/%s (0x%08x)",
+ android::String8(package).string(),
+ android::String8(type).string(),
+ android::String8(name).string(),
+ (int) id);
+}
+
+bool Symbol::operator<(const Symbol& rhs) const {
+ return (package < rhs.package) || (type < rhs.type) || (name < rhs.name) || (id < rhs.id);
+}
+
+SymbolDefinition::SymbolDefinition() {
+}
+
+SymbolDefinition::SymbolDefinition(const Symbol& s, const ConfigDescription& c, const SourcePos& src)
+ : symbol(s)
+ , config(c)
+ , source(src) {
+}
+
+bool SymbolDefinition::operator<(const SymbolDefinition& rhs) const {
+ return (symbol < rhs.symbol) || (config < rhs.config) || (source < rhs.source);
+}
+
+#endif // AAPT_SYMBOL_H
+
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 5bf3470..75198e5 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -286,7 +286,7 @@
* Returns the service set identifier (SSID) of the current 802.11 network.
* If the SSID can be decoded as UTF-8, it will be returned surrounded by double
* quotation marks. Otherwise, it is returned as a string of hex digits. The
- * SSID may be {@code null} if there is no network currently connected.
+ * SSID may be <unknown ssid> if there is no network currently connected.
* @return the SSID
*/
public String getSSID() {