diff options
254 files changed, 7594 insertions, 4782 deletions
diff --git a/api/current.txt b/api/current.txt index 380d641e07db..17f5e5394cd7 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1047,6 +1047,7 @@ package android { field public static final int spinnerStyle = 16842881; // 0x1010081 field public static final int spinnersShown = 16843595; // 0x101034b field public static final int splitMotionEvents = 16843503; // 0x10102ef + field public static final int splitTrack = 16843858; // 0x1010452 field public static final int src = 16843033; // 0x1010119 field public static final int ssp = 16843747; // 0x10103e3 field public static final int sspPattern = 16843749; // 0x10103e5 @@ -7612,7 +7613,7 @@ package android.content { method public long getMaxExecutionDelayMillis(); method public long getMinLatencyMillis(); method public int getNetworkCapabilities(); - method public java.lang.String getServiceClassName(); + method public android.content.ComponentName getService(); method public int getTaskId(); method public boolean isPeriodic(); method public boolean isRequireCharging(); @@ -7627,7 +7628,7 @@ package android.content { } public final class Task.Builder { - ctor public Task.Builder(int, java.lang.Class<android.app.task.TaskService>); + ctor public Task.Builder(int, android.content.ComponentName); method public android.content.Task build(); method public android.content.Task.Builder setBackoffCriteria(long, int); method public android.content.Task.Builder setExtras(android.os.Bundle); @@ -13340,6 +13341,45 @@ package android.media { method public void stop(); } + public final class AudioAttributes { + method public int getContentType(); + method public int getFlags(); + method public java.util.Set<java.lang.String> getTags(); + method public int getUsage(); + field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3 + field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2 + field public static final int CONTENT_TYPE_SONIFICATION = 4; // 0x4 + field public static final int CONTENT_TYPE_SPEECH = 1; // 0x1 + field public static final int CONTENT_TYPE_UNKNOWN = 0; // 0x0 + field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1 + field public static final int USAGE_ALARM = 4; // 0x4 + field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb + field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc + field public static final int USAGE_ASSISTANCE_SONIFICATION = 13; // 0xd + field public static final int USAGE_GAME = 14; // 0xe + field public static final int USAGE_MEDIA = 1; // 0x1 + field public static final int USAGE_NOTIFICATION = 5; // 0x5 + field public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9; // 0x9 + field public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8; // 0x8 + field public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7; // 0x7 + field public static final int USAGE_NOTIFICATION_EVENT = 10; // 0xa + field public static final int USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6; // 0x6 + field public static final int USAGE_UNKNOWN = 0; // 0x0 + field public static final int USAGE_VOICE_COMMUNICATION = 2; // 0x2 + field public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3; // 0x3 + } + + public static class AudioAttributes.Builder { + ctor public AudioAttributes.Builder(); + ctor public AudioAttributes.Builder(android.media.AudioAttributes); + method public android.media.AudioAttributes.Builder addTag(java.lang.String); + method public android.media.AudioAttributes build(); + method public android.media.AudioAttributes.Builder setContentType(int); + method public android.media.AudioAttributes.Builder setFlags(int); + method public android.media.AudioAttributes.Builder setLegacyStreamType(int); + method public android.media.AudioAttributes.Builder setUsage(int); + } + public class AudioFormat { ctor public AudioFormat(); field public static final deprecated int CHANNEL_CONFIGURATION_DEFAULT = 1; // 0x1 @@ -23458,6 +23498,7 @@ package android.provider { method public static boolean putLong(android.content.ContentResolver, java.lang.String, long); method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String); method public static final deprecated void setLocationProviderEnabled(android.content.ContentResolver, java.lang.String, boolean); + field public static final java.lang.String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED = "accessibility_display_inversion_enabled"; field public static final java.lang.String ACCESSIBILITY_ENABLED = "accessibility_enabled"; field public static final java.lang.String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password"; field public static final deprecated java.lang.String ADB_ENABLED = "adb_enabled"; @@ -25182,16 +25223,24 @@ package android.service.notification { method public final deprecated void cancelNotification(java.lang.String, java.lang.String, int); method public final void cancelNotification(java.lang.String); method public final void cancelNotifications(java.lang.String[]); - method public java.lang.String[] getActiveNotificationKeys(); method public android.service.notification.StatusBarNotification[] getActiveNotifications(); method public android.service.notification.StatusBarNotification[] getActiveNotifications(java.lang.String[]); + method public java.lang.String[] getOrderedNotificationKeys(); method public android.os.IBinder onBind(android.content.Intent); method public void onListenerConnected(java.lang.String[]); + method public void onNotificationOrderUpdate(); method public abstract void onNotificationPosted(android.service.notification.StatusBarNotification); method public abstract void onNotificationRemoved(android.service.notification.StatusBarNotification); field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService"; } + public class NotificationOrderUpdate implements android.os.Parcelable { + ctor public NotificationOrderUpdate(android.os.Parcel); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + public class StatusBarNotification implements android.os.Parcelable { ctor public StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long); ctor public StatusBarNotification(android.os.Parcel); @@ -29130,11 +29179,11 @@ package android.tv { } public final class TvInputManager { - method public void createSession(android.content.ComponentName, android.tv.TvInputManager.SessionCreateCallback, android.os.Handler); - method public boolean getAvailability(android.content.ComponentName); + method public void createSession(java.lang.String, android.tv.TvInputManager.SessionCallback, android.os.Handler); + method public boolean getAvailability(java.lang.String); method public java.util.List<android.tv.TvInputInfo> getTvInputList(); - method public void registerListener(android.content.ComponentName, android.tv.TvInputManager.TvInputListener, android.os.Handler); - method public void unregisterListener(android.content.ComponentName, android.tv.TvInputManager.TvInputListener); + method public void registerListener(java.lang.String, android.tv.TvInputManager.TvInputListener, android.os.Handler); + method public void unregisterListener(java.lang.String, android.tv.TvInputManager.TvInputListener); } public static final class TvInputManager.Session { @@ -29143,13 +29192,15 @@ package android.tv { method public void tune(android.net.Uri); } - public static abstract interface TvInputManager.SessionCreateCallback { - method public abstract void onSessionCreated(android.tv.TvInputManager.Session); + public static abstract class TvInputManager.SessionCallback { + ctor public TvInputManager.SessionCallback(); + method public void onSessionCreated(android.tv.TvInputManager.Session); + method public void onSessionReleased(android.tv.TvInputManager.Session); } public static abstract class TvInputManager.TvInputListener { ctor public TvInputManager.TvInputListener(); - method public void onAvailabilityChanged(android.content.ComponentName, boolean); + method public void onAvailabilityChanged(java.lang.String, boolean); } public abstract class TvInputService extends android.app.Service { @@ -29181,7 +29232,7 @@ package android.tv { ctor public TvView(android.content.Context); ctor public TvView(android.content.Context, android.util.AttributeSet); ctor public TvView(android.content.Context, android.util.AttributeSet, int); - method public void bindTvInput(android.content.ComponentName, android.tv.TvInputManager.SessionCreateCallback); + method public void bindTvInput(java.lang.String, android.tv.TvInputManager.SessionCallback); method public boolean dispatchUnhandledInputEvent(android.view.InputEvent); method public boolean onUnhandledInputEvent(android.view.InputEvent); method public void setOnUnhandledInputEventListener(android.tv.TvView.OnUnhandledInputEventListener); @@ -34347,9 +34398,11 @@ package android.widget { ctor public AbsSeekBar(android.content.Context, android.util.AttributeSet, int); ctor public AbsSeekBar(android.content.Context, android.util.AttributeSet, int, int); method public int getKeyProgressIncrement(); + method public boolean getSplitTrack(); method public android.graphics.drawable.Drawable getThumb(); method public int getThumbOffset(); method public void setKeyProgressIncrement(int); + method public void setSplitTrack(boolean); method public void setThumb(android.graphics.drawable.Drawable); method public void setThumbOffset(int); } @@ -36154,6 +36207,7 @@ package android.widget { ctor public Switch(android.content.Context, android.util.AttributeSet); ctor public Switch(android.content.Context, android.util.AttributeSet, int); ctor public Switch(android.content.Context, android.util.AttributeSet, int, int); + method public boolean getSplitTrack(); method public int getSwitchMinWidth(); method public int getSwitchPadding(); method public java.lang.CharSequence getTextOff(); @@ -36162,6 +36216,7 @@ package android.widget { method public int getThumbTextPadding(); method public android.graphics.drawable.Drawable getTrackDrawable(); method public void onMeasure(int, int); + method public void setSplitTrack(boolean); method public void setSwitchMinWidth(int); method public void setSwitchPadding(int); method public void setSwitchTextAppearance(android.content.Context, int); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 161cb7655276..4cf30ae60d59 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -47,6 +47,7 @@ import android.hardware.display.DisplayManagerGlobal; import android.net.IConnectivityManager; import android.net.Proxy; import android.net.ProxyInfo; +import android.net.Uri; import android.opengl.GLUtils; import android.os.AsyncTask; import android.os.Binder; @@ -839,7 +840,7 @@ public final class ActivityThread { InetAddress.clearDnsCache(); } - public void setHttpProxy(String host, String port, String exclList, String pacFileUrl) { + public void setHttpProxy(String host, String port, String exclList, Uri pacFileUrl) { Proxy.setHttpProxySystemProperty(host, port, exclList, pacFileUrl); } diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index e7902a9d9825..0029efa2c56b 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -25,6 +25,7 @@ import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; +import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.Debug; @@ -339,7 +340,7 @@ public abstract class ApplicationThreadNative extends Binder final String proxy = data.readString(); final String port = data.readString(); final String exclList = data.readString(); - final String pacFileUrl = data.readString(); + final Uri pacFileUrl = Uri.CREATOR.createFromParcel(data); setHttpProxy(proxy, port, exclList, pacFileUrl); return true; } @@ -1008,13 +1009,13 @@ class ApplicationThreadProxy implements IApplicationThread { } public void setHttpProxy(String proxy, String port, String exclList, - String pacFileUrl) throws RemoteException { + Uri pacFileUrl) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); data.writeString(proxy); data.writeString(port); data.writeString(exclList); - data.writeString(pacFileUrl); + pacFileUrl.writeToParcel(data, 0); mRemote.transact(SET_HTTP_PROXY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); } diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index a8320342d45d..d5fbd0bd61d6 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -25,6 +25,7 @@ import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; +import android.net.Uri; import android.os.Bundle; import android.os.Debug; import android.os.ParcelFileDescriptor; @@ -106,7 +107,7 @@ public interface IApplicationThread extends IInterface { void updateTimeZone() throws RemoteException; void clearDnsCache() throws RemoteException; void setHttpProxy(String proxy, String port, String exclList, - String pacFileUrl) throws RemoteException; + Uri pacFileUrl) throws RemoteException; void processInBackground() throws RemoteException; void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args) throws RemoteException; diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index bba6caf8b05f..76a6a8e71fd7 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -659,8 +659,8 @@ public class Notification implements Parcelable /** * @hide - * Extra added by NotificationManagerService to indicate whether a NotificationScorer - * modified the Notifications's score. + * Extra added by NotificationManagerService to indicate whether + * the Notifications's score has been modified. */ public static final String EXTRA_SCORE_MODIFIED = "android.scoreModified"; diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 58049fd11914..888444644c75 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2068,46 +2068,49 @@ public class DevicePolicyManager { } /** - * Called by a profile owner to disable account management for a specific type of account. - * - * <p>The calling device admin must be a profile owner. If it is not, a - * security exception will be thrown. + * Called by profile or device owner to re-enable system apps by intent that were disabled + * by default when the managed profile was created. This should only be called from a profile + * or device owner running within a managed profile. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. - * @param accountType For which account management is disabled or enabled. - * @param disabled The boolean indicating that account management will be disabled (true) or - * enabled (false). + * @param intent An intent matching the app(s) to be installed. All apps that resolve for this + * intent will be re-enabled in the current profile. + * @return int The number of activities that matched the intent and were installed. */ - public void setAccountManagementDisabled(ComponentName admin, String accountType, - boolean disabled) { + public int enableSystemApp(ComponentName admin, Intent intent) { if (mService != null) { try { - mService.setAccountManagementDisabled(admin, accountType, disabled); + return mService.enableSystemAppWithIntent(admin, intent); } catch (RemoteException e) { - Log.w(TAG, "Failed talking with device policy service", e); + Log.w(TAG, "Failed to install packages matching filter: " + intent); } } + return 0; } /** - * Called by profile or device owner to re-enable system apps by intent that were disabled - * by default when the managed profile was created. This should only be called from a profile - * or device owner running within a managed profile. + * Called by a profile owner to disable account management for a specific type of account. + * + * <p>The calling device admin must be a profile owner. If it is not, a + * security exception will be thrown. + * + * <p>When account management is disabled for an account type, adding or removing an account + * of that type will not be possible. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. - * @param intent An intent matching the app(s) to be installed. All apps that resolve for this - * intent will be re-enabled in the current profile. - * @return int The number of activities that matched the intent and were installed. + * @param accountType For which account management is disabled or enabled. + * @param disabled The boolean indicating that account management will be disabled (true) or + * enabled (false). */ - public int enableSystemApp(ComponentName admin, Intent intent) { + public void setAccountManagementDisabled(ComponentName admin, String accountType, + boolean disabled) { if (mService != null) { try { - return mService.enableSystemAppWithIntent(admin, intent); + mService.setAccountManagementDisabled(admin, accountType, disabled); } catch (RemoteException e) { - Log.w(TAG, "Failed to install packages matching filter: " + intent); + Log.w(TAG, "Failed talking with device policy service", e); } } - return 0; } /** diff --git a/core/java/android/app/task/TaskParams.java b/core/java/android/app/task/TaskParams.java index e2eafd85da6c..035108274ed0 100644 --- a/core/java/android/app/task/TaskParams.java +++ b/core/java/android/app/task/TaskParams.java @@ -29,7 +29,14 @@ public class TaskParams implements Parcelable { private final int taskId; private final Bundle extras; - private final IBinder mCallback; + private final IBinder callback; + + /** @hide */ + public TaskParams(int taskId, Bundle extras, IBinder callback) { + this.taskId = taskId; + this.extras = extras; + this.callback = callback; + } /** * @return The unique id of this task, specified at creation time. @@ -47,17 +54,15 @@ public class TaskParams implements Parcelable { return extras; } - /** - * @hide - */ + /** @hide */ public ITaskCallback getCallback() { - return ITaskCallback.Stub.asInterface(mCallback); + return ITaskCallback.Stub.asInterface(callback); } private TaskParams(Parcel in) { taskId = in.readInt(); extras = in.readBundle(); - mCallback = in.readStrongBinder(); + callback = in.readStrongBinder(); } @Override @@ -69,7 +74,7 @@ public class TaskParams implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(taskId); dest.writeBundle(extras); - dest.writeStrongBinder(mCallback); + dest.writeStrongBinder(callback); } public static final Creator<TaskParams> CREATOR = new Creator<TaskParams>() { diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java index a0b603e78647..f0c82997aa96 100644 --- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -20,7 +20,7 @@ import android.net.BaseNetworkStateTracker; import android.content.Context; import android.net.ConnectivityManager; import android.net.DhcpResults; -import android.net.LinkCapabilities; +import android.net.NetworkCapabilities; import android.net.LinkProperties; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; @@ -75,7 +75,7 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { private BluetoothTetheringDataTracker() { mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_BLUETOOTH, 0, NETWORKTYPE, ""); mLinkProperties = new LinkProperties(); - mLinkCapabilities = new LinkCapabilities(); + mNetworkCapabilities = new NetworkCapabilities(); mNetworkInfo.setIsAvailable(false); setTeardownRequested(false); @@ -242,16 +242,6 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { } } - /** - * A capability is an Integer/String pair, the capabilities - * are defined in the class LinkSocket#Key. - * - * @return a copy of this connections capabilities, may be empty but never null. - */ - public LinkCapabilities getLinkCapabilities() { - return new LinkCapabilities(mLinkCapabilities); - } - /** * Fetch default gateway address for the network */ diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java index d4f7f06b008d..6b4404db2cc0 100644 --- a/core/java/android/content/SharedPreferences.java +++ b/core/java/android/content/SharedPreferences.java @@ -82,7 +82,7 @@ public interface SharedPreferences { /** * Set a set of String values in the preferences editor, to be written - * back once {@link #commit} is called. + * back once {@link #commit} or {@link #apply} is called. * * @param key The name of the preference to modify. * @param values The set of new values for the preference. Passing {@code null} diff --git a/core/java/android/content/Task.java b/core/java/android/content/Task.java index ed5ed88407bb..407880f3f9c8 100644 --- a/core/java/android/content/Task.java +++ b/core/java/android/content/Task.java @@ -42,6 +42,20 @@ public class Task implements Parcelable { public final int EXPONENTIAL = 1; } + private final int taskId; + // TODO: Change this to use PersistableBundle when that lands in master. + private final Bundle extras; + private final ComponentName service; + private final boolean requireCharging; + private final boolean requireDeviceIdle; + private final int networkCapabilities; + private final long minLatencyMillis; + private final long maxExecutionDelayMillis; + private final boolean isPeriodic; + private final long intervalMillis; + private final long initialBackoffMillis; + private final int backoffPolicy; + /** * Unique task id associated with this class. This is assigned to your task by the scheduler. */ @@ -59,8 +73,8 @@ public class Task implements Parcelable { /** * Name of the service endpoint that will be called back into by the TaskManager. */ - public String getServiceClassName() { - return serviceClassName; + public ComponentName getService() { + return service; } /** @@ -132,24 +146,10 @@ public class Task implements Parcelable { return backoffPolicy; } - private final int taskId; - // TODO: Change this to use PersistableBundle when that lands in master. - private final Bundle extras; - private final String serviceClassName; - private final boolean requireCharging; - private final boolean requireDeviceIdle; - private final int networkCapabilities; - private final long minLatencyMillis; - private final long maxExecutionDelayMillis; - private final boolean isPeriodic; - private final long intervalMillis; - private final long initialBackoffMillis; - private final int backoffPolicy; - private Task(Parcel in) { taskId = in.readInt(); extras = in.readBundle(); - serviceClassName = in.readString(); + service = ComponentName.readFromParcel(in); requireCharging = in.readInt() == 1; requireDeviceIdle = in.readInt() == 1; networkCapabilities = in.readInt(); @@ -164,7 +164,7 @@ public class Task implements Parcelable { private Task(Task.Builder b) { taskId = b.mTaskId; extras = new Bundle(b.mExtras); - serviceClassName = b.mTaskServiceClassName; + service = b.mTaskService; requireCharging = b.mRequiresCharging; requireDeviceIdle = b.mRequiresDeviceIdle; networkCapabilities = b.mNetworkCapabilities; @@ -185,7 +185,7 @@ public class Task implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeInt(taskId); out.writeBundle(extras); - out.writeString(serviceClassName); + ComponentName.writeToParcel(service, out); out.writeInt(requireCharging ? 1 : 0); out.writeInt(requireDeviceIdle ? 1 : 0); out.writeInt(networkCapabilities); @@ -215,7 +215,7 @@ public class Task implements Parcelable { public final class Builder { private int mTaskId; private Bundle mExtras; - private String mTaskServiceClassName; + private ComponentName mTaskService; // Requirements. private boolean mRequiresCharging; private boolean mRequiresDeviceIdle; @@ -236,11 +236,11 @@ public class Task implements Parcelable { * @param taskId Application-provided id for this task. Subsequent calls to cancel, or * tasks created with the same taskId, will update the pre-existing task with * the same id. - * @param cls The endpoint that you implement that will receive the callback from the + * @param taskService The endpoint that you implement that will receive the callback from the * TaskManager. */ - public Builder(int taskId, Class<TaskService> cls) { - mTaskServiceClassName = cls.getClass().getName(); + public Builder(int taskId, ComponentName taskService) { + mTaskService = taskService; mTaskId = taskId; } @@ -296,7 +296,7 @@ public class Task implements Parcelable { * period. You have no control over when within this interval this task will be executed, * only the guarantee that it will be executed at most once within this interval. * A periodic task will be repeated until the phone is turned off, however it will only be - * persisted if the client app has declared the + * persisted beyond boot if the client app has declared the * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission. You can schedule * periodic tasks without this permission, they simply will cease to exist after the phone * restarts. diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index 4bea9eeac225..86208fc96229 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -397,6 +397,32 @@ public final class Sensor { public static final String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate"; /** + * A constant describing a wake gesture sensor. + * <p> + * Wake gesture sensors enable waking up the device based on a device specific motion. + * <p> + * When this sensor triggers, the device behaves as if the power button was pressed, turning the + * screen on. This behavior (turning on the screen when this sensor triggers) might be + * deactivated by the user in the device settings. Changes in settings do not impact the + * behavior of the sensor: only whether the framework turns the screen on when it triggers. + * <p> + * The actual gesture to be detected is not specified, and can be chosen by the manufacturer of + * the device. This sensor must be low power, as it is likely to be activated 24/7. + * Values of events created by this sensors should not be used. + * + * @hide This sensor is expected to only be used by the power manager + */ + public static final int TYPE_WAKE_GESTURE = 42; + + /** + * A constant string describing a wake gesture sensor. + * + * @hide This sensor is expected to only be used by the power manager + * @see #TYPE_WAKE_GESTURE + */ + public static final String STRING_TYPE_WAKE_GESTURE = "android.sensor.wake_gesture"; + + /** * A constant describing all sensor types. */ public static final int TYPE_ALL = -1; diff --git a/core/java/android/net/BaseNetworkStateTracker.java b/core/java/android/net/BaseNetworkStateTracker.java index 804f8eecbbdc..79db389dff99 100644 --- a/core/java/android/net/BaseNetworkStateTracker.java +++ b/core/java/android/net/BaseNetworkStateTracker.java @@ -44,7 +44,8 @@ public abstract class BaseNetworkStateTracker implements NetworkStateTracker { protected NetworkInfo mNetworkInfo; protected LinkProperties mLinkProperties; - protected LinkCapabilities mLinkCapabilities; + protected NetworkCapabilities mNetworkCapabilities; + protected Network mNetwork = new Network(ConnectivityManager.INVALID_NET_ID); private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); @@ -54,7 +55,7 @@ public abstract class BaseNetworkStateTracker implements NetworkStateTracker { mNetworkInfo = new NetworkInfo( networkType, -1, ConnectivityManager.getNetworkTypeName(networkType), null); mLinkProperties = new LinkProperties(); - mLinkCapabilities = new LinkCapabilities(); + mNetworkCapabilities = new NetworkCapabilities(); } protected BaseNetworkStateTracker() { @@ -98,8 +99,8 @@ public abstract class BaseNetworkStateTracker implements NetworkStateTracker { } @Override - public LinkCapabilities getLinkCapabilities() { - return new LinkCapabilities(mLinkCapabilities); + public NetworkCapabilities getNetworkCapabilities() { + return new NetworkCapabilities(mNetworkCapabilities); } @Override @@ -201,4 +202,14 @@ public abstract class BaseNetworkStateTracker implements NetworkStateTracker { public void stopSampling(SamplingDataTracker.SamplingSnapshot s) { // nothing to do } + + @Override + public void setNetId(int netId) { + mNetwork = new Network(netId); + } + + @Override + public Network getNetwork() { + return mNetwork; + } } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 30d70434a223..3e0025043a07 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -408,6 +408,11 @@ public class ConnectivityManager { */ public static final int CONNECTIVITY_CHANGE_DELAY_DEFAULT = 3000; + /** + * @hide + */ + public final static int INVALID_NET_ID = 0; + private final IConnectivityManager mService; private final String mPackageName; diff --git a/core/java/android/net/DummyDataStateTracker.java b/core/java/android/net/DummyDataStateTracker.java index a5d059e54790..eff9f9f323b6 100644 --- a/core/java/android/net/DummyDataStateTracker.java +++ b/core/java/android/net/DummyDataStateTracker.java @@ -190,13 +190,6 @@ public class DummyDataStateTracker extends BaseNetworkStateTracker { return new LinkProperties(mLinkProperties); } - /** - * @see android.net.NetworkStateTracker#getLinkCapabilities() - */ - public LinkCapabilities getLinkCapabilities() { - return new LinkCapabilities(mLinkCapabilities); - } - public void setDependencyMet(boolean met) { // not supported on this network } diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java index 10b5d0ba3329..c1afc9bad0d6 100644 --- a/core/java/android/net/EthernetDataTracker.java +++ b/core/java/android/net/EthernetDataTracker.java @@ -103,7 +103,7 @@ public class EthernetDataTracker extends BaseNetworkStateTracker { private EthernetDataTracker() { mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORKTYPE, ""); mLinkProperties = new LinkProperties(); - mLinkCapabilities = new LinkCapabilities(); + mNetworkCapabilities = new NetworkCapabilities(); } private void interfaceUpdated() { @@ -372,16 +372,6 @@ public class EthernetDataTracker extends BaseNetworkStateTracker { return new LinkProperties(mLinkProperties); } - /** - * A capability is an Integer/String pair, the capabilities - * are defined in the class LinkSocket#Key. - * - * @return a copy of this connections capabilities, may be empty but never null. - */ - public LinkCapabilities getLinkCapabilities() { - return new LinkCapabilities(mLinkCapabilities); - } - /** * Fetch default gateway address for the network */ diff --git a/core/java/android/net/LinkCapabilities.java b/core/java/android/net/LinkCapabilities.java deleted file mode 100644 index fb444eaeb5f8..000000000000 --- a/core/java/android/net/LinkCapabilities.java +++ /dev/null @@ -1,362 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.os.Parcelable; -import android.os.Parcel; -import android.util.Log; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -/** - * A class representing the capabilities of a link - * - * @hide - */ -public class LinkCapabilities implements Parcelable { - private static final String TAG = "LinkCapabilities"; - private static final boolean DBG = false; - - /** The Map of Keys to Values */ - private HashMap<Integer, String> mCapabilities; - - - /** - * The set of keys defined for a links capabilities. - * - * Keys starting with RW are read + write, i.e. the application - * can request for a certain requirement corresponding to that key. - * Keys starting with RO are read only, i.e. the the application - * can read the value of that key from the socket but cannot request - * a corresponding requirement. - * - * TODO: Provide a documentation technique for concisely and precisely - * define the syntax for each value string associated with a key. - */ - public static final class Key { - /** No constructor */ - private Key() {} - - /** - * An integer representing the network type. - * @see ConnectivityManager - */ - public final static int RO_NETWORK_TYPE = 1; - - /** - * Desired minimum forward link (download) bandwidth for the - * in kilobits per second (kbps). Values should be strings such - * "50", "100", "1500", etc. - */ - public final static int RW_DESIRED_FWD_BW = 2; - - /** - * Required minimum forward link (download) bandwidth, in - * per second (kbps), below which the socket cannot function. - * Values should be strings such as "50", "100", "1500", etc. - */ - public final static int RW_REQUIRED_FWD_BW = 3; - - /** - * Available forward link (download) bandwidth for the socket. - * This value is in kilobits per second (kbps). - * Values will be strings such as "50", "100", "1500", etc. - */ - public final static int RO_AVAILABLE_FWD_BW = 4; - - /** - * Desired minimum reverse link (upload) bandwidth for the socket - * in kilobits per second (kbps). - * Values should be strings such as "50", "100", "1500", etc. - * <p> - * This key is set via the needs map. - */ - public final static int RW_DESIRED_REV_BW = 5; - - /** - * Required minimum reverse link (upload) bandwidth, in kilobits - * per second (kbps), below which the socket cannot function. - * If a rate is not specified, the default rate of kbps will be - * Values should be strings such as "50", "100", "1500", etc. - */ - public final static int RW_REQUIRED_REV_BW = 6; - - /** - * Available reverse link (upload) bandwidth for the socket. - * This value is in kilobits per second (kbps). - * Values will be strings such as "50", "100", "1500", etc. - */ - public final static int RO_AVAILABLE_REV_BW = 7; - - /** - * Maximum latency for the socket, in milliseconds, above which - * socket cannot function. - * Values should be strings such as "50", "300", "500", etc. - */ - public final static int RW_MAX_ALLOWED_LATENCY = 8; - - /** - * Interface that the socket is bound to. This can be a virtual - * interface (e.g. VPN or Mobile IP) or a physical interface - * (e.g. wlan0 or rmnet0). - * Values will be strings such as "wlan0", "rmnet0" - */ - public final static int RO_BOUND_INTERFACE = 9; - - /** - * Physical interface that the socket is routed on. - * This can be different from BOUND_INTERFACE in cases such as - * VPN or Mobile IP. The physical interface may change over time - * if seamless mobility is supported. - * Values will be strings such as "wlan0", "rmnet0" - */ - public final static int RO_PHYSICAL_INTERFACE = 10; - } - - /** - * Role informs the LinkSocket about the data usage patterns of your - * application. - * <P> - * {@code Role.DEFAULT} is the default role, and is used whenever - * a role isn't set. - */ - public static final class Role { - /** No constructor */ - private Role() {} - - // examples only, discuss which roles should be defined, and then - // code these to match - - /** Default Role */ - public static final String DEFAULT = "default"; - /** Bulk down load */ - public static final String BULK_DOWNLOAD = "bulk.download"; - /** Bulk upload */ - public static final String BULK_UPLOAD = "bulk.upload"; - - /** VoIP Application at 24kbps */ - public static final String VOIP_24KBPS = "voip.24k"; - /** VoIP Application at 32kbps */ - public static final String VOIP_32KBPS = "voip.32k"; - - /** Video Streaming at 480p */ - public static final String VIDEO_STREAMING_480P = "video.streaming.480p"; - /** Video Streaming at 720p */ - public static final String VIDEO_STREAMING_720I = "video.streaming.720i"; - - /** Video Chat Application at 360p */ - public static final String VIDEO_CHAT_360P = "video.chat.360p"; - /** Video Chat Application at 480p */ - public static final String VIDEO_CHAT_480P = "video.chat.480i"; - } - - /** - * Constructor - */ - public LinkCapabilities() { - mCapabilities = new HashMap<Integer, String>(); - } - - /** - * Copy constructor. - * - * @param source - */ - public LinkCapabilities(LinkCapabilities source) { - if (source != null) { - mCapabilities = new HashMap<Integer, String>(source.mCapabilities); - } else { - mCapabilities = new HashMap<Integer, String>(); - } - } - - /** - * Create the {@code LinkCapabilities} with values depending on role type. - * @param applicationRole a {@code LinkSocket.Role} - * @return the {@code LinkCapabilities} associated with the applicationRole, empty if none - */ - public static LinkCapabilities createNeedsMap(String applicationRole) { - if (DBG) log("createNeededCapabilities(applicationRole) EX"); - return new LinkCapabilities(); - } - - /** - * Remove all capabilities - */ - public void clear() { - mCapabilities.clear(); - } - - /** - * Returns whether this map is empty. - */ - public boolean isEmpty() { - return mCapabilities.isEmpty(); - } - - /** - * Returns the number of elements in this map. - * - * @return the number of elements in this map. - */ - public int size() { - return mCapabilities.size(); - } - - /** - * Given the key return the capability string - * - * @param key - * @return the capability string - */ - public String get(int key) { - return mCapabilities.get(key); - } - - /** - * Store the key/value capability pair - * - * @param key - * @param value - */ - public void put(int key, String value) { - mCapabilities.put(key, value); - } - - /** - * Returns whether this map contains the specified key. - * - * @param key to search for. - * @return {@code true} if this map contains the specified key, - * {@code false} otherwise. - */ - public boolean containsKey(int key) { - return mCapabilities.containsKey(key); - } - - /** - * Returns whether this map contains the specified value. - * - * @param value to search for. - * @return {@code true} if this map contains the specified value, - * {@code false} otherwise. - */ - public boolean containsValue(String value) { - return mCapabilities.containsValue(value); - } - - /** - * Returns a set containing all of the mappings in this map. Each mapping is - * an instance of {@link Map.Entry}. As the set is backed by this map, - * changes in one will be reflected in the other. - * - * @return a set of the mappings. - */ - public Set<Entry<Integer, String>> entrySet() { - return mCapabilities.entrySet(); - } - - /** - * @return the set of the keys. - */ - public Set<Integer> keySet() { - return mCapabilities.keySet(); - } - - /** - * @return the set of values - */ - public Collection<String> values() { - return mCapabilities.values(); - } - - /** - * Implement the Parcelable interface - * @hide - */ - public int describeContents() { - return 0; - } - - /** - * Convert to string for debugging - */ - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("{"); - boolean firstTime = true; - for (Entry<Integer, String> entry : mCapabilities.entrySet()) { - if (firstTime) { - firstTime = false; - } else { - sb.append(","); - } - sb.append(entry.getKey()); - sb.append(":\""); - sb.append(entry.getValue()); - sb.append("\""); - } - sb.append("}"); - return sb.toString(); - } - - /** - * Implement the Parcelable interface. - * @hide - */ - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mCapabilities.size()); - for (Entry<Integer, String> entry : mCapabilities.entrySet()) { - dest.writeInt(entry.getKey().intValue()); - dest.writeString(entry.getValue()); - } - } - - /** - * Implement the Parcelable interface. - * @hide - */ - public static final Creator<LinkCapabilities> CREATOR = - new Creator<LinkCapabilities>() { - public LinkCapabilities createFromParcel(Parcel in) { - LinkCapabilities capabilities = new LinkCapabilities(); - int size = in.readInt(); - while (size-- != 0) { - int key = in.readInt(); - String value = in.readString(); - capabilities.mCapabilities.put(key, value); - } - return capabilities; - } - - public LinkCapabilities[] newArray(int size) { - return new LinkCapabilities[size]; - } - }; - - /** - * Debug logging - */ - protected static void log(String s) { - Log.d(TAG, s); - } -} diff --git a/core/java/android/net/LinkSocket.java b/core/java/android/net/LinkSocket.java deleted file mode 100644 index 5aa64512dc82..000000000000 --- a/core/java/android/net/LinkSocket.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.net.LinkCapabilities; -import android.net.LinkProperties; -import android.net.LinkSocketNotifier; - -import android.util.Log; - -import java.io.IOException; -import java.net.Socket; -import java.net.SocketAddress; -import java.net.SocketTimeoutException; -import java.net.UnknownHostException; -import java.util.HashSet; -import java.util.Set; - -/** @hide */ -public class LinkSocket extends Socket { - private final static String TAG = "LinkSocket"; - private final static boolean DBG = true; - - /** - * Default constructor - */ - public LinkSocket() { - if (DBG) log("LinkSocket() EX"); - } - - /** - * Creates a new unconnected socket. - * @param notifier a reference to a class that implements {@code LinkSocketNotifier} - */ - public LinkSocket(LinkSocketNotifier notifier) { - if (DBG) log("LinkSocket(notifier) EX"); - } - - /** - * Creates a new unconnected socket usign the given proxy type. - * @param notifier a reference to a class that implements {@code LinkSocketNotifier} - * @param proxy the specified proxy for this socket - * @throws IllegalArgumentException if the argument proxy is null or of an invalid type. - * @throws SecurityException if a security manager exists and it denies the permission - * to connect to the given proxy. - */ - public LinkSocket(LinkSocketNotifier notifier, Proxy proxy) { - if (DBG) log("LinkSocket(notifier, proxy) EX"); - } - - /** - * @return the {@code LinkProperties} for the socket - */ - public LinkProperties getLinkProperties() { - if (DBG) log("LinkProperties() EX"); - return new LinkProperties(); - } - - /** - * Set the {@code LinkCapabilies} needed for this socket. If the socket is already connected - * or is a duplicate socket the request is ignored and {@code false} will - * be returned. A needs map can be created via the {@code createNeedsMap} static - * method. - * @param needs the needs of the socket - * @return {@code true} if needs are successfully set, {@code false} otherwise - */ - public boolean setNeededCapabilities(LinkCapabilities needs) { - if (DBG) log("setNeeds() EX"); - return false; - } - - /** - * @return the LinkCapabilites set by setNeededCapabilities, empty if none has been set - */ - public LinkCapabilities getNeededCapabilities() { - if (DBG) log("getNeeds() EX"); - return null; - } - - /** - * @return all of the {@code LinkCapabilities} of the link used by this socket - */ - public LinkCapabilities getCapabilities() { - if (DBG) log("getCapabilities() EX"); - return null; - } - - /** - * Returns this LinkSockets set of capabilities, filtered according to - * the given {@code Set}. Capabilities in the Set but not available from - * the link will not be reported in the results. Capabilities of the link - * but not listed in the Set will also not be reported in the results. - * @param capabilities {@code Set} of capabilities requested - * @return the filtered {@code LinkCapabilities} of this LinkSocket, may be empty - */ - public LinkCapabilities getCapabilities(Set<Integer> capabilities) { - if (DBG) log("getCapabilities(capabilities) EX"); - return new LinkCapabilities(); - } - - /** - * Provide the set of capabilities the application is interested in tracking - * for this LinkSocket. - * @param capabilities a {@code Set} of capabilities to track - */ - public void setTrackedCapabilities(Set<Integer> capabilities) { - if (DBG) log("setTrackedCapabilities(capabilities) EX"); - } - - /** - * @return the {@code LinkCapabilities} that are tracked, empty if none has been set. - */ - public Set<Integer> getTrackedCapabilities() { - if (DBG) log("getTrackedCapabilities(capabilities) EX"); - return new HashSet<Integer>(); - } - - /** - * Connects this socket to the given remote host address and port specified - * by dstName and dstPort. - * @param dstName the address of the remote host to connect to - * @param dstPort the port to connect to on the remote host - * @param timeout the timeout value in milliseconds or 0 for infinite timeout - * @throws UnknownHostException if the given dstName is invalid - * @throws IOException if the socket is already connected or an error occurs - * while connecting - * @throws SocketTimeoutException if the timeout fires - */ - public void connect(String dstName, int dstPort, int timeout) - throws UnknownHostException, IOException, SocketTimeoutException { - if (DBG) log("connect(dstName, dstPort, timeout) EX"); - } - - /** - * Connects this socket to the given remote host address and port specified - * by dstName and dstPort. - * @param dstName the address of the remote host to connect to - * @param dstPort the port to connect to on the remote host - * @throws UnknownHostException if the given dstName is invalid - * @throws IOException if the socket is already connected or an error occurs - * while connecting - */ - public void connect(String dstName, int dstPort) - throws UnknownHostException, IOException { - if (DBG) log("connect(dstName, dstPort, timeout) EX"); - } - - /** - * Connects this socket to the given remote host address and port specified - * by the SocketAddress with the specified timeout. - * @deprecated Use {@code connect(String dstName, int dstPort, int timeout)} - * instead. Using this method may result in reduced functionality. - * @param remoteAddr the address and port of the remote host to connect to - * @throws IllegalArgumentException if the given SocketAddress is invalid - * @throws IOException if the socket is already connected or an error occurs - * while connecting - * @throws SocketTimeoutException if the timeout expires - */ - @Override - @Deprecated - public void connect(SocketAddress remoteAddr, int timeout) - throws IOException, SocketTimeoutException { - if (DBG) log("connect(remoteAddr, timeout) EX DEPRECATED"); - } - - /** - * Connects this socket to the given remote host address and port specified - * by the SocketAddress. - * TODO add comment on all these that the network selection happens during connect - * and may take 30 seconds - * @deprecated Use {@code connect(String dstName, int dstPort)} - * Using this method may result in reduced functionality. - * @param remoteAddr the address and port of the remote host to connect to. - * @throws IllegalArgumentException if the SocketAddress is invalid or not supported. - * @throws IOException if the socket is already connected or an error occurs - * while connecting - */ - @Override - @Deprecated - public void connect(SocketAddress remoteAddr) throws IOException { - if (DBG) log("connect(remoteAddr) EX DEPRECATED"); - } - - /** - * Connect a duplicate socket socket to the same remote host address and port - * as the original with a timeout parameter. - * @param timeout the timeout value in milliseconds or 0 for infinite timeout - * @throws IOException if the socket is already connected or an error occurs - * while connecting - */ - public void connect(int timeout) throws IOException { - if (DBG) log("connect(timeout) EX"); - } - - /** - * Connect a duplicate socket socket to the same remote host address and port - * as the original. - * @throws IOException if the socket is already connected or an error occurs - * while connecting - */ - public void connect() throws IOException { - if (DBG) log("connect() EX"); - } - - /** - * Closes the socket. It is not possible to reconnect or rebind to this - * socket thereafter which means a new socket instance has to be created. - * @throws IOException if an error occurs while closing the socket - */ - @Override - public synchronized void close() throws IOException { - if (DBG) log("close() EX"); - } - - /** - * Request that a new LinkSocket be created using a different radio - * (such as WiFi or 3G) than the current LinkSocket. If a different - * radio is available a call back will be made via {@code onBetterLinkAvail}. - * If unable to find a better radio, application will be notified via - * {@code onNewLinkUnavailable} - * @see LinkSocketNotifier#onBetterLinkAvailable(LinkSocket, LinkSocket) - * @param linkRequestReason reason for requesting a new link. - */ - public void requestNewLink(LinkRequestReason linkRequestReason) { - if (DBG) log("requestNewLink(linkRequestReason) EX"); - } - - /** - * @deprecated LinkSocket will automatically pick the optimum interface - * to bind to - * @param localAddr the specific address and port on the local machine - * to bind to - * @throws IOException always as this method is deprecated for LinkSocket - */ - @Override - @Deprecated - public void bind(SocketAddress localAddr) throws UnsupportedOperationException { - if (DBG) log("bind(localAddr) EX throws IOException"); - throw new UnsupportedOperationException("bind is deprecated for LinkSocket"); - } - - /** - * Reason codes an application can specify when requesting for a new link. - * TODO: need better documentation - */ - public static final class LinkRequestReason { - /** No constructor */ - private LinkRequestReason() {} - - /** This link is working properly */ - public static final int LINK_PROBLEM_NONE = 0; - /** This link has an unknown issue */ - public static final int LINK_PROBLEM_UNKNOWN = 1; - } - - /** - * Debug logging - */ - protected static void log(String s) { - Log.d(TAG, s); - } -} diff --git a/core/java/android/net/LinkSocketNotifier.java b/core/java/android/net/LinkSocketNotifier.java deleted file mode 100644 index e2429d8987b8..000000000000 --- a/core/java/android/net/LinkSocketNotifier.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -/** - * Interface used to get feedback about a {@link android.net.LinkSocket}. Instance is optionally - * passed when a LinkSocket is constructed. Multiple LinkSockets may use the same notifier. - * @hide - */ -public interface LinkSocketNotifier { - /** - * This callback function will be called if a better link - * becomes available. - * TODO - this shouldn't be checked for all cases - what's the conditional - * flag? - * If the duplicate socket is accepted, the original will be marked invalid - * and additional use will throw exceptions. - * @param original the original LinkSocket - * @param duplicate the new LinkSocket that better meets the application - * requirements - * @return {@code true} if the application intends to use this link - * - * REM - * TODO - how agressive should we be? - * At a minimum CS tracks which LS have this turned on and tracks the requirements - * When a new link becomes available, automatically check if any of the LinkSockets - * will care. - * If found, grab a refcount on the link so it doesn't go away and send notification - * Optionally, periodically setup connection on available networks to check for better links - * Maybe pass this info into the LinkFactories so condition changes can be acted on more quickly - */ - public boolean onBetterLinkAvailable(LinkSocket original, LinkSocket duplicate); - - /** - * This callback function will be called when a LinkSocket no longer has - * an active link. - * @param socket the LinkSocket that lost its link - * - * REM - * NetworkStateTracker tells us it is disconnected - * CS checks the table for LS on that link - * CS calls each callback (need to work out p2p cross process callback) - */ - public void onLinkLost(LinkSocket socket); - - /** - * This callback function will be called when an application calls - * requestNewLink on a LinkSocket but the LinkSocket is unable to find - * a suitable new link. - * @param socket the LinkSocket for which a new link was not found - * TODO - why the diff between initial request (sync) and requestNewLink? - * - * REM - * CS process of trying to find a new link must track the LS that started it - * on failure, call callback - */ - public void onNewLinkUnavailable(LinkSocket socket); - - /** - * This callback function will be called when any of the notification-marked - * capabilities of the LinkSocket (e.g. upstream bandwidth) have changed. - * @param socket the linkSocet for which capabilities have changed - * @param changedCapabilities the set of capabilities that the application - * is interested in and have changed (with new values) - * - * REM - * Maybe pass the interesting capabilities into the Links. - * Get notified of every capability change - * check for LinkSockets on that Link that are interested in that Capability - call them - */ - public void onCapabilitiesChanged(LinkSocket socket, LinkCapabilities changedCapabilities); -} diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java index 30b61c525eb8..535bbe2415ef 100644 --- a/core/java/android/net/MobileDataStateTracker.java +++ b/core/java/android/net/MobileDataStateTracker.java @@ -66,7 +66,6 @@ public class MobileDataStateTracker extends BaseNetworkStateTracker { private Handler mTarget; private Context mContext; private LinkProperties mLinkProperties; - private LinkCapabilities mLinkCapabilities; private boolean mPrivateDnsRouteSet = false; private boolean mDefaultRouteSet = false; @@ -200,11 +199,11 @@ public class MobileDataStateTracker extends BaseNetworkStateTracker { } mLinkProperties.setMtu(mContext.getResources().getInteger( com.android.internal.R.integer.config_mobile_mtu)); - mLinkCapabilities = intent.getParcelableExtra( - PhoneConstants.DATA_LINK_CAPABILITIES_KEY); - if (mLinkCapabilities == null) { - loge("CONNECTED event did not supply link capabilities."); - mLinkCapabilities = new LinkCapabilities(); + mNetworkCapabilities = intent.getParcelableExtra( + PhoneConstants.DATA_NETWORK_CAPABILITIES_KEY); + if (mNetworkCapabilities == null) { + loge("CONNECTED event did not supply network capabilities."); + mNetworkCapabilities = new NetworkCapabilities(); } } @@ -316,10 +315,10 @@ public class MobileDataStateTracker extends BaseNetworkStateTracker { Slog.d(TAG, "LinkProperties = " ); } - if (mLinkCapabilities != null) { - Slog.d(TAG, "LinkCapabilities = " + mLinkCapabilities); + if (mNetworkCapabilities != null) { + Slog.d(TAG, mNetworkCapabilities.toString()); } else { - Slog.d(TAG, "LinkCapabilities = " ); + Slog.d(TAG, "NetworkCapabilities = " ); } } @@ -750,14 +749,6 @@ public class MobileDataStateTracker extends BaseNetworkStateTracker { return new LinkProperties(mLinkProperties); } - /** - * @see android.net.NetworkStateTracker#getLinkCapabilities() - */ - @Override - public LinkCapabilities getLinkCapabilities() { - return new LinkCapabilities(mLinkCapabilities); - } - public void supplyMessenger(Messenger messenger) { if (VDBG) log(mApnType + " got supplyMessenger"); AsyncChannel ac = new AsyncChannel(); diff --git a/core/java/android/net/LinkCapabilities.aidl b/core/java/android/net/Network.aidl index df7259902feb..73ba1af3f3d0 100644 --- a/core/java/android/net/LinkCapabilities.aidl +++ b/core/java/android/net/Network.aidl @@ -1,6 +1,6 @@ /* ** -** Copyright (C) 2010 The Android Open Source Project +** 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. @@ -17,5 +17,4 @@ package android.net; -parcelable LinkCapabilities; - +parcelable Network; diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java new file mode 100644 index 000000000000..f82bc22483c0 --- /dev/null +++ b/core/java/android/net/Network.java @@ -0,0 +1,59 @@ +/* + * 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 android.net; + +import android.os.Parcelable; +import android.os.Parcel; + + +/** + * Identifies the Network. + * @hide + */ +public class Network implements Parcelable { + + public final int netId; + + public Network(int netId) { + this.netId = netId; + } + + public Network(Network that) { + this.netId = that.netId; + } + + // implement the Parcelable interface + public int describeContents() { + return 0; + } + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(netId); + } + + public static final Creator<Network> CREATOR = + new Creator<Network>() { + public Network createFromParcel(Parcel in) { + int netId = in.readInt(); + + return new Network(netId); + } + + public Network[] newArray(int size) { + return new Network[size]; + } + }; +} diff --git a/core/java/android/net/NetworkCapabilities.aidl b/core/java/android/net/NetworkCapabilities.aidl new file mode 100644 index 000000000000..cd7d71cad978 --- /dev/null +++ b/core/java/android/net/NetworkCapabilities.aidl @@ -0,0 +1,21 @@ +/* +** +** 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 android.net; + +parcelable NetworkCapabilities; + diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java new file mode 100644 index 000000000000..b783046c5c44 --- /dev/null +++ b/core/java/android/net/NetworkCapabilities.java @@ -0,0 +1,328 @@ +/* + * 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 android.net; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.lang.IllegalArgumentException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * A class representing the capabilities of a network + * @hide + */ +public final class NetworkCapabilities implements Parcelable { + private static final String TAG = "NetworkCapabilities"; + private static final boolean DBG = false; + + + /** + * Represents the network's capabilities. If any are specified they will be satisfied + * by any Network that matches all of them. + */ + private long mNetworkCapabilities = (1 << NET_CAPABILITY_NOT_RESTRICTED); + + /** + * Values for NetworkCapabilities. Roughly matches/extends deprecated + * ConnectivityManager TYPE_* + */ + public static final int NET_CAPABILITY_MMS = 0; + public static final int NET_CAPABILITY_SUPL = 1; + public static final int NET_CAPABILITY_DUN = 2; + public static final int NET_CAPABILITY_FOTA = 3; + public static final int NET_CAPABILITY_IMS = 4; + public static final int NET_CAPABILITY_CBS = 5; + public static final int NET_CAPABILITY_WIFI_P2P = 6; + public static final int NET_CAPABILITY_IA = 7; + public static final int NET_CAPABILITY_RCS = 8; + public static final int NET_CAPABILITY_XCAP = 9; + public static final int NET_CAPABILITY_EIMS = 10; + public static final int NET_CAPABILITY_NOT_METERED = 11; + public static final int NET_CAPABILITY_INTERNET = 12; + /** Set by default */ + public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; + + private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS; + private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_RESTRICTED; + + public void addNetworkCapability(int networkCapability) { + if (networkCapability < MIN_NET_CAPABILITY || + networkCapability > MAX_NET_CAPABILITY) { + throw new IllegalArgumentException("NetworkCapability out of range"); + } + mNetworkCapabilities |= 1 << networkCapability; + } + public void removeNetworkCapability(int networkCapability) { + if (networkCapability < MIN_NET_CAPABILITY || + networkCapability > MAX_NET_CAPABILITY) { + throw new IllegalArgumentException("NetworkCapability out of range"); + } + mNetworkCapabilities &= ~(1 << networkCapability); + } + public Collection<Integer> getNetworkCapabilities() { + return enumerateBits(mNetworkCapabilities); + } + public boolean hasCapability(int networkCapability) { + if (networkCapability < MIN_NET_CAPABILITY || + networkCapability > MAX_NET_CAPABILITY) { + return false; + } + return ((mNetworkCapabilities & (1 << networkCapability)) != 0); + } + + private Collection<Integer> enumerateBits(long val) { + ArrayList<Integer> result = new ArrayList<Integer>(); + int resource = 0; + while (val > 0) { + if ((val & 1) == 1) result.add(resource); + val = val >> 1; + resource++; + } + return result; + } + + private void combineNetCapabilities(NetworkCapabilities nc) { + this.mNetworkCapabilities |= nc.mNetworkCapabilities; + } + + private boolean satisfiedByNetCapabilities(NetworkCapabilities nc) { + return ((nc.mNetworkCapabilities & this.mNetworkCapabilities) == this.mNetworkCapabilities); + } + + private boolean equalsNetCapabilities(NetworkCapabilities nc) { + return (nc.mNetworkCapabilities == this.mNetworkCapabilities); + } + + /** + * Representing the transport type. Apps should generally not care about transport. A + * request for a fast internet connection could be satisfied by a number of different + * transports. If any are specified here it will be satisfied a Network that matches + * any of them. If a caller doesn't care about the transport it should not specify any. + */ + private long mTransportTypes; + + /** + * Values for TransportType + */ + public static final int TRANSPORT_CELLULAR = 0; + public static final int TRANSPORT_WIFI = 1; + public static final int TRANSPORT_BLUETOOTH = 2; + public static final int TRANSPORT_ETHERNET = 3; + + private static final int MIN_TRANSPORT = TRANSPORT_CELLULAR; + private static final int MAX_TRANSPORT = TRANSPORT_ETHERNET; + + public void addTransportType(int transportType) { + if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) { + throw new IllegalArgumentException("TransportType out of range"); + } + mTransportTypes |= 1 << transportType; + } + public void removeTransportType(int transportType) { + if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) { + throw new IllegalArgumentException("TransportType out of range"); + } + mTransportTypes &= ~(1 << transportType); + } + public Collection<Integer> getTransportTypes() { + return enumerateBits(mTransportTypes); + } + public boolean hasTransport(int transportType) { + if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) { + return false; + } + return ((mTransportTypes & (1 << transportType)) != 0); + } + + private void combineTransportTypes(NetworkCapabilities nc) { + this.mTransportTypes |= nc.mTransportTypes; + } + private boolean satisfiedByTransportTypes(NetworkCapabilities nc) { + return ((this.mTransportTypes == 0) || + ((this.mTransportTypes & nc.mTransportTypes) != 0)); + } + private boolean equalsTransportTypes(NetworkCapabilities nc) { + return (nc.mTransportTypes == this.mTransportTypes); + } + + /** + * Passive link bandwidth. This is a rough guide of the expected peak bandwidth + * for the first hop on the given transport. It is not measured, but may take into account + * link parameters (Radio technology, allocated channels, etc). + */ + private int mLinkUpBandwidthKbps; + private int mLinkDownBandwidthKbps; + + public void setLinkUpstreamBandwidthKbps(int upKbps) { + mLinkUpBandwidthKbps = upKbps; + } + public int getLinkUpstreamBandwidthKbps() { + return mLinkUpBandwidthKbps; + } + public void setLinkDownstreamBandwidthKbps(int downKbps) { + mLinkDownBandwidthKbps = downKbps; + } + public int getLinkDownstreamBandwidthKbps() { + return mLinkDownBandwidthKbps; + } + + private void combineLinkBandwidths(NetworkCapabilities nc) { + this.mLinkUpBandwidthKbps = + Math.max(this.mLinkUpBandwidthKbps, nc.mLinkUpBandwidthKbps); + this.mLinkDownBandwidthKbps = + Math.max(this.mLinkDownBandwidthKbps, nc.mLinkDownBandwidthKbps); + } + private boolean satisfiedByLinkBandwidths(NetworkCapabilities nc) { + return !(this.mLinkUpBandwidthKbps > nc.mLinkUpBandwidthKbps || + this.mLinkDownBandwidthKbps > nc.mLinkDownBandwidthKbps); + } + private boolean equalsLinkBandwidths(NetworkCapabilities nc) { + return (this.mLinkUpBandwidthKbps == nc.mLinkUpBandwidthKbps && + this.mLinkDownBandwidthKbps == nc.mLinkDownBandwidthKbps); + } + + /** + * Combine a set of Capabilities to this one. Useful for coming up with the complete set + * {@hide} + */ + public void combineCapabilities(NetworkCapabilities nc) { + combineNetCapabilities(nc); + combineTransportTypes(nc); + combineLinkBandwidths(nc); + } + + /** + * Check if our requirements are satisfied by the given Capabilities. + * {@hide} + */ + public boolean satisfiedByNetworkCapabilities(NetworkCapabilities nc) { + return (nc != null && + satisfiedByNetCapabilities(nc) && + satisfiedByTransportTypes(nc) && + satisfiedByLinkBandwidths(nc)); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || (obj instanceof NetworkCapabilities == false)) return false; + NetworkCapabilities that = (NetworkCapabilities)obj; + return (equalsNetCapabilities(that) && + equalsTransportTypes(that) && + equalsLinkBandwidths(that)); + } + + @Override + public int hashCode() { + return ((int)(mNetworkCapabilities & 0xFFFFFFFF) + + ((int)(mNetworkCapabilities >> 32) * 3) + + ((int)(mTransportTypes & 0xFFFFFFFF) * 5) + + ((int)(mTransportTypes >> 32) * 7) + + (mLinkUpBandwidthKbps * 11) + + (mLinkDownBandwidthKbps * 13)); + } + + public NetworkCapabilities() { + } + + public NetworkCapabilities(NetworkCapabilities nc) { + if (nc != null) { + mNetworkCapabilities = nc.mNetworkCapabilities; + mTransportTypes = nc.mTransportTypes; + mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps; + mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps; + } + } + + // Parcelable + public int describeContents() { + return 0; + } + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(mNetworkCapabilities); + dest.writeLong(mTransportTypes); + dest.writeInt(mLinkUpBandwidthKbps); + dest.writeInt(mLinkDownBandwidthKbps); + } + public static final Creator<NetworkCapabilities> CREATOR = + new Creator<NetworkCapabilities>() { + public NetworkCapabilities createFromParcel(Parcel in) { + NetworkCapabilities netCap = new NetworkCapabilities(); + + netCap.mNetworkCapabilities = in.readLong(); + netCap.mTransportTypes = in.readLong(); + netCap.mLinkUpBandwidthKbps = in.readInt(); + netCap.mLinkDownBandwidthKbps = in.readInt(); + return netCap; + } + public NetworkCapabilities[] newArray(int size) { + return new NetworkCapabilities[size]; + } + }; + + public String toString() { + Collection<Integer> types = getTransportTypes(); + String transports = (types.size() > 0 ? " Transports: " : ""); + Iterator<Integer> i = types.iterator(); + while (i.hasNext()) { + switch (i.next()) { + case TRANSPORT_CELLULAR: transports += "CELLULAR"; break; + case TRANSPORT_WIFI: transports += "WIFI"; break; + case TRANSPORT_BLUETOOTH: transports += "BLUETOOTH"; break; + case TRANSPORT_ETHERNET: transports += "ETHERNET"; break; + } + if (i.hasNext()) transports += "|"; + } + + types = getNetworkCapabilities(); + String capabilities = (types.size() > 0 ? " Capabilities: " : ""); + i = types.iterator(); + while (i.hasNext()) { + switch (i.next().intValue()) { + case NET_CAPABILITY_MMS: capabilities += "MMS"; break; + case NET_CAPABILITY_SUPL: capabilities += "SUPL"; break; + case NET_CAPABILITY_DUN: capabilities += "DUN"; break; + case NET_CAPABILITY_FOTA: capabilities += "FOTA"; break; + case NET_CAPABILITY_IMS: capabilities += "IMS"; break; + case NET_CAPABILITY_CBS: capabilities += "CBS"; break; + case NET_CAPABILITY_WIFI_P2P: capabilities += "WIFI_P2P"; break; + case NET_CAPABILITY_IA: capabilities += "IA"; break; + case NET_CAPABILITY_RCS: capabilities += "RCS"; break; + case NET_CAPABILITY_XCAP: capabilities += "XCAP"; break; + case NET_CAPABILITY_EIMS: capabilities += "EIMS"; break; + case NET_CAPABILITY_NOT_METERED: capabilities += "NOT_METERED"; break; + case NET_CAPABILITY_INTERNET: capabilities += "INTERNET"; break; + case NET_CAPABILITY_NOT_RESTRICTED: capabilities += "NOT_RESTRICTED"; break; + } + if (i.hasNext()) capabilities += "&"; + } + + String upBand = ((mLinkUpBandwidthKbps > 0) ? " LinkUpBandwidth>=" + + mLinkUpBandwidthKbps + "Kbps" : ""); + String dnBand = ((mLinkDownBandwidthKbps > 0) ? " LinkDnBandwidth>=" + + mLinkDownBandwidthKbps + "Kbps" : ""); + + return "NetworkCapabilities: [" + transports + capabilities + upBand + dnBand + "]"; + } +} diff --git a/core/java/android/net/NetworkRequest.aidl b/core/java/android/net/NetworkRequest.aidl new file mode 100644 index 000000000000..508defc6b497 --- /dev/null +++ b/core/java/android/net/NetworkRequest.aidl @@ -0,0 +1,20 @@ +/** + * 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 android.net; + +parcelable NetworkRequest; + diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java new file mode 100644 index 000000000000..7e3a06d3809d --- /dev/null +++ b/core/java/android/net/NetworkRequest.java @@ -0,0 +1,87 @@ +/* + * 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 android.net; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @hide + */ +public class NetworkRequest implements Parcelable { + public final NetworkCapabilities networkCapabilities; + public final int requestId; + public final boolean legacy; + private static final AtomicInteger sRequestId = new AtomicInteger(); + + public NetworkRequest(NetworkCapabilities nc) { + this(nc, false, sRequestId.incrementAndGet()); + } + + public NetworkRequest(NetworkCapabilities nc, boolean legacy) { + this(nc, legacy, sRequestId.incrementAndGet()); + } + + private NetworkRequest(NetworkCapabilities nc, boolean legacy, int rId) { + requestId = rId; + networkCapabilities = nc; + this.legacy = legacy; + } + + // implement the Parcelable interface + public int describeContents() { + return 0; + } + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(networkCapabilities, flags); + dest.writeInt(legacy ? 1 : 0); + dest.writeInt(requestId); + } + public static final Creator<NetworkRequest> CREATOR = + new Creator<NetworkRequest>() { + public NetworkRequest createFromParcel(Parcel in) { + NetworkCapabilities nc = (NetworkCapabilities)in.readParcelable(null); + boolean legacy = (in.readInt() == 1); + int requestId = in.readInt(); + return new NetworkRequest(nc, legacy, requestId); + } + public NetworkRequest[] newArray(int size) { + return new NetworkRequest[size]; + } + }; + + public String toString() { + return "NetworkRequest [ id=" + requestId + ", legacy=" + legacy + ", " + + networkCapabilities.toString() + " ]"; + } + + public boolean equals(Object obj) { + if (obj instanceof NetworkRequest == false) return false; + NetworkRequest that = (NetworkRequest)obj; + return (that.legacy == this.legacy && + that.requestId == this.requestId && + ((that.networkCapabilities == null && this.networkCapabilities == null) || + (that.networkCapabilities != null && + that.networkCapabilities.equals(this.networkCapabilities)))); + } + + public int hashCode() { + return requestId + (legacy ? 1013 : 2026) + (networkCapabilities.hashCode() * 1051); + } +} diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java index fbe1f8296be4..2e0e9e432b54 100644 --- a/core/java/android/net/NetworkState.java +++ b/core/java/android/net/NetworkState.java @@ -28,21 +28,21 @@ public class NetworkState implements Parcelable { public final NetworkInfo networkInfo; public final LinkProperties linkProperties; - public final LinkCapabilities linkCapabilities; + public final NetworkCapabilities networkCapabilities; /** Currently only used by testing. */ public final String subscriberId; public final String networkId; public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties, - LinkCapabilities linkCapabilities) { - this(networkInfo, linkProperties, linkCapabilities, null, null); + NetworkCapabilities networkCapabilities) { + this(networkInfo, linkProperties, networkCapabilities, null, null); } public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties, - LinkCapabilities linkCapabilities, String subscriberId, String networkId) { + NetworkCapabilities networkCapabilities, String subscriberId, String networkId) { this.networkInfo = networkInfo; this.linkProperties = linkProperties; - this.linkCapabilities = linkCapabilities; + this.networkCapabilities = networkCapabilities; this.subscriberId = subscriberId; this.networkId = networkId; } @@ -50,7 +50,7 @@ public class NetworkState implements Parcelable { public NetworkState(Parcel in) { networkInfo = in.readParcelable(null); linkProperties = in.readParcelable(null); - linkCapabilities = in.readParcelable(null); + networkCapabilities = in.readParcelable(null); subscriberId = in.readString(); networkId = in.readString(); } @@ -64,7 +64,7 @@ public class NetworkState implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeParcelable(networkInfo, flags); out.writeParcelable(linkProperties, flags); - out.writeParcelable(linkCapabilities, flags); + out.writeParcelable(networkCapabilities, flags); out.writeString(subscriberId); out.writeString(networkId); } diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java index c49b1d131eec..35500cc1cdb6 100644 --- a/core/java/android/net/NetworkStateTracker.java +++ b/core/java/android/net/NetworkStateTracker.java @@ -111,12 +111,9 @@ public interface NetworkStateTracker { public LinkProperties getLinkProperties(); /** - * A capability is an Integer/String pair, the capabilities - * are defined in the class LinkSocket#Key. - * * @return a copy of this connections capabilities, may be empty but never null. */ - public LinkCapabilities getLinkCapabilities(); + public NetworkCapabilities getNetworkCapabilities(); /** * Get interesting information about this network link @@ -250,4 +247,14 @@ public interface NetworkStateTracker { */ public void stopSampling(SamplingDataTracker.SamplingSnapshot s); + /* + * Record the current netId + */ + public void setNetId(int netId); + + /* + * ? + */ + public Network getNetwork(); + } diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java index daf0065d1c47..6a78c29c15ac 100644 --- a/core/java/android/net/Proxy.java +++ b/core/java/android/net/Proxy.java @@ -273,21 +273,19 @@ public final class Proxy { String host = null; String port = null; String exclList = null; - String pacFileUrl = null; + Uri pacFileUrl = Uri.EMPTY; if (p != null) { host = p.getHost(); port = Integer.toString(p.getPort()); exclList = p.getExclusionListAsString(); - if (p.getPacFileUrl() != null) { - pacFileUrl = p.getPacFileUrl().toString(); - } + pacFileUrl = p.getPacFileUrl(); } setHttpProxySystemProperty(host, port, exclList, pacFileUrl); } /** @hide */ public static final void setHttpProxySystemProperty(String host, String port, String exclList, - String pacFileUrl) { + Uri pacFileUrl) { if (exclList != null) exclList = exclList.replace(",", "|"); if (false) Log.d(TAG, "setHttpProxySystemProperty :"+host+":"+port+" - "+exclList); if (host != null) { @@ -311,7 +309,7 @@ public final class Proxy { System.clearProperty("http.nonProxyHosts"); System.clearProperty("https.nonProxyHosts"); } - if (!TextUtils.isEmpty(pacFileUrl)) { + if (!Uri.EMPTY.equals(pacFileUrl)) { ProxySelector.setDefault(new PacProxySelector()); } else { ProxySelector.setDefault(sDefaultProxySelector); diff --git a/core/java/android/net/ProxyDataTracker.java b/core/java/android/net/ProxyDataTracker.java index 461e8b80212a..4973b3d992b6 100644 --- a/core/java/android/net/ProxyDataTracker.java +++ b/core/java/android/net/ProxyDataTracker.java @@ -104,7 +104,7 @@ public class ProxyDataTracker extends BaseNetworkStateTracker { public ProxyDataTracker() { mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_PROXY, 0, NETWORK_TYPE, ""); mLinkProperties = new LinkProperties(); - mLinkCapabilities = new LinkCapabilities(); + mNetworkCapabilities = new NetworkCapabilities(); mNetworkInfo.setIsAvailable(true); try { mLinkProperties.addDns(InetAddress.getByName(DNS1)); diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java index b40941fe9c2b..991d9dacd723 100644 --- a/core/java/android/net/ProxyInfo.java +++ b/core/java/android/net/ProxyInfo.java @@ -44,7 +44,7 @@ public class ProxyInfo implements Parcelable { private String mExclusionList; private String[] mParsedExclusionList; - private String mPacFileUrl; + private Uri mPacFileUrl; /** *@hide */ @@ -85,7 +85,7 @@ public class ProxyInfo implements Parcelable { * at the specified URL. */ public static ProxyInfo buildPacProxy(Uri pacUri) { - return new ProxyInfo(pacUri.toString()); + return new ProxyInfo(pacUri); } /** @@ -96,27 +96,45 @@ public class ProxyInfo implements Parcelable { mHost = host; mPort = port; setExclusionList(exclList); + mPacFileUrl = Uri.EMPTY; } /** * Create a ProxyProperties that points at a PAC URL. * @hide */ - public ProxyInfo(String pacFileUrl) { + public ProxyInfo(Uri pacFileUrl) { mHost = LOCAL_HOST; mPort = LOCAL_PORT; setExclusionList(LOCAL_EXCL_LIST); + if (pacFileUrl == null) { + throw new NullPointerException(); + } mPacFileUrl = pacFileUrl; } /** + * Create a ProxyProperties that points at a PAC URL. + * @hide + */ + public ProxyInfo(String pacFileUrl) { + mHost = LOCAL_HOST; + mPort = LOCAL_PORT; + setExclusionList(LOCAL_EXCL_LIST); + mPacFileUrl = Uri.parse(pacFileUrl); + } + + /** * Only used in PacManager after Local Proxy is bound. * @hide */ - public ProxyInfo(String pacFileUrl, int localProxyPort) { + public ProxyInfo(Uri pacFileUrl, int localProxyPort) { mHost = LOCAL_HOST; mPort = localProxyPort; setExclusionList(LOCAL_EXCL_LIST); + if (pacFileUrl == null) { + throw new NullPointerException(); + } mPacFileUrl = pacFileUrl; } @@ -125,7 +143,7 @@ public class ProxyInfo implements Parcelable { mPort = port; mExclusionList = exclList; mParsedExclusionList = parsedExclList; - mPacFileUrl = null; + mPacFileUrl = Uri.EMPTY; } // copy constructor instead of clone @@ -137,6 +155,9 @@ public class ProxyInfo implements Parcelable { mHost = source.getHost(); mPort = source.getPort(); mPacFileUrl = source.mPacFileUrl; + if (mPacFileUrl == null) { + mPacFileUrl = Uri.EMPTY; + } mExclusionList = source.getExclusionListAsString(); mParsedExclusionList = source.mParsedExclusionList; } @@ -158,10 +179,7 @@ public class ProxyInfo implements Parcelable { * no PAC script. */ public Uri getPacFileUrl() { - if (TextUtils.isEmpty(mPacFileUrl)) { - return null; - } - return Uri.parse(mPacFileUrl); + return mPacFileUrl; } /** @@ -210,7 +228,7 @@ public class ProxyInfo implements Parcelable { * @hide */ public boolean isValid() { - if (!TextUtils.isEmpty(mPacFileUrl)) return true; + if (!Uri.EMPTY.equals(mPacFileUrl)) return true; return Proxy.PROXY_VALID == Proxy.validate(mHost == null ? "" : mHost, mPort == 0 ? "" : Integer.toString(mPort), mExclusionList == null ? "" : mExclusionList); @@ -234,7 +252,7 @@ public class ProxyInfo implements Parcelable { @Override public String toString() { StringBuilder sb = new StringBuilder(); - if (mPacFileUrl != null) { + if (!Uri.EMPTY.equals(mPacFileUrl)) { sb.append("PAC Script: "); sb.append(mPacFileUrl); } else if (mHost != null) { @@ -257,13 +275,15 @@ public class ProxyInfo implements Parcelable { ProxyInfo p = (ProxyInfo)o; // If PAC URL is present in either then they must be equal. // Other parameters will only be for fall back. - if (!TextUtils.isEmpty(mPacFileUrl)) { + if (!Uri.EMPTY.equals(mPacFileUrl)) { return mPacFileUrl.equals(p.getPacFileUrl()) && mPort == p.mPort; } - if (!TextUtils.isEmpty(p.mPacFileUrl)) { + if (!Uri.EMPTY.equals(p.mPacFileUrl)) { + return false; + } + if (mExclusionList != null && !mExclusionList.equals(p.getExclusionListAsString())) { return false; } - if (mExclusionList != null && !mExclusionList.equals(p.getExclusionListAsString())) return false; if (mHost != null && p.getHost() != null && mHost.equals(p.getHost()) == false) { return false; } @@ -296,9 +316,9 @@ public class ProxyInfo implements Parcelable { * @hide */ public void writeToParcel(Parcel dest, int flags) { - if (mPacFileUrl != null) { + if (!Uri.EMPTY.equals(mPacFileUrl)) { dest.writeByte((byte)1); - dest.writeString(mPacFileUrl); + mPacFileUrl.writeToParcel(dest, 0); dest.writeInt(mPort); return; } else { @@ -325,7 +345,7 @@ public class ProxyInfo implements Parcelable { String host = null; int port = 0; if (in.readByte() != 0) { - String url = in.readString(); + Uri url = Uri.CREATOR.createFromParcel(in); int localPort = in.readInt(); return new ProxyInfo(url, localPort); } diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index f5ff1856f50b..9e03f95048d9 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -455,4 +455,14 @@ interface INetworkManagementService * Check whether the mobile radio is currently active. */ boolean isNetworkActive(); + + /** + * setup a new network + */ + void createNetwork(int netId, String iface); + + /** + * remove a network + */ + void removeNetwork(int netId); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 1847b5558f29..e1fd46ee40a6 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3838,22 +3838,11 @@ public final class Settings { /** * Setting that specifies whether display color inversion is enabled. - * - * @hide */ public static final String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED = "accessibility_display_inversion_enabled"; /** - * Integer property that specifies the type of color inversion to - * perform. Valid values are defined in AccessibilityManager. - * - * @hide - */ - public static final String ACCESSIBILITY_DISPLAY_INVERSION = - "accessibility_display_inversion"; - - /** * Setting that specifies whether the quick setting tile for display * color space adjustment is enabled. * @@ -3881,44 +3870,6 @@ public final class Settings { "accessibility_display_daltonizer"; /** - * Setting that specifies whether the quick setting tile for display - * contrast enhancement is enabled. - * - * @hide - */ - public static final String ACCESSIBILITY_DISPLAY_CONTRAST_QUICK_SETTING_ENABLED = - "accessibility_display_contrast_quick_setting_enabled"; - - /** - * Setting that specifies whether display contrast enhancement is - * enabled. - * - * @hide - */ - public static final String ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED = - "accessibility_display_contrast_enabled"; - - /** - * Floating point property that specifies display contrast adjustment. - * Valid range is [0, ...] where 0 is gray, 1 is normal, and higher - * values indicate enhanced contrast. - * - * @hide - */ - public static final String ACCESSIBILITY_DISPLAY_CONTRAST = - "accessibility_display_contrast"; - - /** - * Floating point property that specifies display brightness adjustment. - * Valid range is [-1, 1] where -1 is black, 0 is default, and 1 is - * white. - * - * @hide - */ - public static final String ACCESSIBILITY_DISPLAY_BRIGHTNESS = - "accessibility_display_brightness"; - - /** * The timout for considering a press to be a long press in milliseconds. * @hide */ diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl index d4b29d8e3a4d..d4919eb7dbf5 100644 --- a/core/java/android/service/notification/INotificationListener.aidl +++ b/core/java/android/service/notification/INotificationListener.aidl @@ -17,11 +17,15 @@ package android.service.notification; import android.service.notification.StatusBarNotification; +import android.service.notification.NotificationOrderUpdate; /** @hide */ oneway interface INotificationListener { - void onListenerConnected(in String[] notificationKeys); - void onNotificationPosted(in StatusBarNotification notification); - void onNotificationRemoved(in StatusBarNotification notification); + void onListenerConnected(in NotificationOrderUpdate update); + void onNotificationPosted(in StatusBarNotification notification, + in NotificationOrderUpdate update); + void onNotificationRemoved(in StatusBarNotification notification, + in NotificationOrderUpdate update); + void onNotificationOrderUpdate(in NotificationOrderUpdate update); }
\ No newline at end of file diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 3673f034e4aa..a94f45a9c875 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -22,10 +22,13 @@ import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.IBinder; +import android.os.RemoteException; import android.os.ServiceManager; -import android.os.UserHandle; import android.util.Log; +import java.util.Comparator; +import java.util.HashMap; + /** * A service that receives calls from the system when new notifications are posted or removed. * <p>To extend this class, you must declare the service in your manifest file with @@ -46,6 +49,7 @@ public abstract class NotificationListenerService extends Service { + "[" + getClass().getSimpleName() + "]"; private INotificationListenerWrapper mWrapper = null; + private String[] mNotificationKeys; private INotificationManager mNoMan; @@ -95,6 +99,15 @@ public abstract class NotificationListenerService extends Service { // optional } + /** + * Implement this method to be notified when the notification order cahnges. + * + * Call {@link #getOrderedNotificationKeys()} to retrieve the new order. + */ + public void onNotificationOrderUpdate() { + // optional + } + private final INotificationManager getNotificationInterface() { if (mNoMan == null) { mNoMan = INotificationManager.Stub.asInterface( @@ -202,7 +215,7 @@ public abstract class NotificationListenerService extends Service { * Request the list of outstanding notifications (that is, those that are visible to the * current user). Useful when you don't know what's already been posted. * - * @return An array of active notifications. + * @return An array of active notifications, sorted in natural order. */ public StatusBarNotification[] getActiveNotifications() { return getActiveNotifications(null /*all*/); @@ -213,7 +226,8 @@ public abstract class NotificationListenerService extends Service { * current user). Useful when you don't know what's already been posted. * * @param keys A specific list of notification keys, or {@code null} for all. - * @return An array of active notifications. + * @return An array of active notifications, sorted in natural order + * if {@code keys} is {@code null}. */ public StatusBarNotification[] getActiveNotifications(String[] keys) { if (!isBound()) return null; @@ -226,21 +240,15 @@ public abstract class NotificationListenerService extends Service { } /** - * Request the list of outstanding notification keys(that is, those that are visible to the - * current user). You can use the notification keys for subsequent retrieval via + * Request the list of notification keys in their current natural order. + * You can use the notification keys for subsequent retrieval via * {@link #getActiveNotifications(String[]) or dismissal via * {@link #cancelNotifications(String[]). * - * @return An array of active notification keys. + * @return An array of active notification keys, in their natural order. */ - public String[] getActiveNotificationKeys() { - if (!isBound()) return null; - try { - return getNotificationInterface().getActiveNotificationKeysFromListener(mWrapper); - } catch (android.os.RemoteException ex) { - Log.v(TAG, "Unable to contact notification manager", ex); - } - return null; + public String[] getOrderedNotificationKeys() { + return mNotificationKeys; } @Override @@ -261,28 +269,60 @@ public abstract class NotificationListenerService extends Service { private class INotificationListenerWrapper extends INotificationListener.Stub { @Override - public void onNotificationPosted(StatusBarNotification sbn) { + public void onNotificationPosted(StatusBarNotification sbn, + NotificationOrderUpdate update) { try { - NotificationListenerService.this.onNotificationPosted(sbn); + // protect subclass from concurrent modifications of (@link mNotificationKeys}. + synchronized (mWrapper) { + updateNotificationKeys(update); + NotificationListenerService.this.onNotificationPosted(sbn); + } } catch (Throwable t) { - Log.w(TAG, "Error running onNotificationPosted", t); + Log.w(TAG, "Error running onOrderedNotificationPosted", t); } } @Override - public void onNotificationRemoved(StatusBarNotification sbn) { + public void onNotificationRemoved(StatusBarNotification sbn, + NotificationOrderUpdate update) { try { - NotificationListenerService.this.onNotificationRemoved(sbn); + // protect subclass from concurrent modifications of (@link mNotificationKeys}. + synchronized (mWrapper) { + updateNotificationKeys(update); + NotificationListenerService.this.onNotificationRemoved(sbn); + } } catch (Throwable t) { Log.w(TAG, "Error running onNotificationRemoved", t); } } @Override - public void onListenerConnected(String[] notificationKeys) { + public void onListenerConnected(NotificationOrderUpdate update) { try { - NotificationListenerService.this.onListenerConnected(notificationKeys); + // protect subclass from concurrent modifications of (@link mNotificationKeys}. + synchronized (mWrapper) { + updateNotificationKeys(update); + NotificationListenerService.this.onListenerConnected(mNotificationKeys); + } } catch (Throwable t) { Log.w(TAG, "Error running onListenerConnected", t); } } + @Override + public void onNotificationOrderUpdate(NotificationOrderUpdate update) + throws RemoteException { + try { + // protect subclass from concurrent modifications of (@link mNotificationKeys}. + synchronized (mWrapper) { + updateNotificationKeys(update); + NotificationListenerService.this.onNotificationOrderUpdate(); + } + } catch (Throwable t) { + Log.w(TAG, "Error running onNotificationOrderUpdate", t); + } + } + } + + private void updateNotificationKeys(NotificationOrderUpdate update) { + // TODO: avoid garbage by comparing the lists + mNotificationKeys = update.getOrderedKeys(); } } diff --git a/core/java/android/service/notification/NotificationOrderUpdate.aidl b/core/java/android/service/notification/NotificationOrderUpdate.aidl new file mode 100644 index 000000000000..5d50641d6145 --- /dev/null +++ b/core/java/android/service/notification/NotificationOrderUpdate.aidl @@ -0,0 +1,19 @@ +/** + * 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 android.service.notification; + +parcelable NotificationOrderUpdate; diff --git a/core/java/android/service/notification/NotificationOrderUpdate.java b/core/java/android/service/notification/NotificationOrderUpdate.java new file mode 100644 index 000000000000..20e19a3d0a75 --- /dev/null +++ b/core/java/android/service/notification/NotificationOrderUpdate.java @@ -0,0 +1,62 @@ +/* + * 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 android.service.notification; + +import android.os.Parcel; +import android.os.Parcelable; + +public class NotificationOrderUpdate implements Parcelable { + // TODO replace this with an update instead of the whole array + private final String[] mKeys; + + /** @hide */ + public NotificationOrderUpdate(String[] keys) { + this.mKeys = keys; + } + + public NotificationOrderUpdate(Parcel in) { + this.mKeys = in.readStringArray(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeStringArray(this.mKeys); + } + + public static final Parcelable.Creator<NotificationOrderUpdate> CREATOR + = new Parcelable.Creator<NotificationOrderUpdate>() { + public NotificationOrderUpdate createFromParcel(Parcel parcel) { + return new NotificationOrderUpdate(parcel); + } + + public NotificationOrderUpdate[] newArray(int size) { + return new NotificationOrderUpdate[size]; + } + }; + + /** + * @hide + * @return ordered list of keys + */ + String[] getOrderedKeys() { + return mKeys; + } +} diff --git a/core/java/android/speech/tts/TtsEngines.java b/core/java/android/speech/tts/TtsEngines.java index 4f996cd7b91e..9b929a30d622 100644 --- a/core/java/android/speech/tts/TtsEngines.java +++ b/core/java/android/speech/tts/TtsEngines.java @@ -307,6 +307,24 @@ public class TtsEngines { } /** + * True if a given TTS engine uses the default phone locale as a default locale. Attempts to + * read the value from {@link Settings.Secure#TTS_DEFAULT_LOCALE}, failing which the + * old style value from {@link Settings.Secure#TTS_DEFAULT_LANG} is read. If + * both these values are empty, this methods returns true. + * + * @param engineName the engine to return the locale for. + */ + public boolean isLocaleSetToDefaultForEngine(String engineName) { + return (TextUtils.isEmpty(parseEnginePrefFromList( + getString(mContext.getContentResolver(), Settings.Secure.TTS_DEFAULT_LOCALE), + engineName)) && + TextUtils.isEmpty( + Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.TTS_DEFAULT_LANG))); + } + + + /** * Parses a locale preference value delimited by {@link #LOCALE_DELIMITER}. * Varies from {@link String#split} in that it will always return an array * of length 3 with non null values. diff --git a/core/java/android/tv/ITvInputClient.aidl b/core/java/android/tv/ITvInputClient.aidl index 538f8a173daa..ac83356a969e 100644 --- a/core/java/android/tv/ITvInputClient.aidl +++ b/core/java/android/tv/ITvInputClient.aidl @@ -26,6 +26,7 @@ import android.view.InputChannel; * @hide */ oneway interface ITvInputClient { - void onSessionCreated(in ComponentName name, IBinder token, in InputChannel channel, int seq); - void onAvailabilityChanged(in ComponentName name, boolean isAvailable); + void onSessionCreated(in String inputId, IBinder token, in InputChannel channel, int seq); + void onAvailabilityChanged(in String inputId, boolean isAvailable); + void onSessionReleased(int seq); } diff --git a/core/java/android/tv/ITvInputManager.aidl b/core/java/android/tv/ITvInputManager.aidl index a4c99e450596..b756aba2188a 100644 --- a/core/java/android/tv/ITvInputManager.aidl +++ b/core/java/android/tv/ITvInputManager.aidl @@ -30,12 +30,12 @@ import android.view.Surface; interface ITvInputManager { List<TvInputInfo> getTvInputList(int userId); - boolean getAvailability(in ITvInputClient client, in ComponentName name, int userId); + boolean getAvailability(in ITvInputClient client, in String inputId, int userId); - void registerCallback(in ITvInputClient client, in ComponentName name, int userId); - void unregisterCallback(in ITvInputClient client, in ComponentName name, int userId); + void registerCallback(in ITvInputClient client, in String inputId, int userId); + void unregisterCallback(in ITvInputClient client, in String inputId, int userId); - void createSession(in ITvInputClient client, in ComponentName name, int seq, int userId); + void createSession(in ITvInputClient client, in String inputId, int seq, int userId); void releaseSession(in IBinder sessionToken, int userId); void setSurface(in IBinder sessionToken, in Surface surface, int userId); diff --git a/core/java/android/tv/ITvInputServiceCallback.aidl b/core/java/android/tv/ITvInputServiceCallback.aidl index e535c81e60bd..71fc780b563f 100644 --- a/core/java/android/tv/ITvInputServiceCallback.aidl +++ b/core/java/android/tv/ITvInputServiceCallback.aidl @@ -24,5 +24,5 @@ import android.content.ComponentName; * @hide */ oneway interface ITvInputServiceCallback { - void onAvailabilityChanged(in ComponentName name, boolean isAvailable); + void onAvailabilityChanged(in String inputId, boolean isAvailable); } diff --git a/core/java/android/tv/TvInputInfo.java b/core/java/android/tv/TvInputInfo.java index 90e41778fd97..50462cc56e5b 100644 --- a/core/java/android/tv/TvInputInfo.java +++ b/core/java/android/tv/TvInputInfo.java @@ -39,7 +39,7 @@ public final class TvInputInfo implements Parcelable { public TvInputInfo(ResolveInfo service) { mService = service; ServiceInfo si = service.serviceInfo; - mId = new ComponentName(si.packageName, si.name).flattenToShortString(); + mId = generateInputIdForComponenetName(new ComponentName(si.packageName, si.name)); } /** @@ -75,7 +75,7 @@ public final class TvInputInfo implements Parcelable { * Loads the user-displayed label for this TV input service. * * @param pm Supplies a PackageManager used to load the TV input's resources. - * @return Returns a CharSequence containing the TV input's label. If the TV input does not have + * @return a CharSequence containing the TV input's label. If the TV input does not have * a label, its name is returned. */ public CharSequence loadLabel(PackageManager pm) { @@ -128,6 +128,17 @@ public final class TvInputInfo implements Parcelable { } /** + * Used to generate an input id from a ComponentName. + * + * @param name the component name for generating an input id. + * @return the generated input id for the given {@code name}. + * @hide + */ + public static final String generateInputIdForComponenetName(ComponentName name) { + return name.flattenToShortString(); + } + + /** * Used to make this class parcelable. * * @hide diff --git a/core/java/android/tv/TvInputManager.java b/core/java/android/tv/TvInputManager.java index 7b9b1fb6b67f..c5f179a337c0 100644 --- a/core/java/android/tv/TvInputManager.java +++ b/core/java/android/tv/TvInputManager.java @@ -16,7 +16,6 @@ package android.tv; -import android.content.ComponentName; import android.graphics.Rect; import android.net.Uri; import android.os.Handler; @@ -50,15 +49,15 @@ public final class TvInputManager { private final ITvInputManager mService; // A mapping from an input to the list of its TvInputListenerRecords. - private final Map<ComponentName, List<TvInputListenerRecord>> mTvInputListenerRecordsMap = - new HashMap<ComponentName, List<TvInputListenerRecord>>(); + private final Map<String, List<TvInputListenerRecord>> mTvInputListenerRecordsMap = + new HashMap<String, List<TvInputListenerRecord>>(); - // A mapping from the sequence number of a session to its SessionCreateCallbackRecord. - private final SparseArray<SessionCreateCallbackRecord> mSessionCreateCallbackRecordMap = - new SparseArray<SessionCreateCallbackRecord>(); + // A mapping from the sequence number of a session to its SessionCallbackRecord. + private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap = + new SparseArray<SessionCallbackRecord>(); // A sequence number for the next session to be created. Should be protected by a lock - // {@code mSessionCreateCallbackRecordMap}. + // {@code mSessionCallbackRecordMap}. private int mNextSeq; private final ITvInputClient mClient; @@ -68,31 +67,52 @@ public final class TvInputManager { /** * Interface used to receive the created session. */ - public interface SessionCreateCallback { + public abstract static class SessionCallback { /** * This is called after {@link TvInputManager#createSession} has been processed. * * @param session A {@link TvInputManager.Session} instance created. This can be * {@code null} if the creation request failed. */ - void onSessionCreated(Session session); + public void onSessionCreated(Session session) { + } + + /** + * This is called when {@link TvInputManager.Session} is released. + * This typically happens when the process hosting the session has crashed or been killed. + * + * @param session A {@link TvInputManager.Session} instance released. + */ + public void onSessionReleased(Session session) { + } } - private static final class SessionCreateCallbackRecord { - private final SessionCreateCallback mSessionCreateCallback; + private static final class SessionCallbackRecord { + private final SessionCallback mSessionCallback; private final Handler mHandler; + private Session mSession; - public SessionCreateCallbackRecord(SessionCreateCallback sessionCreateCallback, + public SessionCallbackRecord(SessionCallback sessionCallback, Handler handler) { - mSessionCreateCallback = sessionCreateCallback; + mSessionCallback = sessionCallback; mHandler = handler; } public void postSessionCreated(final Session session) { + mSession = session; mHandler.post(new Runnable() { @Override public void run() { - mSessionCreateCallback.onSessionCreated(session); + mSessionCallback.onSessionCreated(session); + } + }); + } + + public void postSessionReleased() { + mHandler.post(new Runnable() { + @Override + public void run() { + mSessionCallback.onSessionReleased(mSession); } }); } @@ -105,12 +125,11 @@ public final class TvInputManager { /** * This is called when the availability status of a given TV input is changed. * - * @param name {@link ComponentName} of {@link android.app.Service} that implements the - * given TV input. + * @param inputId the id of the TV input. * @param isAvailable {@code true} if the given TV input is available to show TV programs. * {@code false} otherwise. */ - public void onAvailabilityChanged(ComponentName name, boolean isAvailable) { + public void onAvailabilityChanged(String inputId, boolean isAvailable) { } } @@ -127,11 +146,11 @@ public final class TvInputManager { return mListener; } - public void postAvailabilityChanged(final ComponentName name, final boolean isAvailable) { + public void postAvailabilityChanged(final String inputId, final boolean isAvailable) { mHandler.post(new Runnable() { @Override public void run() { - mListener.onAvailabilityChanged(name, isAvailable); + mListener.onAvailabilityChanged(inputId, isAvailable); } }); } @@ -145,34 +164,48 @@ public final class TvInputManager { mUserId = userId; mClient = new ITvInputClient.Stub() { @Override - public void onSessionCreated(ComponentName name, IBinder token, InputChannel channel, + public void onSessionCreated(String inputId, IBinder token, InputChannel channel, int seq) { - synchronized (mSessionCreateCallbackRecordMap) { - SessionCreateCallbackRecord record = mSessionCreateCallbackRecordMap.get(seq); - mSessionCreateCallbackRecordMap.delete(seq); + synchronized (mSessionCallbackRecordMap) { + SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); if (record == null) { Log.e(TAG, "Callback not found for " + token); return; } Session session = null; if (token != null) { - session = new Session(token, channel, mService, mUserId); + session = new Session(token, channel, mService, mUserId, seq, + mSessionCallbackRecordMap); } record.postSessionCreated(session); } } @Override - public void onAvailabilityChanged(ComponentName name, boolean isAvailable) { + public void onSessionReleased(int seq) { + synchronized (mSessionCallbackRecordMap) { + SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); + mSessionCallbackRecordMap.delete(seq); + if (record == null) { + Log.e(TAG, "Callback not found for seq:" + seq); + return; + } + record.mSession.releaseInternal(); + record.postSessionReleased(); + } + } + + @Override + public void onAvailabilityChanged(String inputId, boolean isAvailable) { synchronized (mTvInputListenerRecordsMap) { - List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(name); + List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId); if (records == null) { // Silently ignore - no listener is registered yet. return; } int recordsCount = records.size(); for (int i = 0; i < recordsCount; i++) { - records.get(i).postAvailabilityChanged(name, isAvailable); + records.get(i).postAvailabilityChanged(inputId, isAvailable); } } } @@ -195,24 +228,23 @@ public final class TvInputManager { /** * Returns the availability of a given TV input. * - * @param name {@link ComponentName} of {@link android.app.Service} that implements the given TV - * input. + * @param inputId the id of the TV input. * @throws IllegalArgumentException if the argument is {@code null}. * @throws IllegalStateException If there is no {@link TvInputListener} registered on the given * TV input. */ - public boolean getAvailability(ComponentName name) { - if (name == null) { - throw new IllegalArgumentException("name cannot be null"); + public boolean getAvailability(String inputId) { + if (inputId == null) { + throw new IllegalArgumentException("id cannot be null"); } synchronized (mTvInputListenerRecordsMap) { - List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(name); + List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId); if (records == null || records.size() == 0) { throw new IllegalStateException("At least one listener should be registered."); } } try { - return mService.getAvailability(mClient, name, mUserId); + return mService.getAvailability(mClient, inputId, mUserId); } catch (RemoteException e) { throw new RuntimeException(e); } @@ -221,15 +253,14 @@ public final class TvInputManager { /** * Registers a {@link TvInputListener} for a given TV input. * - * @param name {@link ComponentName} of {@link android.app.Service} that implements the given TV - * input. + * @param inputId the id of the TV input. * @param listener a listener used to monitor status of the given TV input. * @param handler a {@link Handler} that the status change will be delivered to. * @throws IllegalArgumentException if any of the arguments is {@code null}. */ - public void registerListener(ComponentName name, TvInputListener listener, Handler handler) { - if (name == null) { - throw new IllegalArgumentException("name cannot be null"); + public void registerListener(String inputId, TvInputListener listener, Handler handler) { + if (inputId == null) { + throw new IllegalArgumentException("id cannot be null"); } if (listener == null) { throw new IllegalArgumentException("listener cannot be null"); @@ -238,12 +269,12 @@ public final class TvInputManager { throw new IllegalArgumentException("handler cannot be null"); } synchronized (mTvInputListenerRecordsMap) { - List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(name); + List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId); if (records == null) { records = new ArrayList<TvInputListenerRecord>(); - mTvInputListenerRecordsMap.put(name, records); + mTvInputListenerRecordsMap.put(inputId, records); try { - mService.registerCallback(mClient, name, mUserId); + mService.registerCallback(mClient, inputId, mUserId); } catch (RemoteException e) { throw new RuntimeException(e); } @@ -255,22 +286,21 @@ public final class TvInputManager { /** * Unregisters the existing {@link TvInputListener} for a given TV input. * - * @param name {@link ComponentName} of {@link android.app.Service} that implements the given TV - * input. + * @param inputId the id of the TV input. * @param listener the existing listener to remove for the given TV input. * @throws IllegalArgumentException if any of the arguments is {@code null}. */ - public void unregisterListener(ComponentName name, final TvInputListener listener) { - if (name == null) { - throw new IllegalArgumentException("name cannot be null"); + public void unregisterListener(String inputId, final TvInputListener listener) { + if (inputId == null) { + throw new IllegalArgumentException("id cannot be null"); } if (listener == null) { throw new IllegalArgumentException("listener cannot be null"); } synchronized (mTvInputListenerRecordsMap) { - List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(name); + List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId); if (records == null) { - Log.e(TAG, "No listener found for " + name.getClassName()); + Log.e(TAG, "No listener found for " + inputId); return; } for (Iterator<TvInputListenerRecord> it = records.iterator(); it.hasNext();) { @@ -281,11 +311,11 @@ public final class TvInputManager { } if (records.isEmpty()) { try { - mService.unregisterCallback(mClient, name, mUserId); + mService.unregisterCallback(mClient, inputId, mUserId); } catch (RemoteException e) { throw new RuntimeException(e); } finally { - mTvInputListenerRecordsMap.remove(name); + mTvInputListenerRecordsMap.remove(inputId); } } } @@ -298,16 +328,15 @@ public final class TvInputManager { * the given TV input. * </p> * - * @param name {@link ComponentName} of {@link android.app.Service} that implements the given TV - * input. + * @param inputId the id of the TV input. * @param callback a callback used to receive the created session. * @param handler a {@link Handler} that the session creation will be delivered to. * @throws IllegalArgumentException if any of the arguments is {@code null}. */ - public void createSession(ComponentName name, final SessionCreateCallback callback, + public void createSession(String inputId, final SessionCallback callback, Handler handler) { - if (name == null) { - throw new IllegalArgumentException("name cannot be null"); + if (inputId == null) { + throw new IllegalArgumentException("id cannot be null"); } if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); @@ -315,12 +344,12 @@ public final class TvInputManager { if (handler == null) { throw new IllegalArgumentException("handler cannot be null"); } - SessionCreateCallbackRecord record = new SessionCreateCallbackRecord(callback, handler); - synchronized (mSessionCreateCallbackRecordMap) { + SessionCallbackRecord record = new SessionCallbackRecord(callback, handler); + synchronized (mSessionCallbackRecordMap) { int seq = mNextSeq++; - mSessionCreateCallbackRecordMap.put(seq, record); + mSessionCallbackRecordMap.put(seq, record); try { - mService.createSession(mClient, name, seq, mUserId); + mService.createSession(mClient, inputId, seq, mUserId); } catch (RemoteException e) { throw new RuntimeException(e); } @@ -337,6 +366,7 @@ public final class TvInputManager { private final ITvInputManager mService; private final int mUserId; + private final int mSeq; // For scheduling input event handling on the main thread. This also serves as a lock to // protect pending input events and the input channel. @@ -344,17 +374,21 @@ public final class TvInputManager { private final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20); private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20); + private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap; private IBinder mToken; private TvInputEventSender mSender; private InputChannel mChannel; /** @hide */ - private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId) { + private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId, + int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) { mToken = token; mChannel = channel; mService = service; mUserId = userId; + mSeq = seq; + mSessionCallbackRecordMap = sessionCallbackRecordMap; } /** @@ -368,22 +402,11 @@ public final class TvInputManager { } try { mService.releaseSession(mToken, mUserId); - mToken = null; } catch (RemoteException e) { throw new RuntimeException(e); } - synchronized (mHandler) { - if (mChannel != null) { - if (mSender != null) { - flushPendingEventsLocked(); - mSender.dispose(); - mSender = null; - } - mChannel.dispose(); - mChannel = null; - } - } + releaseInternal(); } /** @@ -675,6 +698,24 @@ public final class TvInputManager { mPendingEventPool.release(p); } + private void releaseInternal() { + mToken = null; + synchronized (mHandler) { + if (mChannel != null) { + if (mSender != null) { + flushPendingEventsLocked(); + mSender.dispose(); + mSender = null; + } + mChannel.dispose(); + mChannel = null; + } + } + synchronized (mSessionCallbackRecordMap) { + mSessionCallbackRecordMap.remove(mSeq); + } + } + private final class InputEventHandler extends Handler { public static final int MSG_SEND_INPUT_EVENT = 1; public static final int MSG_TIMEOUT_INPUT_EVENT = 2; diff --git a/core/java/android/tv/TvInputService.java b/core/java/android/tv/TvInputService.java index 70e7f95105b4..1d6298d7e695 100644 --- a/core/java/android/tv/TvInputService.java +++ b/core/java/android/tv/TvInputService.java @@ -60,7 +60,7 @@ public abstract class TvInputService extends Service { */ public static final String SERVICE_INTERFACE = "android.tv.TvInputService"; - private ComponentName mComponentName; + private String mId; private final Handler mHandler = new ServiceHandler(); private final RemoteCallbackList<ITvInputServiceCallback> mCallbacks = new RemoteCallbackList<ITvInputServiceCallback>(); @@ -69,7 +69,8 @@ public abstract class TvInputService extends Service { @Override public void onCreate() { super.onCreate(); - mComponentName = new ComponentName(getPackageName(), getClass().getName()); + mId = TvInputInfo.generateInputIdForComponenetName( + new ComponentName(getPackageName(), getClass().getName())); } @Override @@ -82,7 +83,7 @@ public abstract class TvInputService extends Service { // The first time a callback is registered, the service needs to report its // availability status so that the system can know its initial value. try { - cb.onAvailabilityChanged(mComponentName, mAvailable); + cb.onAvailabilityChanged(mId, mAvailable); } catch (RemoteException e) { Log.e(TAG, "error in onAvailabilityChanged", e); } @@ -531,8 +532,7 @@ public abstract class TvInputService extends Service { int n = mCallbacks.beginBroadcast(); try { for (int i = 0; i < n; i++) { - mCallbacks.getBroadcastItem(i).onAvailabilityChanged(mComponentName, - isAvailable); + mCallbacks.getBroadcastItem(i).onAvailabilityChanged(mId, isAvailable); } } catch (RemoteException e) { Log.e(TAG, "Unexpected exception", e); diff --git a/core/java/android/tv/TvView.java b/core/java/android/tv/TvView.java index 289823b4a2cd..80501e872072 100644 --- a/core/java/android/tv/TvView.java +++ b/core/java/android/tv/TvView.java @@ -16,13 +16,13 @@ package android.tv; -import android.content.ComponentName; import android.content.Context; import android.graphics.Rect; import android.os.Handler; +import android.text.TextUtils; import android.tv.TvInputManager.Session; import android.tv.TvInputManager.Session.FinishedInputEventCallback; -import android.tv.TvInputManager.SessionCreateCallback; +import android.tv.TvInputManager.SessionCallback; import android.util.AttributeSet; import android.util.Log; import android.view.InputEvent; @@ -46,7 +46,7 @@ public class TvView extends SurfaceView { private boolean mOverlayViewCreated; private Rect mOverlayViewFrame; private final TvInputManager mTvInputManager; - private SessionCreateCallback mSessionCreateCallback; + private SessionCallback mSessionCallback; private OnUnhandledInputEventListener mOnUnhandledInputEventListener; private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() { @@ -108,19 +108,19 @@ public class TvView extends SurfaceView { } /** - * Binds a TV input to this view. {@link SessionCreateCallback#onSessionCreated} will be + * Binds a TV input to this view. {@link SessionCallback#onSessionCreated} will be * called to send the result of this binding with {@link TvInputManager.Session}. * If a TV input is already bound, the input will be unbound from this view and its session * will be released. * - * @param name TV input name will be bound to this view. + * @param inputId the id of TV input which will be bound to this view. * @param callback called when TV input is bound. The callback sends * {@link TvInputManager.Session} * @throws IllegalArgumentException if any of the arguments is {@code null}. */ - public void bindTvInput(ComponentName name, SessionCreateCallback callback) { - if (name == null) { - throw new IllegalArgumentException("name cannot be null"); + public void bindTvInput(String inputId, SessionCallback callback) { + if (TextUtils.isEmpty(inputId)) { + throw new IllegalArgumentException("inputId cannot be null or an empty string"); } if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); @@ -130,11 +130,11 @@ public class TvView extends SurfaceView { } // When bindTvInput is called multiple times before the callback is called, // only the callback of the last bindTvInput call will be actually called back. - // The previous callbacks will be ignored. For the logic, mSessionCreateCallback + // The previous callbacks will be ignored. For the logic, mSessionCallback // is newly assigned for every bindTvInput call and compared with // MySessionCreateCallback.this. - mSessionCreateCallback = new MySessionCreateCallback(callback); - mTvInputManager.createSession(name, mSessionCreateCallback, mHandler); + mSessionCallback = new MySessionCallback(callback); + mTvInputManager.createSession(inputId, mSessionCallback, mHandler); } /** @@ -196,7 +196,9 @@ public class TvView extends SurfaceView { if (mSession == null) { return false; } - int ret = mSession.dispatchInputEvent(event, event, mFinishedInputEventCallback, mHandler); + InputEvent copiedEvent = event.copy(); + int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback, + mHandler); return ret != Session.DISPATCH_NOT_HANDLED; } @@ -209,7 +211,9 @@ public class TvView extends SurfaceView { if (mSession == null) { return false; } - int ret = mSession.dispatchInputEvent(event, event, mFinishedInputEventCallback, mHandler); + InputEvent copiedEvent = event.copy(); + int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback, + mHandler); return ret != Session.DISPATCH_NOT_HANDLED; } @@ -222,7 +226,9 @@ public class TvView extends SurfaceView { if (mSession == null) { return false; } - int ret = mSession.dispatchInputEvent(event, event, mFinishedInputEventCallback, mHandler); + InputEvent copiedEvent = event.copy(); + int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback, + mHandler); return ret != Session.DISPATCH_NOT_HANDLED; } @@ -235,7 +241,9 @@ public class TvView extends SurfaceView { if (mSession == null) { return false; } - int ret = mSession.dispatchInputEvent(event, event, mFinishedInputEventCallback, mHandler); + InputEvent copiedEvent = event.copy(); + int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback, + mHandler); return ret != Session.DISPATCH_NOT_HANDLED; } @@ -328,16 +336,16 @@ public class TvView extends SurfaceView { boolean onUnhandledInputEvent(InputEvent event); } - private class MySessionCreateCallback implements SessionCreateCallback { - final SessionCreateCallback mExternalCallback; + private class MySessionCallback extends SessionCallback { + final SessionCallback mExternalCallback; - MySessionCreateCallback(SessionCreateCallback externalCallback) { + MySessionCallback(SessionCallback externalCallback) { mExternalCallback = externalCallback; } @Override public void onSessionCreated(Session session) { - if (this != mSessionCreateCallback) { + if (this != mSessionCallback) { // This callback is obsolete. session.release(); return; @@ -356,5 +364,13 @@ public class TvView extends SurfaceView { mExternalCallback.onSessionCreated(session); } } + + @Override + public void onSessionReleased(Session session) { + mSession = null; + if (mExternalCallback != null) { + mExternalCallback.onSessionReleased(session); + } + } } } diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java index 33964a067818..8f4b710b9a70 100644 --- a/core/java/android/util/TimeUtils.java +++ b/core/java/android/util/TimeUtils.java @@ -245,6 +245,9 @@ public class TimeUtils { private static final int SECONDS_PER_HOUR = 60 * 60; private static final int SECONDS_PER_DAY = 24 * 60 * 60; + /** @hide */ + public static final long NANOS_PER_MS = 1000000; + private static final Object sFormatSync = new Object(); private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+5]; diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 0a7607513378..106643037614 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -112,8 +112,6 @@ public final class Choreographer { private static final int SKIPPED_FRAME_WARNING_LIMIT = SystemProperties.getInt( "debug.choreographer.skipwarning", 30); - private static final long NANOS_PER_MS = 1000000; - private static final int MSG_DO_FRAME = 0; private static final int MSG_DO_SCHEDULE_VSYNC = 1; private static final int MSG_DO_SCHEDULE_CALLBACK = 2; @@ -263,7 +261,7 @@ public final class Choreographer { * @return The refresh rate as the nanoseconds between frames * @hide */ - long getFrameIntervalNanos() { + public long getFrameIntervalNanos() { return mFrameIntervalNanos; } @@ -456,7 +454,7 @@ public final class Choreographer { * @hide */ public long getFrameTime() { - return getFrameTimeNanos() / NANOS_PER_MS; + return getFrameTimeNanos() / TimeUtils.NANOS_PER_MS; } /** @@ -497,7 +495,7 @@ public final class Choreographer { } } else { final long nextFrameTime = Math.max( - mLastFrameTimeNanos / NANOS_PER_MS + sFrameDelay, now); + mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now); if (DEBUG) { Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms."); } @@ -746,7 +744,7 @@ public final class Choreographer { mFrame = frame; Message msg = Message.obtain(mHandler, this); msg.setAsynchronous(true); - mHandler.sendMessageAtTime(msg, timestampNanos / NANOS_PER_MS); + mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS); } @Override diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index be3b6ce97271..ec4d560a5a72 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -16,12 +16,17 @@ package android.view; +import android.animation.TimeInterpolator; import android.graphics.Canvas; import android.graphics.CanvasProperty; import android.graphics.Paint; import android.util.SparseIntArray; +import android.util.TimeUtils; import com.android.internal.util.VirtualRefBasePtr; +import com.android.internal.view.animation.FallbackLUTInterpolator; +import com.android.internal.view.animation.HasNativeInterpolator; +import com.android.internal.view.animation.NativeInterpolatorFactory; import java.lang.ref.WeakReference; @@ -71,9 +76,12 @@ public final class RenderNodeAnimator { public static final int DELTA_TYPE_ABSOLUTE = 0; public static final int DELTA_TYPE_DELTA = 1; - private RenderNode mTarget; private VirtualRefBasePtr mNativePtr; + private RenderNode mTarget; + private TimeInterpolator mInterpolator; + private boolean mStarted = false; + public int mapViewPropertyToRenderProperty(int viewProperty) { return sViewPropertyAnimatorMap.get(viewProperty); } @@ -100,9 +108,37 @@ public final class RenderNodeAnimator { mNativePtr = new VirtualRefBasePtr(ptr); } - public void start(View target) { - mTarget = target.mRenderNode; + private void checkMutable() { + if (mStarted) { + throw new IllegalStateException("Animator has already started, cannot change it now!"); + } + } + + private void applyInterpolator() { + if (mInterpolator == null) return; + + long ni; + if (mInterpolator.getClass().isAnnotationPresent(HasNativeInterpolator.class)) { + ni = ((NativeInterpolatorFactory)mInterpolator).createNativeInterpolator(); + } else { + int duration = nGetDuration(mNativePtr.get()); + ni = FallbackLUTInterpolator.createNativeInterpolator(mInterpolator, duration); + } + nSetInterpolator(mNativePtr.get(), ni); + } + + private void start(RenderNode node) { + if (mStarted) { + throw new IllegalStateException("Already started!"); + } + mStarted = true; + applyInterpolator(); + mTarget = node; mTarget.addAnimator(this); + } + + public void start(View target) { + start(target.mRenderNode); // Kick off a frame to start the process target.invalidateViewProperty(true, false); } @@ -112,8 +148,7 @@ public final class RenderNodeAnimator { throw new IllegalArgumentException("Not a GLES20RecordingCanvas"); } GLES20RecordingCanvas recordingCanvas = (GLES20RecordingCanvas) canvas; - mTarget = recordingCanvas.mNode; - mTarget.addAnimator(this); + start(recordingCanvas.mNode); } public void cancel() { @@ -121,9 +156,15 @@ public final class RenderNodeAnimator { } public void setDuration(int duration) { + checkMutable(); nSetDuration(mNativePtr.get(), duration); } + public void setInterpolator(TimeInterpolator interpolator) { + checkMutable(); + mInterpolator = interpolator; + } + long getNativeAnimator() { return mNativePtr.get(); } @@ -147,4 +188,6 @@ public final class RenderNodeAnimator { private static native long nCreateCanvasPropertyPaintAnimator(WeakReference<RenderNodeAnimator> weakThis, long canvasProperty, int paintField, int deltaValueType, float deltaValue); private static native void nSetDuration(long nativePtr, int duration); + private static native int nGetDuration(long nativePtr); + private static native void nSetInterpolator(long animPtr, long interpolatorPtr); } diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 2587ba11e676..5653066a9aef 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -20,6 +20,7 @@ import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.os.Trace; +import android.util.TimeUtils; import android.view.Surface.OutOfResourcesException; import android.view.View.AttachInfo; @@ -51,8 +52,6 @@ public class ThreadedRenderer extends HardwareRenderer { private static final Rect NULL_RECT = new Rect(); - private static final long NANOS_PER_MS = 1000000; - // Keep in sync with DrawFrameTask.h SYNC_* flags // Nothing interesting to report private static final int SYNC_OK = 0x0; @@ -203,7 +202,7 @@ public class ThreadedRenderer extends HardwareRenderer { void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) { attachInfo.mIgnoreDirtyState = true; long frameTimeNanos = mChoreographer.getFrameTimeNanos(); - attachInfo.mDrawingTime = frameTimeNanos / NANOS_PER_MS; + attachInfo.mDrawingTime = frameTimeNanos / TimeUtils.NANOS_PER_MS; updateRootDisplayList(view, callbacks); diff --git a/core/java/android/view/animation/AccelerateDecelerateInterpolator.java b/core/java/android/view/animation/AccelerateDecelerateInterpolator.java index 158c56e8c34a..ed6949a0f3b6 100644 --- a/core/java/android/view/animation/AccelerateDecelerateInterpolator.java +++ b/core/java/android/view/animation/AccelerateDecelerateInterpolator.java @@ -19,12 +19,17 @@ package android.view.animation; import android.content.Context; import android.util.AttributeSet; +import com.android.internal.view.animation.HasNativeInterpolator; +import com.android.internal.view.animation.NativeInterpolatorFactory; +import com.android.internal.view.animation.NativeInterpolatorFactoryHelper; + /** * An interpolator where the rate of change starts and ends slowly but * accelerates through the middle. * */ -public class AccelerateDecelerateInterpolator implements Interpolator { +@HasNativeInterpolator +public class AccelerateDecelerateInterpolator implements Interpolator, NativeInterpolatorFactory { public AccelerateDecelerateInterpolator() { } @@ -35,4 +40,10 @@ public class AccelerateDecelerateInterpolator implements Interpolator { public float getInterpolation(float input) { return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f; } + + /** @hide */ + @Override + public long createNativeInterpolator() { + return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator(); + } } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index be0c27dc4b2d..3fee0ac0fad0 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -24,6 +24,8 @@ import com.android.internal.view.IInputMethodManager; import com.android.internal.view.IInputMethodSession; import com.android.internal.view.InputBindResult; +import libcore.util.Objects; + import android.content.Context; import android.graphics.Rect; import android.inputmethodservice.InputMethodService; @@ -317,10 +319,17 @@ public final class InputMethodManager { int mCursorSelEnd; int mCursorCandStart; int mCursorCandEnd; + + /** + * The instance that has previously been sent to the input method. + */ + private CursorAnchorInfo mCursorAnchorInfo = null; + /** * The buffer to retrieve the view location in screen coordinates in {@link #updateCursor}. */ private final int[] mViewTopLeft = new int[2]; + // ----------------------------------------------------------- /** @@ -489,6 +498,9 @@ public final class InputMethodManager { case SET_CURSOR_ANCHOR_MONITOR_MODE: { synchronized (mH) { mCursorAnchorMonitorMode = msg.arg1; + // Clear the cache. + mCursorRect.setEmpty(); + mCursorAnchorInfo = null; } return; } @@ -1173,6 +1185,7 @@ public final class InputMethodManager { mCursorCandStart = -1; mCursorCandEnd = -1; mCursorRect.setEmpty(); + mCursorAnchorInfo = null; servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic, this); } else { servedContext = null; @@ -1535,10 +1548,9 @@ public final class InputMethodManager { || mCurrentTextBoxAttribute == null || mCurMethod == null) { return; } + if (DEBUG) Log.d(TAG, "updateCursor"); mTmpCursorRect.set(left, top, right, bottom); - if (!mCursorRect.equals(mTmpCursorRect)) { - if (DEBUG) Log.d(TAG, "updateCursor"); - + if (!Objects.equal(mCursorRect, mTmpCursorRect)) { try { if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod); mCursorRect.set(mTmpCursorRect); @@ -1569,10 +1581,14 @@ public final class InputMethodManager { || mCurrentTextBoxAttribute == null || mCurMethod == null) { return; } - if (DEBUG) Log.d(TAG, "updateCursorAnchorInfo"); - + if (Objects.equal(mCursorAnchorInfo, cursorAnchorInfo)) { + Log.w(TAG, "Ignoring redundant updateCursorAnchorInfo: info=" + cursorAnchorInfo); + return; + } + if (DEBUG) Log.v(TAG, "updateCursorAnchorInfo: " + cursorAnchorInfo); try { mCurMethod.updateCursorAnchorInfo(cursorAnchorInfo); + mCursorAnchorInfo = cursorAnchorInfo; } catch (RemoteException e) { Log.w(TAG, "IME died: " + mCurId, e); } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 565ea130e221..d9a4f5710d4b 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -743,7 +743,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * * @param view The view whose scroll state is being reported * - * @param scrollState The current scroll state. One of + * @param scrollState The current scroll state. One of * {@link #SCROLL_STATE_TOUCH_SCROLL} or {@link #SCROLL_STATE_IDLE}. */ public void onScrollStateChanged(AbsListView view, int scrollState); @@ -3305,18 +3305,22 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te private void scrollIfNeeded(int x, int y, MotionEvent vtev) { int rawDeltaY = y - mMotionY; + int scrollOffsetCorrection = 0; + int scrollConsumedCorrection = 0; + if (mLastY == Integer.MIN_VALUE) { + rawDeltaY -= mMotionCorrection; + } if (dispatchNestedPreScroll(0, rawDeltaY, mScrollConsumed, mScrollOffset)) { rawDeltaY -= mScrollConsumed[1]; - mMotionCorrection -= mScrollOffset[1]; - if (mLastY != Integer.MIN_VALUE) { - mLastY -= mScrollOffset[1] + mScrollConsumed[1]; - } + scrollOffsetCorrection -= mScrollOffset[1]; + scrollConsumedCorrection -= mScrollConsumed[1]; if (vtev != null) { vtev.offsetLocation(0, mScrollOffset[1]); } } - final int deltaY = rawDeltaY - mMotionCorrection; - int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY; + final int deltaY = rawDeltaY; + int incrementalDeltaY = + mLastY != Integer.MIN_VALUE ? y - mLastY + scrollConsumedCorrection : deltaY; int lastYCorrection = 0; if (mTouchMode == TOUCH_MODE_SCROLL) { @@ -3378,7 +3382,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te (motionViewRealTop - motionViewPrevTop); if (dispatchNestedScroll(0, overscroll - incrementalDeltaY, 0, overscroll, mScrollOffset)) { - mMotionCorrection -= mScrollOffset[1]; lastYCorrection -= mScrollOffset[1]; if (vtev != null) { vtev.offsetLocation(0, mScrollOffset[1]); @@ -3421,9 +3424,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } } - mMotionY = y; + mMotionY = y + scrollOffsetCorrection; } - mLastY = y + lastYCorrection; + mLastY = y + lastYCorrection + scrollOffsetCorrection; } } else if (mTouchMode == TOUCH_MODE_OVERSCROLL) { if (y != mLastY) { diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index 225cd6d589ca..4f2d9c647fda 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -19,7 +19,9 @@ package android.widget; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.Insets; import android.graphics.Rect; +import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.util.AttributeSet; @@ -32,9 +34,12 @@ import android.view.accessibility.AccessibilityNodeInfo; import com.android.internal.R; public abstract class AbsSeekBar extends ProgressBar { + private final Rect mTempRect = new Rect(); + private Drawable mThumb; private int mThumbOffset; - + private boolean mSplitTrack; + /** * On touch, this offset plus the scaled value from the position of the * touch will form the progress value. Usually 0. @@ -51,10 +56,10 @@ public abstract class AbsSeekBar extends ProgressBar { * progress. */ private int mKeyProgressIncrement = 1; - + private static final int NO_ALPHA = 0xFF; private float mDisabledAlpha; - + private int mScaledTouchSlop; private float mTouchDownX; private boolean mIsDragging; @@ -76,12 +81,16 @@ public abstract class AbsSeekBar extends ProgressBar { TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.SeekBar, defStyleAttr, defStyleRes); - Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb); - setThumb(thumb); // will guess mThumbOffset if thumb != null... - // ...but allow layout to override this - int thumbOffset = a.getDimensionPixelOffset( + + final Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb); + setThumb(thumb); + + // Guess thumb offset if thumb != null, but allow layout to override. + final int thumbOffset = a.getDimensionPixelOffset( com.android.internal.R.styleable.SeekBar_thumbOffset, getThumbOffset()); setThumbOffset(thumbOffset); + + mSplitTrack = a.getBoolean(com.android.internal.R.styleable.SeekBar_splitTrack, false); a.recycle(); a = context.obtainStyledAttributes(attrs, @@ -97,7 +106,7 @@ public abstract class AbsSeekBar extends ProgressBar { * <p> * If the thumb is a valid drawable (i.e. not null), half its width will be * used as the new thumb offset (@see #setThumbOffset(int)). - * + * * @param thumb Drawable representing the thumb */ public void setThumb(Drawable thumb) { @@ -132,7 +141,7 @@ public abstract class AbsSeekBar extends ProgressBar { mThumb = thumb; invalidate(); if (needUpdate) { - updateThumbPos(getWidth(), getHeight()); + updateThumbAndTrackPos(getWidth(), getHeight()); if (thumb != null && thumb.isStateful()) { // Note that if the states are different this won't work. // For now, let's consider that an app bug. @@ -162,7 +171,7 @@ public abstract class AbsSeekBar extends ProgressBar { /** * Sets the thumb offset that allows the thumb to extend out of the range of * the track. - * + * * @param thumbOffset The offset amount in pixels. */ public void setThumbOffset(int thumbOffset) { @@ -171,8 +180,27 @@ public abstract class AbsSeekBar extends ProgressBar { } /** + * Specifies whether the track should be split by the thumb. When true, + * the thumb's optical bounds will be clipped out of the track drawable, + * then the thumb will be drawn into the resulting gap. + * + * @param splitTrack Whether the track should be split by the thumb + */ + public void setSplitTrack(boolean splitTrack) { + mSplitTrack = splitTrack; + invalidate(); + } + + /** + * Returns whether the track should be split by the thumb. + */ + public boolean getSplitTrack() { + return mSplitTrack; + } + + /** * Sets the amount of progress changed via the arrow keys. - * + * * @param increment The amount to increment or decrement when the user * presses the arrow keys. */ @@ -184,14 +212,14 @@ public abstract class AbsSeekBar extends ProgressBar { * Returns the amount of progress changed via the arrow keys. * <p> * By default, this will be a value that is derived from the max progress. - * + * * @return The amount to increment or decrement when the user presses the * arrow keys. This will be positive. */ public int getKeyProgressIncrement() { return mKeyProgressIncrement; } - + @Override public synchronized void setMax(int max) { super.setMax(max); @@ -217,79 +245,85 @@ public abstract class AbsSeekBar extends ProgressBar { @Override protected void drawableStateChanged() { super.drawableStateChanged(); - - Drawable progressDrawable = getProgressDrawable(); + + final Drawable progressDrawable = getProgressDrawable(); if (progressDrawable != null) { progressDrawable.setAlpha(isEnabled() ? NO_ALPHA : (int) (NO_ALPHA * mDisabledAlpha)); } - - if (mThumb != null && mThumb.isStateful()) { - int[] state = getDrawableState(); - mThumb.setState(state); + + final Drawable thumb = mThumb; + if (thumb != null && thumb.isStateful()) { + thumb.setState(getDrawableState()); } } - + @Override void onProgressRefresh(float scale, boolean fromUser) { super.onProgressRefresh(scale, fromUser); - Drawable thumb = mThumb; + + final Drawable thumb = mThumb; if (thumb != null) { setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE); - /* - * Since we draw translated, the drawable's bounds that it signals - * for invalidation won't be the actual bounds we want invalidated, - * so just invalidate this whole view. - */ + + // Since we draw translated, the drawable's bounds that it signals + // for invalidation won't be the actual bounds we want invalidated, + // so just invalidate this whole view. invalidate(); } } - - + @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); - updateThumbPos(w, h); + + updateThumbAndTrackPos(w, h); } - private void updateThumbPos(int w, int h) { - Drawable d = getCurrentDrawable(); - Drawable thumb = mThumb; - int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight(); + private void updateThumbAndTrackPos(int w, int h) { + final Drawable track = getCurrentDrawable(); + final Drawable thumb = mThumb; + // The max height does not incorporate padding, whereas the height - // parameter does - int trackHeight = Math.min(mMaxHeight, h - mPaddingTop - mPaddingBottom); - - int max = getMax(); - float scale = max > 0 ? (float) getProgress() / (float) max : 0; - + // parameter does. + final int trackHeight = Math.min(mMaxHeight, h - mPaddingTop - mPaddingBottom); + final int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight(); + + // Apply offset to whichever item is taller. + final int trackOffset; + final int thumbOffset; if (thumbHeight > trackHeight) { - if (thumb != null) { - setThumbPos(w, thumb, scale, 0); - } - int gapForCenteringTrack = (thumbHeight - trackHeight) / 2; - if (d != null) { - // Canvas will be translated by the padding, so 0,0 is where we start drawing - d.setBounds(0, gapForCenteringTrack, - w - mPaddingRight - mPaddingLeft, h - mPaddingBottom - gapForCenteringTrack - - mPaddingTop); - } + trackOffset = (thumbHeight - trackHeight) / 2; + thumbOffset = 0; } else { - if (d != null) { - // Canvas will be translated by the padding, so 0,0 is where we start drawing - d.setBounds(0, 0, w - mPaddingRight - mPaddingLeft, h - mPaddingBottom - - mPaddingTop); - } - int gap = (trackHeight - thumbHeight) / 2; - if (thumb != null) { - setThumbPos(w, thumb, scale, gap); - } + trackOffset = 0; + thumbOffset = (trackHeight - thumbHeight) / 2; + } + + if (track != null) { + track.setBounds(0, trackOffset, w - mPaddingRight - mPaddingLeft, + h - mPaddingBottom - trackOffset - mPaddingTop); + } + + if (thumb != null) { + setThumbPos(w, thumb, getScale(), thumbOffset); } } + private float getScale() { + final int max = getMax(); + return max > 0 ? getProgress() / (float) max : 0; + } + /** - * @param gap If set to {@link Integer#MIN_VALUE}, this will be ignored and + * Updates the thumb drawable bounds. + * + * @param w Width of the view, including padding + * @param thumb Drawable used for the thumb + * @param scale Current progress between 0 and 1 + * @param offset Vertical offset for centering. If set to + * {@link Integer#MIN_VALUE}, the current offset will be used. */ - private void setThumbPos(int w, Drawable thumb, float scale, int gap) { + private void setThumbPos(int w, Drawable thumb, float scale, int offset) { int available = w - mPaddingLeft - mPaddingRight; final int thumbWidth = thumb.getIntrinsicWidth(); final int thumbHeight = thumb.getIntrinsicHeight(); @@ -301,13 +335,13 @@ public abstract class AbsSeekBar extends ProgressBar { final int thumbPos = (int) (scale * available + 0.5f); final int top, bottom; - if (gap == Integer.MIN_VALUE) { + if (offset == Integer.MIN_VALUE) { final Rect oldBounds = thumb.getBounds(); top = oldBounds.top; bottom = oldBounds.bottom; } else { - top = gap; - bottom = gap + thumbHeight; + top = offset; + bottom = offset + thumbHeight; } final int left = (isLayoutRtl() && mMirrorForRtl) ? available - thumbPos : thumbPos; @@ -342,6 +376,33 @@ public abstract class AbsSeekBar extends ProgressBar { protected synchronized void onDraw(Canvas canvas) { super.onDraw(canvas); + drawThumb(canvas); + } + + @Override + void drawTrack(Canvas canvas) { + final Drawable thumbDrawable = mThumb; + if (thumbDrawable != null && mSplitTrack) { + final Insets insets = thumbDrawable.getOpticalInsets(); + final Rect tempRect = mTempRect; + thumbDrawable.copyBounds(tempRect); + tempRect.offset(mPaddingLeft - mThumbOffset, mPaddingTop); + tempRect.left += insets.left; + tempRect.right -= insets.right; + + final int saveCount = canvas.save(); + canvas.clipRect(tempRect, Op.DIFFERENCE); + super.drawTrack(canvas); + canvas.restoreToCount(saveCount); + } else { + super.drawTrack(canvas); + } + } + + /** + * Draw the thumb. + */ + void drawThumb(Canvas canvas) { if (mThumb != null) { canvas.save(); // Translate the padding. For the x, we need to allow the thumb to @@ -366,17 +427,17 @@ public abstract class AbsSeekBar extends ProgressBar { } dw += mPaddingLeft + mPaddingRight; dh += mPaddingTop + mPaddingBottom; - + setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0), resolveSizeAndState(dh, heightMeasureSpec, 0)); } - + @Override public boolean onTouchEvent(MotionEvent event) { if (!mIsUserSeekable || !isEnabled()) { return false; } - + switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (isInScrollingContainer()) { @@ -391,7 +452,7 @@ public abstract class AbsSeekBar extends ProgressBar { attemptClaimDrag(); } break; - + case MotionEvent.ACTION_MOVE: if (mIsDragging) { trackTouchEvent(event); @@ -408,7 +469,7 @@ public abstract class AbsSeekBar extends ProgressBar { } } break; - + case MotionEvent.ACTION_UP: if (mIsDragging) { trackTouchEvent(event); @@ -426,7 +487,7 @@ public abstract class AbsSeekBar extends ProgressBar { // value has not apparently changed) invalidate(); break; - + case MotionEvent.ACTION_CANCEL: if (mIsDragging) { onStopTrackingTouch(); @@ -493,7 +554,7 @@ public abstract class AbsSeekBar extends ProgressBar { mParent.requestDisallowInterceptTouchEvent(true); } } - + /** * This is called when the user has started touching this widget. */ @@ -526,7 +587,7 @@ public abstract class AbsSeekBar extends ProgressBar { setProgress(progress - mKeyProgressIncrement, true); onKeyChange(); return true; - + case KeyEvent.KEYCODE_DPAD_RIGHT: if (progress >= getMax()) break; setProgress(progress + mKeyProgressIncrement, true); @@ -595,17 +656,13 @@ public abstract class AbsSeekBar extends ProgressBar { public void onRtlPropertiesChanged(int layoutDirection) { super.onRtlPropertiesChanged(layoutDirection); - int max = getMax(); - float scale = max > 0 ? (float) getProgress() / (float) max : 0; - - Drawable thumb = mThumb; + final Drawable thumb = mThumb; if (thumb != null) { - setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE); - /* - * Since we draw translated, the drawable's bounds that it signals - * for invalidation won't be the actual bounds we want invalidated, - * so just invalidate this whole view. - */ + setThumbPos(getWidth(), thumb, getScale(), Integer.MIN_VALUE); + + // Since we draw translated, the drawable's bounds that it signals + // for invalidation won't be the actual bounds we want invalidated, + // so just invalidate this whole view. invalidate(); } } diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index f7e81b8bd2c7..0c3715dc6b7b 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -1066,21 +1066,30 @@ public class ProgressBar extends View { protected synchronized void onDraw(Canvas canvas) { super.onDraw(canvas); - Drawable d = mCurrentDrawable; + drawTrack(canvas); + } + + /** + * Draws the progress bar track. + */ + void drawTrack(Canvas canvas) { + final Drawable d = mCurrentDrawable; if (d != null) { // Translate canvas so a indeterminate circular progress bar with padding // rotates properly in its animation - canvas.save(); + final int saveCount = canvas.save(); + if(isLayoutRtl() && mMirrorForRtl) { canvas.translate(getWidth() - mPaddingRight, mPaddingTop); canvas.scale(-1.0f, 1.0f); } else { canvas.translate(mPaddingLeft, mPaddingTop); } - long time = getDrawingTime(); + + final long time = getDrawingTime(); if (mHasAnimation) { mAnimation.getTransformation(time, mTransformation); - float scale = mTransformation.getAlpha(); + final float scale = mTransformation.getAlpha(); try { mInDrawing = true; d.setLevel((int) (scale * MAX_LEVEL)); @@ -1089,8 +1098,10 @@ public class ProgressBar extends View { } postInvalidateOnAnimation(); } + d.draw(canvas); - canvas.restore(); + canvas.restoreToCount(saveCount); + if (mShouldStartAnimationDrawable && d instanceof Animatable) { ((Animatable) d).start(); mShouldStartAnimationDrawable = false; diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index 08af4dee6c64..438e16476e0d 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -22,9 +22,11 @@ import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.Insets; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Typeface; +import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.text.Layout; import android.text.StaticLayout; @@ -85,6 +87,7 @@ public class Switch extends CompoundButton { private int mThumbTextPadding; private int mSwitchMinWidth; private int mSwitchPadding; + private boolean mSplitTrack; private CharSequence mTextOn; private CharSequence mTextOff; @@ -174,13 +177,13 @@ public class Switch extends CompoundButton { super(context, attrs, defStyleAttr, defStyleRes); mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); - Resources res = getResources(); + + final Resources res = getResources(); mTextPaint.density = res.getDisplayMetrics().density; mTextPaint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale); final TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.Switch, defStyleAttr, defStyleRes); - mThumbDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_thumb); mTrackDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_track); mTextOn = a.getText(com.android.internal.R.styleable.Switch_textOn); @@ -191,15 +194,16 @@ public class Switch extends CompoundButton { com.android.internal.R.styleable.Switch_switchMinWidth, 0); mSwitchPadding = a.getDimensionPixelSize( com.android.internal.R.styleable.Switch_switchPadding, 0); + mSplitTrack = a.getBoolean(com.android.internal.R.styleable.Switch_splitTrack, false); - int appearance = a.getResourceId( + final int appearance = a.getResourceId( com.android.internal.R.styleable.Switch_switchTextAppearance, 0); if (appearance != 0) { setSwitchTextAppearance(context, appearance); } a.recycle(); - ViewConfiguration config = ViewConfiguration.get(context); + final ViewConfiguration config = ViewConfiguration.get(context); mTouchSlop = config.getScaledTouchSlop(); mMinFlingVelocity = config.getScaledMinimumFlingVelocity(); @@ -469,6 +473,29 @@ public class Switch extends CompoundButton { } /** + * Specifies whether the track should be split by the thumb. When true, + * the thumb's optical bounds will be clipped out of the track drawable, + * then the thumb will be drawn into the resulting gap. + * + * @param splitTrack Whether the track should be split by the thumb + * + * @attr ref android.R.styleable#Switch_splitTrack + */ + public void setSplitTrack(boolean splitTrack) { + mSplitTrack = splitTrack; + invalidate(); + } + + /** + * Returns whether the track should be split by the thumb. + * + * @attr ref android.R.styleable#Switch_splitTrack + */ + public boolean getSplitTrack() { + return mSplitTrack; + } + + /** * Returns the text displayed when the button is in the checked state. * * @attr ref android.R.styleable#Switch_textOn @@ -518,13 +545,15 @@ public class Switch extends CompoundButton { mTrackDrawable.getPadding(mTempRect); - final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth()); + final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth()) + + mThumbTextPadding * 2; + mThumbWidth = Math.max(maxTextWidth, mThumbDrawable.getIntrinsicWidth()); + final int switchWidth = Math.max(mSwitchMinWidth, - maxTextWidth * 2 + mThumbTextPadding * 4 + mTempRect.left + mTempRect.right); + 2 * mThumbWidth + mTempRect.left + mTempRect.right); final int switchHeight = Math.max(mTrackDrawable.getIntrinsicHeight(), mThumbDrawable.getIntrinsicHeight()); - mThumbWidth = maxTextWidth + mThumbTextPadding * 2; mSwitchWidth = switchWidth; mSwitchHeight = switchHeight; @@ -777,7 +806,7 @@ public class Switch extends CompoundButton { final Drawable trackDrawable = mTrackDrawable; final Drawable thumbDrawable = mThumbDrawable; - // Draw the switch + // Layout the track. final int switchLeft = mSwitchLeft; final int switchTop = mSwitchTop; final int switchRight = mSwitchRight; @@ -793,9 +822,10 @@ public class Switch extends CompoundButton { // Relies on mTempRect, MUST be called first! final int thumbPos = getThumbOffset(); + // Layout the thumb. thumbDrawable.getPadding(tempRect); - int thumbLeft = switchInnerLeft - tempRect.left + thumbPos; - int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + tempRect.right; + final int thumbLeft = switchInnerLeft - tempRect.left + thumbPos; + final int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + tempRect.right; thumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom); final Drawable background = getBackground(); @@ -805,20 +835,32 @@ public class Switch extends CompoundButton { super.onDraw(canvas); - trackDrawable.draw(canvas); + if (mSplitTrack) { + final Insets insets = thumbDrawable.getOpticalInsets(); + thumbDrawable.copyBounds(tempRect); + tempRect.left += insets.left; + tempRect.right -= insets.right; + + final int saveCount = canvas.save(); + canvas.clipRect(tempRect, Op.DIFFERENCE); + trackDrawable.draw(canvas); + canvas.restoreToCount(saveCount); + } else { + trackDrawable.draw(canvas); + } final int saveCount = canvas.save(); canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom); thumbDrawable.draw(canvas); - final int drawableState[] = getDrawableState(); - if (mTextColors != null) { - mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0)); - } - mTextPaint.drawableState = drawableState; - final Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout; if (switchText != null) { + final int drawableState[] = getDrawableState(); + if (mTextColors != null) { + mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0)); + } + mTextPaint.drawableState = drawableState; + final int left = (thumbLeft + thumbRight) / 2 - switchText.getWidth() / 2; final int top = (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2; canvas.translate(left, top); @@ -889,12 +931,16 @@ public class Switch extends CompoundButton { protected void drawableStateChanged() { super.drawableStateChanged(); - int[] myDrawableState = getDrawableState(); + final int[] myDrawableState = getDrawableState(); + + if (mThumbDrawable != null && mThumbDrawable.setState(myDrawableState)) { + // Handle changes to thumb width and height. + requestLayout(); + } - // Set the state of the Drawable - // Drawable may be null when checked state is set from XML, from super constructor - if (mThumbDrawable != null) mThumbDrawable.setState(myDrawableState); - if (mTrackDrawable != null) mTrackDrawable.setState(myDrawableState); + if (mTrackDrawable != null) { + mTrackDrawable.setState(myDrawableState); + } invalidate(); } diff --git a/core/java/com/android/internal/notification/DemoContactNotificationScorer.java b/core/java/com/android/internal/notification/DemoContactNotificationScorer.java deleted file mode 100644 index f48472447023..000000000000 --- a/core/java/com/android/internal/notification/DemoContactNotificationScorer.java +++ /dev/null @@ -1,188 +0,0 @@ -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -package com.android.internal.notification; - -import android.app.Notification; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.provider.ContactsContract; -import android.provider.Settings; -import android.text.SpannableString; -import android.util.Slog; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -/** - * This NotificationScorer bumps up the priority of notifications that contain references to the - * display names of starred contacts. The references it picks up are spannable strings which, in - * their entirety, match the display name of some starred contact. The magnitude of the bump ranges - * from 0 to 15 (assuming NOTIFICATION_PRIORITY_MULTIPLIER = 10) depending on the initial score, and - * the mapping is defined by priorityBumpMap. In a production version of this scorer, a notification - * extra will be used to specify contact identifiers. - */ - -public class DemoContactNotificationScorer implements NotificationScorer { - private static final String TAG = "DemoContactNotificationScorer"; - private static final boolean DBG = false; - - protected static final boolean ENABLE_CONTACT_SCORER = true; - private static final String SETTING_ENABLE_SCORER = "contact_scorer_enabled"; - protected boolean mEnabled; - - // see NotificationManagerService - private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; - - private Context mContext; - - private static final List<String> RELEVANT_KEYS_LIST = Arrays.asList( - Notification.EXTRA_INFO_TEXT, Notification.EXTRA_TEXT, Notification.EXTRA_TEXT_LINES, - Notification.EXTRA_SUB_TEXT, Notification.EXTRA_TITLE - ); - - private static final String[] PROJECTION = new String[] { - ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME - }; - - private static final Uri CONTACTS_URI = ContactsContract.Contacts.CONTENT_URI; - - private static List<String> extractSpannedStrings(CharSequence charSequence) { - if (charSequence == null) return Collections.emptyList(); - if (!(charSequence instanceof SpannableString)) { - return Arrays.asList(charSequence.toString()); - } - SpannableString spannableString = (SpannableString)charSequence; - // get all spans - Object[] ssArr = spannableString.getSpans(0, spannableString.length(), Object.class); - // spanned string sequences - ArrayList<String> sss = new ArrayList<String>(); - for (Object spanObj : ssArr) { - try { - sss.add(spannableString.subSequence(spannableString.getSpanStart(spanObj), - spannableString.getSpanEnd(spanObj)).toString()); - } catch(StringIndexOutOfBoundsException e) { - Slog.e(TAG, "Bad indices when extracting spanned subsequence", e); - } - } - return sss; - }; - - private static String getQuestionMarksInParens(int n) { - StringBuilder sb = new StringBuilder("("); - for (int i = 0; i < n; i++) { - if (sb.length() > 1) sb.append(','); - sb.append('?'); - } - sb.append(")"); - return sb.toString(); - } - - private boolean hasStarredContact(Bundle extras) { - if (extras == null) return false; - ArrayList<String> qStrings = new ArrayList<String>(); - // build list to query against the database for display names. - for (String rk: RELEVANT_KEYS_LIST) { - if (extras.get(rk) == null) { - continue; - } else if (extras.get(rk) instanceof CharSequence) { - qStrings.addAll(extractSpannedStrings((CharSequence) extras.get(rk))); - } else if (extras.get(rk) instanceof CharSequence[]) { - // this is intended for Notification.EXTRA_TEXT_LINES - for (CharSequence line: (CharSequence[]) extras.get(rk)){ - qStrings.addAll(extractSpannedStrings(line)); - } - } else { - Slog.w(TAG, "Strange, the extra " + rk + " is of unexpected type."); - } - } - if (qStrings.isEmpty()) return false; - String[] qStringsArr = qStrings.toArray(new String[qStrings.size()]); - - String selection = ContactsContract.Contacts.DISPLAY_NAME + " IN " - + getQuestionMarksInParens(qStringsArr.length) + " AND " - + ContactsContract.Contacts.STARRED+" ='1'"; - - Cursor c = null; - try { - c = mContext.getContentResolver().query( - CONTACTS_URI, PROJECTION, selection, qStringsArr, null); - if (c != null) return c.getCount() > 0; - } catch(Throwable t) { - Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); - } finally { - if (c != null) { - c.close(); - } - } - return false; - } - - private final static int clamp(int x, int low, int high) { - return (x < low) ? low : ((x > high) ? high : x); - } - - private static int priorityBumpMap(int incomingScore) { - //assumption is that scale runs from [-2*pm, 2*pm] - int pm = NOTIFICATION_PRIORITY_MULTIPLIER; - int theScore = incomingScore; - // enforce input in range - theScore = clamp(theScore, -2 * pm, 2 * pm); - if (theScore != incomingScore) return incomingScore; - // map -20 -> -20 and -10 -> 5 (when pm = 10) - if (theScore <= -pm) { - theScore += 1.5 * (theScore + 2 * pm); - } else { - // map 0 -> 10, 10 -> 15, 20 -> 20; - theScore += 0.5 * (2 * pm - theScore); - } - if (DBG) Slog.v(TAG, "priorityBumpMap: score before: " + incomingScore - + ", score after " + theScore + "."); - return theScore; - } - - @Override - public void initialize(Context context) { - if (DBG) Slog.v(TAG, "Initializing " + getClass().getSimpleName() + "."); - mContext = context; - mEnabled = ENABLE_CONTACT_SCORER && 1 == Settings.Global.getInt( - mContext.getContentResolver(), SETTING_ENABLE_SCORER, 0); - } - - @Override - public int getScore(Notification notification, int score) { - if (notification == null || !mEnabled) { - if (DBG) Slog.w(TAG, "empty notification? scorer disabled?"); - return score; - } - boolean hasStarredPriority = hasStarredContact(notification.extras); - - if (DBG) { - if (hasStarredPriority) { - Slog.v(TAG, "Notification references starred contact. Promoted!"); - } else { - Slog.v(TAG, "Notification lacks any starred contact reference. Not promoted!"); - } - } - if (hasStarredPriority) score = priorityBumpMap(score); - return score; - } -} - diff --git a/core/java/com/android/internal/notification/NotificationScorer.java b/core/java/com/android/internal/notification/NotificationScorer.java deleted file mode 100644 index 863c08cede93..000000000000 --- a/core/java/com/android/internal/notification/NotificationScorer.java +++ /dev/null @@ -1,27 +0,0 @@ -/* -* Copyright (C) 2013 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -package com.android.internal.notification; - -import android.app.Notification; -import android.content.Context; - -public interface NotificationScorer { - - public void initialize(Context context); - public int getScore(Notification notification, int score); - -} diff --git a/core/java/com/android/internal/notification/PeopleNotificationScorer.java b/core/java/com/android/internal/notification/PeopleNotificationScorer.java deleted file mode 100644 index efb5f6368a3f..000000000000 --- a/core/java/com/android/internal/notification/PeopleNotificationScorer.java +++ /dev/null @@ -1,227 +0,0 @@ -/* -* Copyright (C) 2014 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -package com.android.internal.notification; - -import android.app.Notification; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.provider.ContactsContract; -import android.provider.ContactsContract.Contacts; -import android.provider.Settings; -import android.text.TextUtils; -import android.util.LruCache; -import android.util.Slog; - -/** - * This {@link NotificationScorer} attempts to validate people references. - * Also elevates the priority of real people. - */ -public class PeopleNotificationScorer implements NotificationScorer { - private static final String TAG = "PeopleNotificationScorer"; - private static final boolean DBG = false; - - private static final boolean ENABLE_PEOPLE_SCORER = true; - private static final String SETTING_ENABLE_PEOPLE_SCORER = "people_scorer_enabled"; - private static final String[] LOOKUP_PROJECTION = { Contacts._ID }; - private static final int MAX_PEOPLE = 10; - private static final int PEOPLE_CACHE_SIZE = 200; - // see NotificationManagerService - private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; - - protected boolean mEnabled; - private Context mContext; - - // maps raw person handle to resolved person object - private LruCache<String, LookupResult> mPeopleCache; - - private float findMaxContactScore(Bundle extras) { - if (extras == null) { - return 0f; - } - - final String[] people = extras.getStringArray(Notification.EXTRA_PEOPLE); - if (people == null || people.length == 0) { - return 0f; - } - - float rank = 0f; - for (int personIdx = 0; personIdx < people.length && personIdx < MAX_PEOPLE; personIdx++) { - final String handle = people[personIdx]; - if (TextUtils.isEmpty(handle)) continue; - - LookupResult lookupResult = mPeopleCache.get(handle); - if (lookupResult == null || lookupResult.isExpired()) { - final Uri uri = Uri.parse(handle); - if ("tel".equals(uri.getScheme())) { - if (DBG) Slog.w(TAG, "checking telephone URI: " + handle); - lookupResult = lookupPhoneContact(handle, uri.getSchemeSpecificPart()); - } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) { - if (DBG) Slog.w(TAG, "checking lookup URI: " + handle); - lookupResult = resolveContactsUri(handle, uri); - } else { - if (DBG) Slog.w(TAG, "unsupported URI " + handle); - } - } else { - if (DBG) Slog.w(TAG, "using cached lookupResult: " + lookupResult.mId); - } - if (lookupResult != null) { - rank = Math.max(rank, lookupResult.getRank()); - } - } - return rank; - } - - private LookupResult lookupPhoneContact(final String handle, final String number) { - LookupResult lookupResult = null; - Cursor c = null; - try { - Uri numberUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, - Uri.encode(number)); - c = mContext.getContentResolver().query(numberUri, LOOKUP_PROJECTION, null, null, null); - if (c != null && c.getCount() > 0) { - c.moveToFirst(); - final int idIdx = c.getColumnIndex(Contacts._ID); - final int id = c.getInt(idIdx); - if (DBG) Slog.w(TAG, "is valid: " + id); - lookupResult = new LookupResult(id); - } - } catch(Throwable t) { - Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); - } finally { - if (c != null) { - c.close(); - } - } - if (lookupResult == null) { - lookupResult = new LookupResult(LookupResult.INVALID_ID); - } - mPeopleCache.put(handle, lookupResult); - return lookupResult; - } - - private LookupResult resolveContactsUri(String handle, final Uri personUri) { - LookupResult lookupResult = null; - Cursor c = null; - try { - c = mContext.getContentResolver().query(personUri, LOOKUP_PROJECTION, null, null, null); - if (c != null && c.getCount() > 0) { - c.moveToFirst(); - final int idIdx = c.getColumnIndex(Contacts._ID); - final int id = c.getInt(idIdx); - if (DBG) Slog.w(TAG, "is valid: " + id); - lookupResult = new LookupResult(id); - } - } catch(Throwable t) { - Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); - } finally { - if (c != null) { - c.close(); - } - } - if (lookupResult == null) { - lookupResult = new LookupResult(LookupResult.INVALID_ID); - } - mPeopleCache.put(handle, lookupResult); - return lookupResult; - } - - private final static int clamp(int x, int low, int high) { - return (x < low) ? low : ((x > high) ? high : x); - } - - // TODO: rework this function before shipping - private static int priorityBumpMap(int incomingScore) { - //assumption is that scale runs from [-2*pm, 2*pm] - int pm = NOTIFICATION_PRIORITY_MULTIPLIER; - int theScore = incomingScore; - // enforce input in range - theScore = clamp(theScore, -2 * pm, 2 * pm); - if (theScore != incomingScore) return incomingScore; - // map -20 -> -20 and -10 -> 5 (when pm = 10) - if (theScore <= -pm) { - theScore += 1.5 * (theScore + 2 * pm); - } else { - // map 0 -> 10, 10 -> 15, 20 -> 20; - theScore += 0.5 * (2 * pm - theScore); - } - if (DBG) Slog.v(TAG, "priorityBumpMap: score before: " + incomingScore - + ", score after " + theScore + "."); - return theScore; - } - - @Override - public void initialize(Context context) { - if (DBG) Slog.v(TAG, "Initializing " + getClass().getSimpleName() + "."); - mContext = context; - mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE); - mEnabled = ENABLE_PEOPLE_SCORER && 1 == Settings.Global.getInt( - mContext.getContentResolver(), SETTING_ENABLE_PEOPLE_SCORER, 0); - } - - @Override - public int getScore(Notification notification, int score) { - if (notification == null || !mEnabled) { - if (DBG) Slog.w(TAG, "empty notification? scorer disabled?"); - return score; - } - float contactScore = findMaxContactScore(notification.extras); - if (contactScore > 0f) { - if (DBG) Slog.v(TAG, "Notification references a real contact. Promoted!"); - score = priorityBumpMap(score); - } else { - if (DBG) Slog.v(TAG, "Notification lacks any valid contact reference. Not promoted!"); - } - return score; - } - - private static class LookupResult { - private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr - public static final int INVALID_ID = -1; - - private final long mExpireMillis; - private int mId; - - public LookupResult(int id) { - mId = id; - mExpireMillis = System.currentTimeMillis() + CONTACT_REFRESH_MILLIS; - } - - public boolean isExpired() { - return mExpireMillis < System.currentTimeMillis(); - } - - public boolean isInvalid() { - return mId == INVALID_ID || isExpired(); - } - - public float getRank() { - if (isInvalid()) { - return 0f; - } else { - return 1f; // TODO: finer grained score - } - } - - public LookupResult setId(int id) { - mId = id; - return this; - } - } -} - diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java index 52281d92fd7b..34f62bac29ea 100644 --- a/core/java/com/android/internal/util/AsyncChannel.java +++ b/core/java/com/android/internal/util/AsyncChannel.java @@ -450,6 +450,7 @@ public class AsyncChannel { public void disconnect() { if ((mConnection != null) && (mSrcContext != null)) { mSrcContext.unbindService(mConnection); + mConnection = null; } try { // Send the DISCONNECTED, although it may not be received @@ -463,10 +464,12 @@ public class AsyncChannel { // Tell source we're disconnected. if (mSrcHandler != null) { replyDisconnected(STATUS_SUCCESSFUL); + mSrcHandler = null; } // Unlink only when bindService isn't used if (mConnection == null && mDstMessenger != null && mDeathMonitor!= null) { mDstMessenger.getBinder().unlinkToDeath(mDeathMonitor, 0); + mDeathMonitor = null; } } diff --git a/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java b/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java new file mode 100644 index 000000000000..aec2b7e41649 --- /dev/null +++ b/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java @@ -0,0 +1,66 @@ +/* + * 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.internal.view.animation; + +import android.animation.TimeInterpolator; +import android.util.TimeUtils; +import android.view.Choreographer; + +/** + * Interpolator that builds a lookup table to use. This is a fallback for + * building a native interpolator from a TimeInterpolator that is not marked + * with {@link HasNativeInterpolator} + */ +@HasNativeInterpolator +public class FallbackLUTInterpolator implements NativeInterpolatorFactory { + + private final float mLut[]; + + /** + * Used to cache the float[] LUT for use across multiple native + * interpolator creation + */ + public FallbackLUTInterpolator(TimeInterpolator interpolator, int duration) { + mLut = createLUT(interpolator, duration); + } + + private static float[] createLUT(TimeInterpolator interpolator, int duration) { + long frameIntervalNanos = Choreographer.getInstance().getFrameIntervalNanos(); + int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS); + int numAnimFrames = (int) Math.ceil(duration / animIntervalMs); + float values[] = new float[numAnimFrames]; + float lastFrame = numAnimFrames - 1; + for (int i = 0; i < numAnimFrames; i++) { + float inValue = i / lastFrame; + values[i] = interpolator.getInterpolation(inValue); + } + return values; + } + + @Override + public long createNativeInterpolator() { + return NativeInterpolatorFactoryHelper.createLutInterpolator(mLut); + } + + /** + * Used to create a one-shot float[] LUT & native interpolator + */ + public static long createNativeInterpolator(TimeInterpolator interpolator, int duration) { + float[] lut = createLUT(interpolator, duration); + return NativeInterpolatorFactoryHelper.createLutInterpolator(lut); + } +} diff --git a/core/java/com/android/internal/view/animation/HasNativeInterpolator.java b/core/java/com/android/internal/view/animation/HasNativeInterpolator.java new file mode 100644 index 000000000000..48ea4da9a025 --- /dev/null +++ b/core/java/com/android/internal/view/animation/HasNativeInterpolator.java @@ -0,0 +1,37 @@ +/* + * 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.internal.view.animation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This is a class annotation that signals that it is safe to create + * a native interpolator counterpart via {@link NativeInterpolatorFactory} + * + * The idea here is to prevent subclasses of interpolators from being treated as a + * NativeInterpolatorFactory, and instead have them fall back to the LUT & LERP + * method like a custom interpolator. + * + * @hide + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +public @interface HasNativeInterpolator { +} diff --git a/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java b/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java new file mode 100644 index 000000000000..fcacd52ca398 --- /dev/null +++ b/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java @@ -0,0 +1,21 @@ +/* + * 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.internal.view.animation; + +public interface NativeInterpolatorFactory { + long createNativeInterpolator(); +} diff --git a/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java b/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java new file mode 100644 index 000000000000..2b2528084b9f --- /dev/null +++ b/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java @@ -0,0 +1,28 @@ +/* + * 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.internal.view.animation; + +/** + * Static utility class for constructing native interpolators to keep the + * JNI simpler + */ +public final class NativeInterpolatorFactoryHelper { + private NativeInterpolatorFactoryHelper() {} + + public static native long createAccelerateDecelerateInterpolator(); + public static native long createLutInterpolator(float[] values); +} diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 26f77b5de827..7dc639d552a1 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -156,7 +156,8 @@ LOCAL_SRC_FILES:= \ android_animation_PropertyValuesHolder.cpp \ com_android_internal_net_NetworkStatsFactory.cpp \ com_android_internal_os_Zygote.cpp \ - com_android_internal_util_VirtualRefBasePtr.cpp + com_android_internal_util_VirtualRefBasePtr.cpp \ + com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 9941cd9dfa41..01d8814ea8f8 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -131,6 +131,7 @@ extern int register_android_view_Surface(JNIEnv* env); extern int register_android_view_SurfaceControl(JNIEnv* env); extern int register_android_view_SurfaceSession(JNIEnv* env); extern int register_android_view_TextureView(JNIEnv* env); +extern int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv *env); extern int register_android_database_CursorWindow(JNIEnv* env); extern int register_android_database_SQLiteConnection(JNIEnv* env); extern int register_android_database_SQLiteGlobal(JNIEnv* env); @@ -1206,6 +1207,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_view_SurfaceControl), REG_JNI(register_android_view_SurfaceSession), REG_JNI(register_android_view_TextureView), + REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper), REG_JNI(register_com_google_android_gles_jni_EGLImpl), REG_JNI(register_com_google_android_gles_jni_GLImpl), REG_JNI(register_android_opengl_jni_EGL14), diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp index 4787d28847e3..5733f6046456 100644 --- a/core/jni/android_view_RenderNodeAnimator.cpp +++ b/core/jni/android_view_RenderNodeAnimator.cpp @@ -132,6 +132,17 @@ static void setDuration(JNIEnv* env, jobject clazz, jlong animatorPtr, jint dura animator->setDuration(duration); } +static jint getDuration(JNIEnv* env, jobject clazz, jlong animatorPtr) { + BaseAnimator* animator = reinterpret_cast<BaseAnimator*>(animatorPtr); + return static_cast<jint>(animator->duration()); +} + +static void setInterpolator(JNIEnv* env, jobject clazz, jlong animatorPtr, jlong interpolatorPtr) { + BaseAnimator* animator = reinterpret_cast<BaseAnimator*>(animatorPtr); + Interpolator* interpolator = reinterpret_cast<Interpolator*>(interpolatorPtr); + animator->setInterpolator(interpolator); +} + #endif // ---------------------------------------------------------------------------- @@ -146,6 +157,8 @@ static JNINativeMethod gMethods[] = { { "nCreateCanvasPropertyFloatAnimator", "(Ljava/lang/ref/WeakReference;JIF)J", (void*) createCanvasPropertyFloatAnimator }, { "nCreateCanvasPropertyPaintAnimator", "(Ljava/lang/ref/WeakReference;JIIF)J", (void*) createCanvasPropertyPaintAnimator }, { "nSetDuration", "(JI)V", (void*) setDuration }, + { "nGetDuration", "(J)I", (void*) getDuration }, + { "nSetInterpolator", "(JJ)V", (void*) setInterpolator }, #endif }; diff --git a/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp b/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp new file mode 100644 index 000000000000..c2d3dd1568b9 --- /dev/null +++ b/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include "jni.h" +#include <nativehelper/JNIHelp.h> +#include <android_runtime/AndroidRuntime.h> + +#include <Interpolator.h> + +namespace android { + +using namespace uirenderer; + +#ifdef USE_OPENGL_RENDERER + +static jlong createAccelerateDecelerateInterpolator(JNIEnv* env, jobject clazz) { + return reinterpret_cast<jlong>(new AccelerateDecelerateInterpolator()); +} + +static jlong createLutInterpolator(JNIEnv* env, jobject clazz, jfloatArray jlut) { + jsize len = env->GetArrayLength(jlut); + if (len <= 0) { + return 0; + } + float* lut = new float[len]; + env->GetFloatArrayRegion(jlut, 0, len, lut); + return reinterpret_cast<jlong>(new LUTInterpolator(lut, len)); +} + +#endif + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "com/android/internal/view/animation/NativeInterpolatorFactoryHelper"; + +static JNINativeMethod gMethods[] = { +#ifdef USE_OPENGL_RENDERER + { "createAccelerateDecelerateInterpolator", "()J", (void*) createAccelerateDecelerateInterpolator }, + { "createLutInterpolator", "([F)J", (void*) createLutInterpolator }, +#endif +}; + +int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv* env) { + return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); +} + + +} // namespace android diff --git a/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png Binary files differindex f1023eafb9bd..5a9952823c10 100644 --- a/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png +++ b/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png Binary files differindex 15ceeee974c0..79de664548c8 100644 --- a/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png +++ b/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png diff --git a/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png Binary files differindex 90b1498c8de8..73e8f1c99fd9 100644 --- a/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png +++ b/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png diff --git a/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png Binary files differindex b535758cb966..ff6affe85df4 100644 --- a/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png +++ b/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png diff --git a/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png Binary files differindex 1833704179e7..e40cba8999c2 100644 --- a/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png +++ b/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png Binary files differindex e64d3f2958da..437a3e3dde7c 100644 --- a/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png +++ b/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png diff --git a/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png Binary files differindex ffd6c39ea1e5..8949b52955ba 100644 --- a/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png +++ b/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png diff --git a/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png Binary files differindex 15faff081b89..d7276838f108 100644 --- a/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png +++ b/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml index 8eb00fa5cdb6..668cff7cd4f2 100644 --- a/core/res/res/drawable-nodpi/platlogo.xml +++ b/core/res/res/drawable-nodpi/platlogo.xml @@ -18,22 +18,21 @@ <viewport android:viewportHeight="25" android:viewportWidth="25" /> - <group> - <path - android:name="shadow" - android:pathData="m12,2.5 a11,11 0 1,0 1,0 - M6.5,7.5 - l5,0 l0,7 l7,0 l0,5 l-12,0 z" - android:fill="#40000000" - /> - <path - android:name="circle-L-ranch" - android:pathData="m12,1.5 a11,11 0 1,0 1,0 - M6.5,6.5 - l5,0 l0,7 l7,0 l0,5 l-12,0 z" - android:fill="#FFFFFF40" - /> - </group> + <path + android:name="shadow" + android:pathData="m12,2.5 a11,11 0 1,0 1,0 + M6.5,7.5 + l5,0 l0,7 l7,0 l0,5 l-12,0 z" + android:fill="#40000000" + /> + <path + android:name="circle-L-ranch" + android:pathData="m12,1.5 a11,11 0 1,0 1,0 + M6.5,6.5 + l5,0 l0,7 l7,0 l0,5 l-12,0 z" + android:fill="#FFFFFF40" + /> + </vector> diff --git a/core/res/res/drawable-nodpi/stat_sys_adb.xml b/core/res/res/drawable-nodpi/stat_sys_adb.xml index 49028b4d5564..b8ddb77bd2df 100644 --- a/core/res/res/drawable-nodpi/stat_sys_adb.xml +++ b/core/res/res/drawable-nodpi/stat_sys_adb.xml @@ -18,13 +18,13 @@ <viewport android:viewportHeight="25" android:viewportWidth="25" /> - <group> - <path - android:name="adb" - android:pathData="m3,3l8,0l0,11l11,0l0,8l-19,0z" - android:fill="#FFFFFFFF" - /> - </group> + + <path + android:name="adb" + android:pathData="m3,3l8,0l0,11l11,0l0,8l-19,0z" + android:fill="#FFFFFFFF" + /> + </vector> diff --git a/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png Binary files differindex ad72f069e99b..729e0bfa01a4 100644 --- a/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png +++ b/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png Binary files differindex 7aceed16f9a1..d018a7cf1a2a 100644 --- a/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png +++ b/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png diff --git a/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png Binary files differindex 309b528f8eb0..a7a972c2af2e 100644 --- a/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png +++ b/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png diff --git a/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png Binary files differindex 139795e68bde..dd8910b5b8d8 100644 --- a/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png +++ b/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png Binary files differindex c11b0aefbf02..a2b5716f6d32 100644 --- a/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png +++ b/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png Binary files differindex cde797ec62db..caabc2c1bc21 100644 --- a/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png +++ b/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png diff --git a/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png Binary files differindex 9e234af8734c..8d79a13e2002 100644 --- a/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png +++ b/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png diff --git a/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png Binary files differindex b371eab4a908..e0e4ef9c5c4a 100644 --- a/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png +++ b/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png diff --git a/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml b/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml index d172b050d46e..f82fe7a1d4c2 100644 --- a/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml +++ b/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml @@ -16,22 +16,26 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_enabled="false"> - <bitmap android:src="@drawable/scrubber_track_qntm_alpha" + <nine-patch android:src="@drawable/scrubber_track_qntm_alpha" android:tint="?attr/colorControlNormal" /> </item> <item> <layer-list> <item android:id="@id/background"> - <bitmap android:src="@drawable/scrubber_track_qntm_alpha" + <nine-patch android:src="@drawable/scrubber_track_qntm_alpha" android:tint="?attr/colorControlNormal" /> </item> <item android:id="@id/secondaryProgress"> - <bitmap android:src="@drawable/scrubber_primary_qntm_alpha" - android:tint="?attr/colorControlNormal" /> + <scale android:scaleWidth="100%"> + <nine-patch android:src="@drawable/scrubber_primary_qntm_alpha" + android:tint="?attr/colorControlNormal" /> + </scale> </item> <item android:id="@id/progress"> - <bitmap android:src="@drawable/scrubber_primary_qntm_alpha" - android:tint="?attr/colorControlActivated" /> + <scale android:scaleWidth="100%"> + <nine-patch android:src="@drawable/scrubber_primary_qntm_alpha" + android:tint="?attr/colorControlActivated" /> + </scale> </item> </layer-list> </item> diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml index 4d6f0a318516..bee6c21c7724 100644 --- a/core/res/res/values-km-rKH/strings.xml +++ b/core/res/res/values-km-rKH/strings.xml @@ -68,7 +68,7 @@ </plurals> <string name="imei" msgid="2625429890869005782">"IMEI"</string> <string name="meid" msgid="4841221237681254195">"MEID"</string> - <string name="ClipMmi" msgid="6952821216480289285">"លេខសម្គាល់អ្នកហៅចូល"</string> + <string name="ClipMmi" msgid="6952821216480289285">"លេខសម្គាល់អ្នកហៅចូល"</string> <string name="ClirMmi" msgid="7784673673446833091">"លេខសម្គាល់អ្នកហៅចេញ"</string> <string name="CfMmi" msgid="5123218989141573515">"បញ្ជូនការហៅបន្ត"</string> <string name="CwMmi" msgid="9129678056795016867">"រង់ចាំការហៅ"</string> @@ -125,7 +125,7 @@ <string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> ៖ មិនបានបញ្ជូនបន្ត"</string> <string name="fcComplete" msgid="3118848230966886575">"កូដលក្ខណៈពេញលេញ។"</string> <string name="fcError" msgid="3327560126588500777">"បញ្ហាការតភ្ជាប់ ឬកូដលក្ខណៈមិនត្រឹមត្រូវ។"</string> - <string name="httpErrorOk" msgid="1191919378083472204">"យល់ព្រម"</string> + <string name="httpErrorOk" msgid="1191919378083472204">"យល់ព្រម"</string> <string name="httpError" msgid="7956392511146698522">"មានកំហុសបណ្ដាញ។"</string> <string name="httpErrorLookup" msgid="4711687456111963163">"រកមិនឃើញ URL ។"</string> <string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"គ្រោងការណ៍ផ្ទៀងផ្ទាត់តំបន់បណ្ដាញមិនត្រូវបានគាំទ្រ។"</string> @@ -183,7 +183,7 @@ <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"បើកសំឡេង"</string> <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ពេលជិះយន្តហោះ"</string> <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"បានបើករបៀបពេលជិះយន្តហោះ"</string> - <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បានបិទរបៀបពេលជិះយន្តហោះ"</string> + <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បានបិទរបៀបពេលជិះយន្តហោះ"</string> <string name="global_action_settings" msgid="1756531602592545966">"ការកំណត់"</string> <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string> <string name="safeMode" msgid="2788228061547930246">"របៀបសុវត្ថិភាព"</string> @@ -195,7 +195,7 @@ <string name="permgrouplab_messages" msgid="7521249148445456662">"សាររបស់អ្នក"</string> <string name="permgroupdesc_messages" msgid="7821999071003699236">"អាន និងសរសេរសារ SMS, អ៊ីមែល និងសារផ្សេងៗទៀតរបស់អ្នក។"</string> <string name="permgrouplab_personalInfo" msgid="3519163141070533474">"ព័ត៌មានផ្ទាល់ខ្លួនរបស់អ្នក"</string> - <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"ចូលដំណើរការព័ត៌មានដោយផ្ទាល់អំពីអ្នក ដែលបានរក្សាទុកក្នុងកាតទំនាក់ទំនងរបស់អ្នក។"</string> + <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"ចូលដំណើរការព័ត៌មានដោយផ្ទាល់អំពីអ្នក ដែលបានរក្សាទុកក្នុងកាតទំនាក់ទំនងរបស់អ្នក។"</string> <string name="permgrouplab_socialInfo" msgid="5799096623412043791">"ព័ត៌មានសង្គមរបស់អ្នក"</string> <string name="permgroupdesc_socialInfo" msgid="7129842457611643493">"ចូលដំណើរការព័ត៌មានដោយផ្ទាល់អំពីទំនាក់ទំនង និងការភ្ជាប់សង្គមរបស់អ្នក។"</string> <string name="permgrouplab_location" msgid="635149742436692049">"ទីតាំងរបស់អ្នក"</string> @@ -386,7 +386,7 @@ <string name="permdesc_readInputState" msgid="8387754901688728043">"ឲ្យកម្មវិធីមើលគ្រាប់ចុចដែលអ្នកចុចពេលមានអន្តរកម្មជាមួយកម្មវិធីផ្សេង (ដូចជា បញ្ចូលពាក្យសម្ងាត់)។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> <string name="permlab_bindInputMethod" msgid="3360064620230515776">"ចងទៅវិធីសាស្ត្របញ្ចូល"</string> <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃវិធីសាស្ត្របញ្ចូល។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> - <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ចងសេវាកម្មភាពមធ្យោបាយងាយស្រួល"</string> + <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ចងសេវាកម្មភាពមធ្យោបាយងាយស្រួល"</string> <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃសេវាកម្មភាពងាយស្រួល។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> <string name="permlab_bindPrintService" msgid="8462815179572748761">"ចងសេវាកម្មបោះពុម្ព"</string> <string name="permdesc_bindPrintService" msgid="7960067623209111135">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃសេវាកម្មធាតុក្រាហ្វិក។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> @@ -404,7 +404,7 @@ <string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅចំណុចប្រទាក់កម្រិតកំពូលរបស់សេវាកម្មអន្តរកម្មសំឡេង។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"ភ្ជាប់ទៅការបង្ហាញពីចម្ងាយ"</string> <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅចំណុចប្រទាក់កម្រិតកំពូលនៃការបង្ហាញពីចម្ងាយ។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> - <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចងសេវាកម្មធាតុក្រាហ្វិក"</string> + <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចងសេវាកម្មធាតុក្រាហ្វិក"</string> <string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃសេវាកម្មធាតុក្រាហ្វិក។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"ភ្ជាប់ទៅសេវាកម្មក្រុមហ៊ុនផ្ដល់ច្រក"</string> <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅក្រុមហ៊ុនផ្ដល់ច្រកដែលបានចុះឈ្មោះ។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> @@ -412,7 +412,7 @@ <string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"ឲ្យម្ចាស់ផ្ញើគោលបំណងទៅអ្នកគ្រប់គ្រងឧបករណ៍។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> <string name="permlab_bindTvInput" msgid="5601264742478168987">"ភ្ជាប់ទៅការបញ្ចូលទូរទស្សន៍"</string> <string name="permdesc_bindTvInput" msgid="2371008331852001924">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅចំណុចប្រទាក់កម្រិតខ្ពស់នៃការបញ្ចូលទូរទស្សន៍។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> - <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"បន្ថែម ឬលុបកម្មវិធីគ្រប់គ្រងឧបករណ៍"</string> + <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"បន្ថែម ឬលុបកម្មវិធីគ្រប់គ្រងឧបករណ៍"</string> <string name="permdesc_manageDeviceAdmins" msgid="5025608167709942485">"អនុញ្ញាតឲ្យម្ចាស់បន្ថែម ឬលុបកម្មវិធីគ្រប់គ្រងឧបករណ៍សកម្មចេញ។ មិនគួរប្រើសម្រាប់កម្មវិធីធម្មតាទេ។"</string> <string name="permlab_setOrientation" msgid="3365947717163866844">"ប្ដូរទិសអេក្រង់"</string> <string name="permdesc_setOrientation" msgid="3046126619316671476">"ឲ្យកម្មវិធីប្ដូរការបង្វិលអេក្រង់នៅពេលណាមួយ។ មិនចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string> @@ -424,9 +424,9 @@ <string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"ឲ្យកម្មវិធីស្នើសញ្ញាដែលបានផ្ដល់ត្រូវផ្ញើទៅដំណើរការស្ថិតស្ថេរទាំងអស់។"</string> <string name="permlab_persistentActivity" msgid="8841113627955563938">"ធ្វើឲ្យកម្មវិធីដំណើរការជានិច្ច"</string> <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"ឲ្យកម្មវិធីធ្វើជាផ្នែកស្ថិតស្ថេរដោយខ្លួនឯងក្នុងអង្គចងចាំ។ វាអាចកំណត់អង្គចងចាំដែលអាចប្រើបានចំពោះកម្មវិធីផ្សេងៗ ដោយធ្វើឲ្យកុំព្យូទ័របន្ទះយឺត។"</string> - <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ឲ្យកម្មវិធី ធ្វើជាផ្នែកអចិន្ត្រៃយ៍នៃខ្លួនក្នុងអង្គចងចាំ។ វាអាចកម្រិតអង្គចងចាំអាចប្រើបាន ដើម្បីធ្វើឲ្យកម្មវិធីផ្សេងធ្វើឲ្យទូរស័ព្ទរបស់អ្នកយឺត។"</string> + <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ឲ្យកម្មវិធី ធ្វើជាផ្នែកអចិន្ត្រៃយ៍នៃខ្លួនក្នុងអង្គចងចាំ។ វាអាចកម្រិតអង្គចងចាំអាចប្រើបាន ដើម្បីធ្វើឲ្យកម្មវិធីផ្សេងធ្វើឲ្យទូរស័ព្ទរបស់អ្នកយឺត។"</string> <string name="permlab_deletePackages" msgid="184385129537705938">"លុបកម្មវិធី"</string> - <string name="permdesc_deletePackages" msgid="7411480275167205081">"ឲ្យកម្មវិធីលុបកញ្ចប់ Android ។ កម្មវិធីព្យាបាទអាចប្រើវា ដើម្បីលុបកម្មវិធីសំខាន់ៗ។"</string> + <string name="permdesc_deletePackages" msgid="7411480275167205081">"ឲ្យកម្មវិធីលុបកញ្ចប់ Android ។ កម្មវិធីព្យាបាទអាចប្រើវា ដើម្បីលុបកម្មវិធីសំខាន់ៗ។ "</string> <string name="permlab_clearAppUserData" msgid="274109191845842756">"លុបទិន្នន័យរបស់កម្មវិធីផ្សេង"</string> <string name="permdesc_clearAppUserData" msgid="4625323684125459488">"ឲ្យកម្មវិធីសម្អាតទិន្នន័យអ្នកប្រើ។"</string> <string name="permlab_deleteCacheFiles" msgid="3128665571837408675">"លុបឃ្លាំងសម្ងាត់កម្មវិធីផ្សេងៗ"</string> @@ -477,7 +477,7 @@ <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"ឲ្យកម្មវិធីកែទិន្នន័យអំពីទំនាក់ទំនងរបស់អ្នកដែលបានរក្សាទុកក្នុងកុំព្យូទ័របន្ទះ រួមមានប្រេកង់ដែលអ្នកបានហៅ អ៊ីមែល ឬទាក់ទងតាមវិធីផ្សេងៗជាមួយទំនាក់ទំនងជាក់លាក់។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីលុបទិន្នន័យទំនាក់ទំនងរបស់អ្នក។"</string> <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"ឲ្យកម្មវិធីកែទិន្នន័យអំពីទំនាក់ទំនងរបស់អ្នកដែលបានរក្សាទុកក្នុងទូរស័ព្ទរបស់អ្នក រួមមានប្រេកង់ដែលអ្នកបានហៅ អ៊ីមែល ឬបានទាក់ទងតាមវិធីផ្សេងៗជាមួយទំនាក់ទំនាក់ជាក់លាក់។ សិទ្ធិនេះឲ្យកម្មវិធីលុបទិន្នន័យទំនាក់ទំនង។"</string> <string name="permlab_readCallLog" msgid="3478133184624102739">"អានកំណត់ហេតុហៅ"</string> - <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"ឲ្យកម្មវិធីអានបញ្ជីហៅកុំព្យូទ័របន្ទះរបស់អ្នក រួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីរក្សាទុកទិន្នន័យបញ្ជីហៅរបស់អ្នក ហើយកម្មវិធីព្យាបាទអាចចែករំលែកទិន្នន័យបញ្ជីហៅដោយមិនឲ្យអ្នកដឹង។"</string> + <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"ឲ្យកម្មវិធីអានបញ្ជីហៅកុំព្យូទ័របន្ទះរបស់អ្នក រួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីរក្សាទុកទិន្នន័យបញ្ជីហៅរបស់អ្នក ហើយកម្មវិធីព្យាបាទអាចចែករំលែកទិន្នន័យបញ្ជីហៅដោយមិនឲ្យអ្នកដឹង។"</string> <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"ឲ្យកម្មវិធីអានបញ្ជីហៅទូរស័ព្ទរបស់អ្នក រួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីរក្សាទុកទិន្នន័យបញ្ជីហៅរបស់អ្នក ហើយកម្មវិធីព្យាបាទអាចចែករំលែកទិន្នន័យបញ្ជីហៅដោយមិនឲ្យអ្នកដឹង។"</string> <string name="permlab_writeCallLog" msgid="8552045664743499354">"សរសេរបញ្ជីហៅ"</string> <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ឲ្យកម្មវិធីកែបញ្ជីហៅកុំព្យូទ័របន្ទះរបស់អ្នករួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។កម្មវិធីព្យាបាទអាចប្រើវា ដើម្បីលុប ឬកែបញ្ជីហៅរបស់អ្នក។"</string> @@ -611,7 +611,7 @@ <string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"ឲ្យកម្មវិធីកំណត់ជំនួយទំហំផ្ទាំងរូបភាពប្រព័ន្ធ។"</string> <string name="permlab_masterClear" msgid="2315750423139697397">"កំណត់ប្រព័ន្ធទៅលំនាំដើមរោងចក្រឡើងវិញ"</string> <string name="permdesc_masterClear" msgid="3665380492633910226">"ឲ្យកម្មវិធីកំណត់ប្រព័ន្ធដូចការកំណត់ចេញពីរោងចក្រឡើងវិញពេញលេញ ដោយលុបទិន្នន័យ ការកំណត់រចនាសម្ព័ន្ធ និងកម្មវិធីបានដំឡើង។"</string> - <string name="permlab_setTime" msgid="2021614829591775646">"កំណត់ម៉ោង"</string> + <string name="permlab_setTime" msgid="2021614829591775646">"កំណត់ម៉ោង"</string> <string name="permdesc_setTime" product="tablet" msgid="1896341438151152881">"ឲ្យកម្មវិធីប្ដូរម៉ោងកុំព្យូទ័របន្ទះ។"</string> <string name="permdesc_setTime" product="default" msgid="1855702730738020">"ឲ្យកម្មវិធីប្ដូរម៉ោងទូរស័ព្ទ។"</string> <string name="permlab_setTimeZone" msgid="2945079801013077340">"កំណត់តំបន់ពេលវេលា"</string> @@ -777,7 +777,7 @@ <string-array name="organizationTypes"> <item msgid="7546335612189115615">"កន្លែងធ្វើការ"</item> <item msgid="4378074129049520373">"ផ្សេងៗ"</item> - <item msgid="3455047468583965104">"តាមតម្រូវការ"</item> + <item msgid="3455047468583965104">"តាមតម្រូវការ"</item> </string-array> <string-array name="imProtocols"> <item msgid="8595261363518459565">"AIM"</item> @@ -793,7 +793,7 @@ <string name="phoneTypeHome" msgid="2570923463033985887">"ផ្ទះ"</string> <string name="phoneTypeMobile" msgid="6501463557754751037">"ចល័ត"</string> <string name="phoneTypeWork" msgid="8863939667059911633">"កន្លែងធ្វើការ"</string> - <string name="phoneTypeFaxWork" msgid="3517792160008890912">"ទូរសារកន្លែងធ្វើការ"</string> + <string name="phoneTypeFaxWork" msgid="3517792160008890912">"ទូរសារកន្លែងធ្វើការ"</string> <string name="phoneTypeFaxHome" msgid="2067265972322971467">"ទូរសារផ្ទះ"</string> <string name="phoneTypePager" msgid="7582359955394921732">"ភេយ័រ"</string> <string name="phoneTypeOther" msgid="1544425847868765990">"ផ្សេងៗ"</string> @@ -918,7 +918,7 @@ <string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"ព្យាយាមលំនាំច្រើនពេក"</string> <string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"ដើម្បីដោះសោ ចូលគណនី Google របស់អ្នក។"</string> <string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"ឈ្មោះអ្នកប្រើ (អ៊ីមែល)"</string> - <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"ពាក្យសម្ងាត់"</string> + <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"ពាក្យសម្ងាត់"</string> <string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"ចូល"</string> <string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"ឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់មិនត្រឹមត្រូវ។"</string> <string name="lockscreen_glogin_account_recovery_hint" msgid="1696924763690379073">"ភ្លេចឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់របស់អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string> @@ -963,7 +963,7 @@ <string name="factorytest_failed" msgid="5410270329114212041">"បានបរាជ័យក្នុងការសាកល្បងរោងចក្រ"</string> <string name="factorytest_not_system" msgid="4435201656767276723">"សកម្មភាព FACTORY_TEST ត្រូវបានគាំទ្រសម្រាប់តែកញ្ចប់បានដំឡើងក្នុង /system/app."</string> <string name="factorytest_no_action" msgid="872991874799998561">"រកមិនឃើញកញ្ចប់ដែលផ្ដល់សកម្មភាព FACTORY_TEST ។"</string> - <string name="factorytest_reboot" msgid="6320168203050791643">"ចាប់ផ្ដើមឡើងវិញ"</string> + <string name="factorytest_reboot" msgid="6320168203050791643">"ចាប់ផ្ដើមឡើងវិញ"</string> <string name="js_dialog_title" msgid="1987483977834603872">"ទំព័រមានចំណងជើង \"<xliff:g id="TITLE">%s</xliff:g>\" សរសេរ៖"</string> <string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string> <string name="js_dialog_before_unload_title" msgid="2619376555525116593">"បញ្ជាក់ការរុករក"</string> @@ -1021,7 +1021,7 @@ <string name="prepend_shortcut_label" msgid="2572214461676015642">"ម៉ឺនុយ +"</string> <string name="menu_space_shortcut_label" msgid="2410328639272162537">"ដកឃ្លា"</string> <string name="menu_enter_shortcut_label" msgid="2743362785111309668">"enter"</string> - <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"លុប"</string> + <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"លុប"</string> <string name="search_go" msgid="8298016669822141719">"ស្វែងរក"</string> <string name="searchview_description_search" msgid="6749826639098512120">"ស្វែងរក"</string> <string name="searchview_description_query" msgid="5911778593125355124">"ស្វែងរកសំណួរ"</string> @@ -1105,18 +1105,18 @@ <string name="preposition_for_date" msgid="9093949757757445117">"នៅ <xliff:g id="DATE">%s</xliff:g>"</string> <string name="preposition_for_time" msgid="5506831244263083793">"នៅម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string> <string name="preposition_for_year" msgid="5040395640711867177">"ក្នុងឆ្នាំ <xliff:g id="YEAR">%s</xliff:g>"</string> - <string name="day" msgid="8144195776058119424">"ថ្ងៃ"</string> + <string name="day" msgid="8144195776058119424">"ថ្ងៃ"</string> <string name="days" msgid="4774547661021344602">"ថ្ងៃ"</string> <string name="hour" msgid="2126771916426189481">"ម៉ោង"</string> <string name="hours" msgid="894424005266852993">"ម៉ោង"</string> - <string name="minute" msgid="9148878657703769868">"នាទី"</string> + <string name="minute" msgid="9148878657703769868">"នាទី"</string> <string name="minutes" msgid="5646001005827034509">"នាទី"</string> - <string name="second" msgid="3184235808021478">"វិនាទី"</string> + <string name="second" msgid="3184235808021478">"វិនាទី"</string> <string name="seconds" msgid="3161515347216589235">"វិនាទី"</string> - <string name="week" msgid="5617961537173061583">"សប្ដាហ៍"</string> - <string name="weeks" msgid="6509623834583944518">"សប្ដាហ៍"</string> - <string name="year" msgid="4001118221013892076">"ឆ្នាំ"</string> - <string name="years" msgid="6881577717993213522">"ឆ្នាំ"</string> + <string name="week" msgid="5617961537173061583">"សប្ដាហ៍"</string> + <string name="weeks" msgid="6509623834583944518">"សប្ដាហ៍"</string> + <string name="year" msgid="4001118221013892076">"ឆ្នាំ"</string> + <string name="years" msgid="6881577717993213522">"ឆ្នាំ"</string> <plurals name="duration_seconds"> <item quantity="one" msgid="6962015528372969481">"1 វិនាទី"</item> <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> វិនាទី"</item> @@ -1132,12 +1132,12 @@ <string name="VideoView_error_title" msgid="3534509135438353077">"បញ្ហាវីដេអូ"</string> <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"វីដេអូនេះមិនត្រឹមត្រូវសម្រាប់ចរន្តចូលឧបករណ៍នេះ។"</string> <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"មិនអាចចាក់វីដេអូនេះ។"</string> - <string name="VideoView_error_button" msgid="2822238215100679592">"យល់ព្រម"</string> + <string name="VideoView_error_button" msgid="2822238215100679592">"យល់ព្រម"</string> <string name="relative_time" msgid="1818557177829411417">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="noon" msgid="7245353528818587908">"រសៀល"</string> <string name="Noon" msgid="3342127745230013127">"រសៀល"</string> <string name="midnight" msgid="7166259508850457595">"កណ្ដាលអធ្រាត្រ"</string> - <string name="Midnight" msgid="5630806906897892201">"កណ្ដាលអធ្រាត្រ"</string> + <string name="Midnight" msgid="5630806906897892201">"កណ្ដាលអធ្រាត្រ"</string> <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string> <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> <string name="selectAll" msgid="6876518925844129331">"ជ្រើសទាំងអស់"</string> @@ -1154,13 +1154,13 @@ <string name="inputMethod" msgid="1653630062304567879">"វិធីសាស្ត្របញ្ចូល"</string> <string name="editTextMenuTitle" msgid="4909135564941815494">"សកម្មភាពអត្ថបទ"</string> <string name="low_internal_storage_view_title" msgid="5576272496365684834">"អស់ទំហំផ្ទុក"</string> - <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារប្រព័ន្ធមួយចំនួនអាចមិនដំណើរការ"</string> + <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារប្រព័ន្ធមួយចំនួនអាចមិនដំណើរការ"</string> <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងដំណើរការ"</string> <string name="app_running_notification_text" msgid="4653586947747330058">"ប៉ះ ដើម្បីមើលព័ត៌មានបន្ថែម ឬបញ្ឈប់កម្មវិធី។"</string> - <string name="ok" msgid="5970060430562524910">"យល់ព្រម"</string> - <string name="cancel" msgid="6442560571259935130">"បោះបង់"</string> - <string name="yes" msgid="5362982303337969312">"យល់ព្រម"</string> - <string name="no" msgid="5141531044935541497">"បោះបង់"</string> + <string name="ok" msgid="5970060430562524910">"យល់ព្រម"</string> + <string name="cancel" msgid="6442560571259935130">"បោះបង់"</string> + <string name="yes" msgid="5362982303337969312">"យល់ព្រម"</string> + <string name="no" msgid="5141531044935541497">"បោះបង់"</string> <string name="dialog_alert_title" msgid="2049658708609043103">"ប្រយ័ត្ន"</string> <string name="loading" msgid="7933681260296021180">"កំពុងផ្ទុក..."</string> <string name="capital_on" msgid="1544682755514494298">"បើក"</string> @@ -1169,7 +1169,7 @@ <string name="whichHomeApplication" msgid="4616420172727326782">"ជ្រើសកម្មវិធីដើម"</string> <string name="alwaysUse" msgid="4583018368000610438">"ប្រើតាមលំនាំដើមសម្រាប់សកម្មភាពនេះ។"</string> <string name="clearDefaultHintMsg" msgid="3252584689512077257">"សម្អាតលំនាំដើមក្នុងការកំណត់ប្រព័ន្ធ > កម្មវិធី > ទាញយក។"</string> - <string name="chooseActivity" msgid="7486876147751803333">"ជ្រើសសកម្មភាព"</string> + <string name="chooseActivity" msgid="7486876147751803333">"ជ្រើសសកម្មភាព"</string> <string name="chooseUsbActivity" msgid="6894748416073583509">"ជ្រើសកម្មវិធីសម្រាប់ឧបករណ៍យូអេសប៊ី"</string> <string name="noApplications" msgid="2991814273936504689">"គ្មានកម្មវិធីអាចអនុវត្តសកម្មភាពនេះ។"</string> <string name="aerr_title" msgid="1905800560317137752"></string> @@ -1180,7 +1180,7 @@ <string name="anr_activity_process" msgid="5776209883299089767">"សកម្មភាព <xliff:g id="ACTIVITY">%1$s</xliff:g> មិនឆ្លើយតប។\n\nតើអ្នកចង់បិទវា?"</string> <string name="anr_application_process" msgid="8941757607340481057">"<xliff:g id="APPLICATION">%1$s</xliff:g> មិនឆ្លើយតប។ តើអ្នកចង់បិទវា?"</string> <string name="anr_process" msgid="6513209874880517125">"ដំណើរការ <xliff:g id="PROCESS">%1$s</xliff:g> មិនឆ្លើយតប។ \n\nតើអ្នកចង់បិទវាឬ?"</string> - <string name="force_close" msgid="8346072094521265605">"យល់ព្រម"</string> + <string name="force_close" msgid="8346072094521265605">"យល់ព្រម"</string> <string name="report" msgid="4060218260984795706">"រាយការណ៍"</string> <string name="wait" msgid="7147118217226317732">"រង់ចាំ"</string> <string name="webpage_unresponsive" msgid="3272758351138122503">"ទំព័រក្លាយជាមិនឆ្លើយតប។\n\nតើអ្នកចង់បិទវា?"</string> @@ -1262,19 +1262,19 @@ <string name="sms_short_code_details" msgid="3492025719868078457"><font fgcolor="#ffffb060">"នេះអាចកាត់លុយ"</font>" លើគណនីចល័តរបស់អ្នក។"</string> <string name="sms_premium_short_code_details" msgid="5523826349105123687"><font fgcolor="#ffffb060">"វានឹងគិតថ្លៃសេវាកម្មលើគណនីចល័តរបស់អ្នក។"</font></string> <string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"ផ្ញើ"</string> - <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"បោះបង់"</string> + <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"បោះបង់"</string> <string name="sms_short_code_remember_choice" msgid="5289538592272218136">"ចងចាំជម្រើសរបស់ខ្ញុំ"</string> <string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"អ្នកអាចប្ដូរវាពេលក្រោយក្នុងការកំណត់ > កម្មវិធី"</string> <string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"អនុញ្ញាតជានិច្ច"</string> <string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"កុំអនុញ្ញាត"</string> <string name="sim_removed_title" msgid="6227712319223226185">"បានដកស៊ីមកាតចេញ"</string> - <string name="sim_removed_message" msgid="2333164559970958645">"បណ្ដាញចល័តនឹងប្រើលែងបានរហូតដល់អ្នកចាប់ផ្ដើមជាមួយស៊ីមកាតដែលបាបញ្ចូលត្រឹមត្រូវ។"</string> + <string name="sim_removed_message" msgid="2333164559970958645">"បណ្ដាញចល័តនឹងប្រើលែងបានរហូតដល់អ្នកចាប់ផ្ដើមជាមួយស៊ីមកាតដែលបាបញ្ចូលត្រឹមត្រូវ។"</string> <string name="sim_done_button" msgid="827949989369963775">"រួចរាល់"</string> <string name="sim_added_title" msgid="3719670512889674693">"បានបន្ថែមស៊ីមកាត"</string> <string name="sim_added_message" msgid="6599945301141050216">"ចាប់ផ្ដើមឧបករណ៍របស់អ្នកឡើងវិញ ដើម្បីចូលដំណើរការបណ្ដាញចល័ត។"</string> <string name="sim_restart_button" msgid="4722407842815232347">"ចាប់ផ្ដើមឡើងវិញ"</string> - <string name="time_picker_dialog_title" msgid="8349362623068819295">"កំណត់ម៉ោង"</string> - <string name="date_picker_dialog_title" msgid="5879450659453782278">"កំណត់កាលបរិច្ឆេទ"</string> + <string name="time_picker_dialog_title" msgid="8349362623068819295">"កំណត់ម៉ោង"</string> + <string name="date_picker_dialog_title" msgid="5879450659453782278">"កំណត់កាលបរិច្ឆេទ"</string> <string name="date_time_set" msgid="5777075614321087758">"កំណត់"</string> <string name="date_time_done" msgid="2507683751759308828">"រួចរាល់"</string> <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"ថ្មី៖ "</font></string> @@ -1352,7 +1352,7 @@ <string name="permdesc_copyProtectedData" msgid="4390697124288317831">"ឲ្យកម្មវិធីដកសេវាកម្មនៃកម្មវិធីផ្ទុកលំនាំដើម ដើម្បីចម្លងមាតិកា។ មិនសម្រាប់ប្រើដោយកម្មវិធីលំនាំដើម។"</string> <string name="permlab_route_media_output" msgid="1642024455750414694">"នាំផ្លូវលទ្ធផលមេឌៀ"</string> <string name="permdesc_route_media_output" msgid="4932818749547244346">"ឲ្យកម្មវិធីនាំផ្លូវលទ្ធផលមេឌៀទៅឧបករណ៍ខាងក្រៅផ្សេង។"</string> - <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ចូលដំណើរការឧបករណ៍ផ្ទុកសុវត្ថិភាព"</string> + <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ចូលដំណើរការឧបករណ៍ផ្ទុកសុវត្ថិភាព"</string> <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"ឲ្យកម្មវិធីចូលការផ្ទុកមានសុវត្ថិភាព keguard ។"</string> <string name="permlab_control_keyguard" msgid="172195184207828387">"ពិនិត្យការបង្ហាញ និងលាក់ការការពារ"</string> <string name="permdesc_control_keyguard" msgid="3043732290518629061">"ឲ្យកម្មវិធីគ្រប់គ្រង keguard ។"</string> @@ -1367,7 +1367,7 @@ <string name="ime_action_go" msgid="8320845651737369027">"ទៅ"</string> <string name="ime_action_search" msgid="658110271822807811">"ស្វែងរក"</string> <string name="ime_action_send" msgid="2316166556349314424">"ផ្ញើ"</string> - <string name="ime_action_next" msgid="3138843904009813834">"បន្ទាប់"</string> + <string name="ime_action_next" msgid="3138843904009813834">"បន្ទាប់"</string> <string name="ime_action_done" msgid="8971516117910934605">"រួចរាល់"</string> <string name="ime_action_previous" msgid="1443550039250105948">"មុន"</string> <string name="ime_action_default" msgid="2840921885558045721">"អនុវត្ត"</string> @@ -1376,7 +1376,7 @@ <string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"កម្មវិធីមួយ ឬច្រើនដូចខាងក្រោមស្នើសិទ្ធិ ដើម្បីចូលគណនីរបស់អ្នកឥឡូវ និងពេលអនាគត។"</string> <string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"តើអ្នកចង់អនុញ្ញាតសំណើនេះ?"</string> <string name="grant_permissions_header_text" msgid="6874497408201826708">"ស្នើចូល"</string> - <string name="allow" msgid="7225948811296386551">"អនុញ្ញាត"</string> + <string name="allow" msgid="7225948811296386551">"អនុញ្ញាត"</string> <string name="deny" msgid="2081879885755434506">"បដិសេធ"</string> <string name="permission_request_notification_title" msgid="6486759795926237907">"បានស្នើសិទ្ធិ"</string> <string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"បានស្នើសិទ្ធិ\nសម្រាប់គណនី <xliff:g id="ACCOUNT">%s</xliff:g> ។"</string> @@ -1399,12 +1399,12 @@ <string name="no_file_chosen" msgid="6363648562170759465">"គ្មានឯកសារបានជ្រើស"</string> <string name="reset" msgid="2448168080964209908">"កំណត់ឡើងវិញ"</string> <string name="submit" msgid="1602335572089911941">"ដាក់ស្នើ"</string> - <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"បានបើករបៀបរថយន្ត"</string> + <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"បានបើករបៀបរថយន្ត"</string> <string name="car_mode_disable_notification_message" msgid="8035230537563503262">"ប៉ះ ដើម្បីចេញពីរបៀបរថយន្ត។"</string> <string name="tethered_notification_title" msgid="3146694234398202601">"ភ្ជាប់ ឬហតស្ពតសកម្ម"</string> <string name="tethered_notification_message" msgid="6857031760103062982">"ប៉ះ ដើម្បីរៀបចំ។"</string> <string name="back_button_label" msgid="2300470004503343439">"ថយក្រោយ"</string> - <string name="next_button_label" msgid="1080555104677992408">"បន្ទាប់"</string> + <string name="next_button_label" msgid="1080555104677992408">"បន្ទាប់"</string> <string name="skip_button_label" msgid="1275362299471631819">"រំលង"</string> <string name="throttle_warning_notification_title" msgid="4890894267454867276">"ការប្រើទិន្នន័យចល័តខ្ពស់"</string> <string name="throttle_warning_notification_message" msgid="3340822228599337743">"ប៉ះ ដើម្បីស្វែងយល់បន្ថែមអំពីការប្រើទិន្នន័យចល័ត។"</string> @@ -1430,7 +1430,7 @@ <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"ឧបករណ៍ផ្ទុកយូអេសប៊ីបច្ចុប្បន្នកំពុងប្រើដោយកុំព្យូទ័រ។"</string> <string name="media_shared" product="default" msgid="5706130568133540435">"បច្ចុប្បន្នកាតអេសឌីកំពុងប្រើដោយកុំព្យូទ័រ"</string> <string name="media_unknown_state" msgid="729192782197290385">"មិនស្គាល់ស្ថានភាពមេឌៀខាងក្រៅ។"</string> - <string name="share" msgid="1778686618230011964">"ចែករំលែក"</string> + <string name="share" msgid="1778686618230011964">"ចែករំលែក"</string> <string name="find" msgid="4808270900322985960">"រក"</string> <string name="websearch" msgid="4337157977400211589">"ស្វែងរកតាមបណ្ដាញ"</string> <string name="find_next" msgid="5742124618942193978">"រកបន្ទាប់"</string> @@ -1446,7 +1446,7 @@ <string name="sync_undo_deletes" msgid="2941317360600338602">"មិនធ្វើការលុបវិញ"</string> <string name="sync_do_nothing" msgid="3743764740430821845">"មិនធ្វើអ្វីទេឥឡូវ"</string> <string name="choose_account_label" msgid="5655203089746423927">"ជ្រើសគណនី"</string> - <string name="add_account_label" msgid="2935267344849993553">"បន្ថែមគណនីថ្មី"</string> + <string name="add_account_label" msgid="2935267344849993553">"បន្ថែមគណនីថ្មី"</string> <string name="add_account_button_label" msgid="3611982894853435874">"បន្ថែមគណនី"</string> <string name="number_picker_increment_button" msgid="2412072272832284313">"បង្កើន"</string> <string name="number_picker_decrement_button" msgid="476050778386779067">"បន្ថយ"</string> @@ -1465,15 +1465,15 @@ <string name="date_picker_increment_year_button" msgid="6318697384310808899">"បង្កើនឆ្នាំ"</string> <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"បន្ថយឆ្នាំ"</string> <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string> - <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះបង់"</string> + <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះបង់"</string> <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"លុប"</string> <string name="keyboardview_keycode_done" msgid="1992571118466679775">"រួចរាល់"</string> <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ប្ដូររបៀប"</string> <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string> <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string> - <string name="activitychooserview_choose_application" msgid="2125168057199941199">"ជ្រើសកម្មវិធី"</string> + <string name="activitychooserview_choose_application" msgid="2125168057199941199">"ជ្រើសកម្មវិធី"</string> <string name="activitychooserview_choose_application_error" msgid="8624618365481126668">"មិនអាចចាប់ផ្ដើម <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> - <string name="shareactionprovider_share_with" msgid="806688056141131819">"ចែករំលែកជាមួយ"</string> + <string name="shareactionprovider_share_with" msgid="806688056141131819">"ចែករំលែកជាមួយ"</string> <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"ចែករំលែកជាមួយ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> <string name="content_description_sliding_handle" msgid="415975056159262248">"គ្រប់គ្រងការរុញ។ ប៉ះ & សង្កត់។"</string> <string name="description_target_unlock_tablet" msgid="3833195335629795055">"អូស ដើម្បីដោះសោ។"</string> @@ -1487,7 +1487,7 @@ <string name="storage_internal" msgid="4891916833657929263">"ឧបករណ៍ផ្ទុកខាងក្នុង"</string> <string name="storage_sd_card" msgid="3282948861378286745">"កាតអេសឌី"</string> <string name="storage_usb" msgid="3017954059538517278">"ឧបករណ៍ផ្ទុកយូអេសប៊ី"</string> - <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល"</string> + <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល"</string> <string name="data_usage_warning_title" msgid="1955638862122232342">"ការព្រមានប្រើទិន្នន័យ"</string> <string name="data_usage_warning_body" msgid="2814673551471969954">"ប៉ះ ដើម្បីមើលការប្រើ និងការកំណត់។"</string> <string name="data_usage_3g_limit_title" msgid="7093334419518706686">"បានបិទទិន្នន័យ 2G-3G"</string> @@ -1544,7 +1544,7 @@ <string name="media_route_status_available" msgid="6983258067194649391">"ទំនេរ"</string> <string name="media_route_status_not_available" msgid="6739899962681886401">"មិនទំនេរ"</string> <string name="media_route_status_in_use" msgid="4533786031090198063">"កំពុងប្រើ"</string> - <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"អេក្រង់ជាប់"</string> + <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"អេក្រង់ជាប់"</string> <string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"អេក្រង់ HDMI"</string> <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"#<xliff:g id="ID">%1$d</xliff:g> ត្រួតគ្នា"</string> <string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> dpi"</string> @@ -1576,7 +1576,7 @@ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ព្យាយាមលំនាំច្រើនពេក"</string> <string name="kg_login_instructions" msgid="1100551261265506448">"ដើម្បីដោះសោ ចូលក្នុងគណនី Google ។"</string> <string name="kg_login_username_hint" msgid="5718534272070920364">"ឈ្មោះអ្នកប្រើ (អ៊ីម៉ែល)"</string> - <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string> + <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string> <string name="kg_login_submit_button" msgid="5355904582674054702">"ចូល"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"ឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់មិនត្រឹមត្រូវ។"</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ភ្លេចឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់របស់អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string> @@ -1685,7 +1685,7 @@ <string name="mediasize_japanese_you4" msgid="2091777168747058008">"You4"</string> <string name="mediasize_unknown_portrait" msgid="3088043641616409762">"មិនស្គាល់បញ្ឈរ"</string> <string name="mediasize_unknown_landscape" msgid="4876995327029361552">"មិនស្គាល់ទេសភាព"</string> - <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"បានបោះបង់"</string> + <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"បានបោះបង់"</string> <string name="write_fail_reason_cannot_write" msgid="8132505417935337724">"កំហុសក្នុងការសរសេរមាតិកា"</string> <string name="reason_unknown" msgid="6048913880184628119">"មិនស្គាល់"</string> <string name="reason_service_unavailable" msgid="7824008732243903268">"មិនបានបើកសេវាកម្មបោះពុម្ព"</string> diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml index 08ba7e19b62b..7ec1376211d9 100644 --- a/core/res/res/values-lo-rLA/strings.xml +++ b/core/res/res/values-lo-rLA/strings.xml @@ -1697,7 +1697,7 @@ <string name="restr_pin_enter_old_pin" msgid="1462206225512910757">"PIN ປະຈຸບັນ"</string> <string name="restr_pin_enter_new_pin" msgid="5959606691619959184">"ລະຫັດ PIN ໃໝ່"</string> <string name="restr_pin_confirm_pin" msgid="8501523829633146239">"ຢືນຢັນລະຫັດ PIN ໃໝ່"</string> - <string name="restr_pin_create_pin" msgid="8017600000263450337">"ສ້າງ PIN ສໍາລັບການປັບປຸງຂໍ້ຈໍາກັດ"</string> + <string name="restr_pin_create_pin" msgid="8017600000263450337">"ສ້າງ PIN ສໍາລັບການປັບປຸງຂໍ້ຈໍາກັດ"</string> <string name="restr_pin_error_doesnt_match" msgid="2224214190906994548">"PIN ບໍ່ກົງກັນ. ລອງໃໝ່ອີກຄັ້ງ."</string> <string name="restr_pin_error_too_short" msgid="8173982756265777792">"PIN ສັ້ນເກີນໄປ. ຕ້ອງມີຢ່າງໜ້ອຍ 4 ຫຼັກ."</string> <plurals name="restr_pin_countdown"> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 7a6832ec71e6..cedb92db2d3c 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3343,6 +3343,8 @@ <attr name="thumb" format="reference" /> <!-- An offset for the thumb that allows it to extend out of the range of the track. --> <attr name="thumbOffset" format="dimension" /> + <!-- Whether to split the track and leave a gap for the thumb drawable. --> + <attr name="splitTrack" format="boolean" /> </declare-styleable> <declare-styleable name="StackView"> @@ -6393,6 +6395,8 @@ <attr name="switchMinWidth" format="dimension" /> <!-- Minimum space between the switch and caption text --> <attr name="switchPadding" format="dimension" /> + <!-- Whether to split the track and leave a gap for the thumb drawable. --> + <attr name="splitTrack" /> </declare-styleable> <declare-styleable name="Pointer"> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index f39155b514b0..83cbb74a0ebf 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1393,8 +1393,10 @@ <item>com.android.inputmethod.latin</item> </string-array> - <string-array name="config_notificationScorers"> - <item>com.android.internal.notification.PeopleNotificationScorer</item> + <!-- The list of classes that should be added to the notification ranking pipline. + See {@link com.android.server.notification.NotificationSignalExtractortor} --> + <string-array name="config_notificationSignalExtractors"> + <item>com.android.server.notification.ValidateNotificationPeople</item> </string-array> <!-- Flag indicating that this device does not rotate and will always remain in its default diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index ecb22aed01a2..dc5efea4bece 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2169,6 +2169,7 @@ <public type="attr" name="toId" /> <public type="attr" name="fromId" /> <public type="attr" name="reversible" /> + <public type="attr" name="splitTrack" /> <public-padding type="dimen" name="l_resource_pad" end="0x01050010" /> diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml index 88a2a9fc31fa..e693673d8f28 100644 --- a/core/res/res/values/styles_quantum.xml +++ b/core/res/res/values/styles_quantum.xml @@ -455,10 +455,10 @@ please see styles_device_defaults.xml. <style name="Widget.Quantum.CompoundButton.Switch"> <item name="track">@drawable/switch_track_quantum</item> <item name="thumb">@drawable/switch_inner_quantum</item> + <item name="splitTrack">true</item> <item name="switchTextAppearance">@style/TextAppearance.Quantum.Widget.Switch</item> <item name="textOn"></item> <item name="textOff"></item> - <item name="thumbTextPadding">12dip</item> <item name="switchMinWidth">72dip</item> <item name="switchPadding">16dip</item> <item name="background">?attr/selectableItemBackground</item> @@ -572,10 +572,8 @@ please see styles_device_defaults.xml. <item name="indeterminateOnly">false</item> <item name="progressDrawable">@drawable/scrubber_progress_horizontal_quantum</item> <item name="indeterminateDrawable">@drawable/scrubber_progress_horizontal_quantum</item> - <item name="minHeight">13dip</item> - <item name="maxHeight">13dip</item> <item name="thumb">@drawable/scrubber_control_selector_quantum</item> - <item name="thumbOffset">16dip</item> + <item name="splitTrack">true</item> <item name="focusable">true</item> <item name="paddingStart">16dip</item> <item name="paddingEnd">16dip</item> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 5a78bfeac195..1057cc2eb0f4 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1655,7 +1655,7 @@ <java-symbol type="id" name="button_always" /> <java-symbol type="integer" name="config_globalActionsKeyTimeout" /> <java-symbol type="integer" name="config_maxResolverActivityColumns" /> - <java-symbol type="array" name="config_notificationScorers" /> + <java-symbol type="array" name="config_notificationSignalExtractors" /> <java-symbol type="layout" name="notification_quantum_action" /> <java-symbol type="layout" name="notification_quantum_action_list" /> diff --git a/core/tests/coretests/src/android/net/LinkSocketTest.java b/core/tests/coretests/src/android/net/LinkSocketTest.java deleted file mode 100644 index af77d634024a..000000000000 --- a/core/tests/coretests/src/android/net/LinkSocketTest.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.net.LinkSocket; -import android.test.suitebuilder.annotation.SmallTest; -import junit.framework.TestCase; - -/** - * Test LinkSocket - */ -public class LinkSocketTest extends TestCase { - - @SmallTest - public void testBasic() throws Exception { - LinkSocket ls; - - ls = new LinkSocket(); - ls.close(); - } - - @SmallTest - public void testLinkCapabilities() throws Exception { - LinkCapabilities lc; - - lc = new LinkCapabilities(); - assertEquals(0, lc.size()); - assertEquals(true, lc.isEmpty()); - } -} diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index ff4ab98ae1d1..2da861543cf8 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -46,7 +46,7 @@ import java.util.HashMap; * This lets you create a drawable based on an XML vector graphic It can be * defined in an XML file with the <code><vector></code> element. * <p/> - * The vector drawable has 6 elements: + * The vector drawable has the following elements: * <p/> * <dl> * <dt><code><vector></code></dt> @@ -59,15 +59,15 @@ import java.util.HashMap; * <dd>Used to defined the size of the virtual canvas the paths are drawn on. * The size is defined using the attributes <code>android:viewportHeight</code> * <code>android:viewportWidth</code></dd> - * <dt><code><group></code></dt> - * <dd>Defines the static 2D image.</dd> * <dt><code><path></code></dt> - * <dd>Defines paths to be drawn. The path elements must be within a group + * <dd>Defines paths to be drawn. Multiple paths can be defined in one xml file. + * The paths are drawn in the order of their definition order. * <dl> * <dt><code>android:name</code> * <dd>Defines the name of the path.</dd></dt> * <dt><code>android:pathData</code> - * <dd>Defines path string.</dd></dt> + * <dd>Defines path string. This is using exactly same format as "d" attribute + * in the SVG's path data</dd></dt> * <dt><code>android:fill</code> * <dd>Defines the color to fill the path (none if not present).</dd></dt> * <dt><code>android:stroke</code> @@ -108,7 +108,6 @@ public class VectorDrawable extends Drawable { private static final String SHAPE_SIZE = "size"; private static final String SHAPE_VIEWPORT = "viewport"; - private static final String SHAPE_GROUP = "group"; private static final String SHAPE_PATH = "path"; private static final String SHAPE_VECTOR = "vector"; @@ -266,10 +265,9 @@ public class VectorDrawable extends Drawable { boolean noSizeTag = true; boolean noViewportTag = true; - boolean noGroupTag = true; boolean noPathTag = true; - VGroup currentGroup = null; + VGroup currentGroup = new VGroup(); int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { @@ -286,10 +284,6 @@ public class VectorDrawable extends Drawable { } else if (SHAPE_VIEWPORT.equals(tagName)) { pathRenderer.parseViewport(res, attrs); noViewportTag = false; - } else if (SHAPE_GROUP.equals(tagName)) { - currentGroup = new VGroup(); - pathRenderer.mGroupList.add(currentGroup); - noGroupTag = false; } else if (SHAPE_VECTOR.equals(tagName)) { final TypedArray a = res.obtainAttributes(attrs, R.styleable.VectorDrawable); @@ -310,7 +304,7 @@ public class VectorDrawable extends Drawable { eventType = parser.next(); } - if (noSizeTag || noViewportTag || noGroupTag || noPathTag) { + if (noSizeTag || noViewportTag || noPathTag) { final StringBuffer tag = new StringBuffer(); if (noSizeTag) { @@ -324,13 +318,6 @@ public class VectorDrawable extends Drawable { tag.append(SHAPE_SIZE); } - if (noGroupTag) { - if (tag.length() > 0) { - tag.append(" & "); - } - tag.append(SHAPE_GROUP); - } - if (noPathTag) { if (tag.length() > 0) { tag.append(" or "); @@ -341,6 +328,7 @@ public class VectorDrawable extends Drawable { throw new XmlPullParserException("no " + tag + " defined"); } + pathRenderer.mCurrentGroup = currentGroup; // post parse cleanup pathRenderer.parseFinish(); return pathRenderer; @@ -394,7 +382,7 @@ public class VectorDrawable extends Drawable { private Paint mFillPaint; private PathMeasure mPathMeasure; - final ArrayList<VGroup> mGroupList = new ArrayList<VGroup>(); + private VGroup mCurrentGroup = new VGroup(); float mBaseWidth = 1; float mBaseHeight = 1; @@ -405,7 +393,7 @@ public class VectorDrawable extends Drawable { } public VPathRenderer(VPathRenderer copy) { - mGroupList.addAll(copy.mGroupList); + mCurrentGroup = copy.mCurrentGroup; if (copy.mCurrentPaths != null) { mCurrentPaths = new VPath[copy.mCurrentPaths.length]; for (int i = 0; i < mCurrentPaths.length; i++) { @@ -420,32 +408,24 @@ public class VectorDrawable extends Drawable { } public boolean canApplyTheme() { - final ArrayList<VGroup> groups = mGroupList; - for (int i = groups.size() - 1; i >= 0; i--) { - final ArrayList<VPath> paths = groups.get(i).mVGList; - for (int j = paths.size() - 1; j >= 0; j--) { - final VPath path = paths.get(j); - if (path.canApplyTheme()) { - return true; - } + final ArrayList<VPath> paths = mCurrentGroup.mVGList; + for (int j = paths.size() - 1; j >= 0; j--) { + final VPath path = paths.get(j); + if (path.canApplyTheme()) { + return true; } } - return false; } public void applyTheme(Theme t) { - final ArrayList<VGroup> groups = mGroupList; - for (int i = groups.size() - 1; i >= 0; i--) { - final ArrayList<VPath> paths = groups.get(i).mVGList; - for (int j = paths.size() - 1; j >= 0; j--) { - final VPath path = paths.get(j); - if (path.canApplyTheme()) { - path.applyTheme(t); - } + final ArrayList<VPath> paths = mCurrentGroup.mVGList; + for (int j = paths.size() - 1; j >= 0; j--) { + final VPath path = paths.get(j); + if (path.canApplyTheme()) { + path.applyTheme(t); } } - } public void draw(Canvas canvas, int w, int h) { @@ -537,11 +517,11 @@ public class VectorDrawable extends Drawable { } /** - * Build the "current" path based on the first group + * Build the "current" path based on the current group * TODO: improve memory use & performance or move to C++ */ public void parseFinish() { - final Collection<VPath> paths = mGroupList.get(0).getPaths(); + final Collection<VPath> paths = mCurrentGroup.getPaths(); mCurrentPaths = paths.toArray(new VPath[paths.size()]); for (int i = 0; i < mCurrentPaths.length; i++) { mCurrentPaths[i] = new VPath(mCurrentPaths[i]); diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 098753bd5afa..6aad5fbcc067 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -2452,15 +2452,19 @@ String8 ResTable_config::toString() const { if (mcc != 0) { if (res.size() > 0) res.append("-"); - res.appendFormat("%dmcc", dtohs(mcc)); + res.appendFormat("mcc%d", dtohs(mcc)); } if (mnc != 0) { if (res.size() > 0) res.append("-"); - res.appendFormat("%dmnc", dtohs(mnc)); + res.appendFormat("mnc%d", dtohs(mnc)); } + char localeStr[RESTABLE_MAX_LOCALE_LEN]; getBcp47Locale(localeStr); - res.append(localeStr); + if (strlen(localeStr) > 0) { + if (res.size() > 0) res.append("-"); + res.append(localeStr); + } if ((screenLayout&MASK_LAYOUTDIR) != 0) { if (res.size() > 0) res.append("-"); @@ -2627,6 +2631,20 @@ String8 ResTable_config::toString() const { break; } } + if ((inputFlags&MASK_KEYSHIDDEN) != 0) { + if (res.size() > 0) res.append("-"); + switch (inputFlags&MASK_KEYSHIDDEN) { + case ResTable_config::KEYSHIDDEN_NO: + res.append("keysexposed"); + break; + case ResTable_config::KEYSHIDDEN_YES: + res.append("keyshidden"); + break; + case ResTable_config::KEYSHIDDEN_SOFT: + res.append("keyssoft"); + break; + } + } if (keyboard != KEYBOARD_ANY) { if (res.size() > 0) res.append("-"); switch (keyboard) { @@ -2644,17 +2662,18 @@ String8 ResTable_config::toString() const { break; } } - if ((inputFlags&MASK_KEYSHIDDEN) != 0) { + if ((inputFlags&MASK_NAVHIDDEN) != 0) { if (res.size() > 0) res.append("-"); - switch (inputFlags&MASK_KEYSHIDDEN) { - case ResTable_config::KEYSHIDDEN_NO: - res.append("keysexposed"); + switch (inputFlags&MASK_NAVHIDDEN) { + case ResTable_config::NAVHIDDEN_NO: + res.append("navexposed"); break; - case ResTable_config::KEYSHIDDEN_YES: - res.append("keyshidden"); + case ResTable_config::NAVHIDDEN_YES: + res.append("navhidden"); break; - case ResTable_config::KEYSHIDDEN_SOFT: - res.append("keyssoft"); + default: + res.appendFormat("inputFlagsNavHidden=%d", + dtohs(inputFlags&MASK_NAVHIDDEN)); break; } } @@ -2678,21 +2697,6 @@ String8 ResTable_config::toString() const { break; } } - if ((inputFlags&MASK_NAVHIDDEN) != 0) { - if (res.size() > 0) res.append("-"); - switch (inputFlags&MASK_NAVHIDDEN) { - case ResTable_config::NAVHIDDEN_NO: - res.append("navsexposed"); - break; - case ResTable_config::NAVHIDDEN_YES: - res.append("navhidden"); - break; - default: - res.appendFormat("inputFlagsNavHidden=%d", - dtohs(inputFlags&MASK_NAVHIDDEN)); - break; - } - } if (screenSize != 0) { if (res.size() > 0) res.append("-"); res.appendFormat("%dx%d", dtohs(screenWidth), dtohs(screenHeight)); @@ -5503,7 +5507,25 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, if (package == NULL) { return (mError=NO_MEMORY); } - + + if (idmap_id == 0) { + err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings), + header->dataEnd-(base+dtohl(pkg->typeStrings))); + if (err != NO_ERROR) { + delete group; + delete package; + return (mError=err); + } + + err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings), + header->dataEnd-(base+dtohl(pkg->keyStrings))); + if (err != NO_ERROR) { + delete group; + delete package; + return (mError=err); + } + } + if (id == 0) { // This is a library so assign an ID id = mNextPackageId++; @@ -5521,21 +5543,6 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, return (mError=NO_MEMORY); } - err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings), - header->dataEnd-(base+dtohl(pkg->typeStrings))); - if (err != NO_ERROR) { - delete group; - delete package; - return (mError=err); - } - err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings), - header->dataEnd-(base+dtohl(pkg->keyStrings))); - if (err != NO_ERROR) { - delete group; - delete package; - return (mError=err); - } - //printf("Adding new package id %d at index %d\n", id, idx); err = mPackageGroups.add(group); if (err < NO_ERROR) { diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h index 86fc7c325473..52a180758fb8 100644 --- a/libs/hwui/Animator.h +++ b/libs/hwui/Animator.h @@ -45,6 +45,7 @@ public: ANDROID_API void setInterpolator(Interpolator* interpolator); ANDROID_API void setDuration(nsecs_t durationInMs); + ANDROID_API nsecs_t duration() { return mDuration; } ANDROID_API void setListener(AnimationListener* listener) { mListener = listener; } diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp index 004ddf5b6c59..b56648efc949 100644 --- a/libs/hwui/Interpolator.cpp +++ b/libs/hwui/Interpolator.cpp @@ -13,9 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +#define LOG_TAG "Interpolator" + #include "Interpolator.h" #include <math.h> +#include <cutils/log.h> + +#include "utils/MathUtils.h" namespace android { namespace uirenderer { @@ -28,5 +34,34 @@ float AccelerateDecelerateInterpolator::interpolate(float input) { return (float)(cosf((input + 1) * M_PI) / 2.0f) + 0.5f; } +LUTInterpolator::LUTInterpolator(float* values, size_t size) { + mValues = values; + mSize = size; +} + +LUTInterpolator::~LUTInterpolator() { + delete mValues; + mValues = 0; +} + +float LUTInterpolator::interpolate(float input) { + float lutpos = input * mSize; + if (lutpos >= (mSize - 1)) { + return mValues[mSize - 1]; + } + + float ipart, weight; + weight = modff(lutpos, &ipart); + + int i1 = (int) ipart; + int i2 = MathUtils::min(i1 + 1, mSize - 1); + + float v1 = mValues[i1]; + float v2 = mValues[i2]; + + return MathUtils::lerp(v1, v2, weight); +} + + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/Interpolator.h b/libs/hwui/Interpolator.h index 2cfb60ce5b5f..44fb37cff7b1 100644 --- a/libs/hwui/Interpolator.h +++ b/libs/hwui/Interpolator.h @@ -16,6 +16,10 @@ #ifndef INTERPOLATOR_H #define INTERPOLATOR_H +#include <stddef.h> + +#include <cutils/compiler.h> + namespace android { namespace uirenderer { @@ -31,7 +35,7 @@ protected: Interpolator() {} }; -class AccelerateDecelerateInterpolator : public Interpolator { +class ANDROID_API AccelerateDecelerateInterpolator : public Interpolator { public: AccelerateDecelerateInterpolator() {} virtual ~AccelerateDecelerateInterpolator() {} @@ -39,6 +43,18 @@ public: virtual float interpolate(float input); }; +class ANDROID_API LUTInterpolator : public Interpolator { +public: + LUTInterpolator(float* values, size_t size); + ~LUTInterpolator(); + + virtual float interpolate(float input); + +private: + float* mValues; + size_t mSize; +}; + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h index 8ba44dca2e2d..1a7082bda660 100644 --- a/libs/hwui/utils/MathUtils.h +++ b/libs/hwui/utils/MathUtils.h @@ -33,6 +33,14 @@ public: inline static bool isPositive(float value) { return value >= gNonZeroEpsilon; } + + inline static int min(int a, int b) { + return a < b ? a : b; + } + + inline static float lerp(float v1, float v2, float t) { + return v1 + ((v2 - v1) * t); + } }; // class MathUtils } /* namespace uirenderer */ diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java new file mode 100644 index 000000000000..bb23a363195e --- /dev/null +++ b/media/java/android/media/AudioAttributes.java @@ -0,0 +1,444 @@ +/* + * 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 android.media; + +import android.annotation.IntDef; +import android.util.Log; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * A class to encapsulate a collection of attributes describing information about an audio + * player or recorder. + */ +public final class AudioAttributes { + private final static String TAG = "AudioAttributes"; + + /** + * Content type value to use when the content type is unknown, or other than the ones defined. + */ + public final static int CONTENT_TYPE_UNKNOWN = 0; + /** + * Content type value to use when the content type is speech. + */ + public final static int CONTENT_TYPE_SPEECH = 1; + /** + * Content type value to use when the content type is music. + */ + public final static int CONTENT_TYPE_MUSIC = 2; + /** + * Content type value to use when the content type is a soundtrack, typically accompanying + * a movie or TV program. + */ + public final static int CONTENT_TYPE_MOVIE = 3; + /** + * Content type value to use when the content type is a sound used to accompany a user + * action, such as a beep or sound effect expressing a key click, or event, such as the + * type of a sound for a bonus being received in a game. These sounds are mostly synthesized + * or short Foley sounds. + */ + public final static int CONTENT_TYPE_SONIFICATION = 4; + + /** + * Usage value to use when the usage is unknown. + */ + public final static int USAGE_UNKNOWN = 0; + /** + * Usage value to use when the usage is media, such as music, or movie + * soundtracks. + */ + public final static int USAGE_MEDIA = 1; + /** + * Usage value to use when the usage is voice communications, such as telephony + * or VoIP. + */ + public final static int USAGE_VOICE_COMMUNICATION = 2; + /** + * Usage value to use when the usage is in-call signalling, such as with + * a "busy" beep, or DTMF tones. + */ + public final static int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3; + /** + * Usage value to use when the usage is an alarm (e.g. wake-up alarm). + */ + public final static int USAGE_ALARM = 4; + /** + * Usage value to use when the usage is notification. See other + * notification usages for more specialized uses. + */ + public final static int USAGE_NOTIFICATION = 5; + /** + * Usage value to use when the usage is telephony ringtone. + */ + public final static int USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6; + /** + * Usage value to use when the usage is a request to enter/end a + * communication, such as a VoIP communication or video-conference. + */ + public final static int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7; + /** + * Usage value to use when the usage is notification for an "instant" + * communication such as a chat, or SMS. + */ + public final static int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8; + /** + * Usage value to use when the usage is notification for a + * non-immediate type of communication such as e-mail. + */ + public final static int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9; + /** + * Usage value to use when the usage is to attract the user's attention, + * such as a reminder or low battery warning. + */ + public final static int USAGE_NOTIFICATION_EVENT = 10; + /** + * Usage value to use when the usage is for accessibility, such as with + * a screen reader. + */ + public final static int USAGE_ASSISTANCE_ACCESSIBILITY = 11; + /** + * Usage value to use when the usage is driving or navigation directions. + */ + public final static int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; + /** + * Usage value to use when the usage is sonification, such as with user + * interface sounds. + */ + public final static int USAGE_ASSISTANCE_SONIFICATION = 13; + /** + * Usage value to use when the usage is for game audio. + */ + public final static int USAGE_GAME = 14; + + /** + * Flag defining a behavior where the audibility of the sound will be ensured by the system. + */ + public final static int FLAG_AUDIBILITY_ENFORCED = 0x1 << 0; + /** + * @hide + * Flag defining a behavior where the playback of the sound is ensured without + * degradation only when going to a secure sink. + */ + // FIXME not guaranteed yet + // TODO add OR to getFlags() when supported and in public API + public final static int FLAG_SECURE = 0x1 << 1; + /** + * @hide + * Flag to enable when the stream is associated with SCO usage. + * Internal use only for dealing with legacy STREAM_BLUETOOTH_SCO + */ + public final static int FLAG_SCO = 0x1 << 2; + + + private int mUsage = USAGE_UNKNOWN; + private int mContentType = CONTENT_TYPE_UNKNOWN; + private int mFlags = 0x0; + private HashSet<String> mTags; + + private AudioAttributes() { + } + + /** + * Return the content type. + * @return one of the values that can be set in {@link Builder#setContentType(int)} + */ + public int getContentType() { + return mContentType; + } + + /** + * Return the usage. + * @return one of the values that can be set in {@link Builder#setUsage(int)} + */ + public int getUsage() { + return mUsage; + } + + /** + * Return the flags. + * @return a combined mask of all flags + */ + public int getFlags() { + // only return the flags that are public + return (mFlags & (FLAG_AUDIBILITY_ENFORCED)); + } + + /** + * @hide + * Return all the flags, even the non-public ones. + * Internal use only + * @return a combined mask of all flags + */ + public int getAllFlags() { + return mFlags; + } + + /** + * Return the set of tags. + * @return a read-only set of all tags stored as strings. + */ + public Set<String> getTags() { + return Collections.unmodifiableSet(mTags); + } + + /** + * Builder class for {@link AudioAttributes} objects. + */ + public static class Builder { + private int mUsage = USAGE_UNKNOWN; + private int mContentType = CONTENT_TYPE_UNKNOWN; + private int mFlags = 0x0; + private HashSet<String> mTags = new HashSet<String>(); + + /** + * Constructs a new Builder with the defaults. + */ + public Builder() { + } + + /** + * Constructs a new Builder from a given AudioAttributes + * @param aa the AudioAttributes object whose data will be reused in the new Builder. + */ + @SuppressWarnings("unchecked") // for cloning of mTags + public Builder(AudioAttributes aa) { + mUsage = aa.mUsage; + mContentType = aa.mContentType; + mFlags = aa.mFlags; + mTags = (HashSet<String>) aa.mTags.clone(); + } + + /** + * Combines all of the attributes that have been set and return a new + * {@link AudioAttributes} object. + * @return a new {@link AudioAttributes} object + */ + @SuppressWarnings("unchecked") // for cloning of mTags + public AudioAttributes build() { + AudioAttributes aa = new AudioAttributes(); + aa.mContentType = mContentType; + aa.mUsage = mUsage; + aa.mFlags = mFlags; + aa.mTags = (HashSet<String>) mTags.clone(); + return aa; + } + + /** + * Sets the attribute describing what is the intended use of the the audio signal, + * such as alarm or ringtone. + * @param usage one of {@link AudioAttributes#USAGE_UNKNOWN}, + * {@link AudioAttributes#USAGE_MEDIA}, + * {@link AudioAttributes#USAGE_VOICE_COMMUNICATION}, + * {@link AudioAttributes#USAGE_VOICE_COMMUNICATION_SIGNALLING}, + * {@link AudioAttributes#USAGE_ALARM}, {@link AudioAttributes#USAGE_NOTIFICATION}, + * {@link AudioAttributes#USAGE_NOTIFICATION_TELEPHONY_RINGTONE}, + * {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_REQUEST}, + * {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_INSTANT}, + * {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_DELAYED}, + * {@link AudioAttributes#USAGE_NOTIFICATION_EVENT}, + * {@link AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY}, + * {@link AudioAttributes#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE}, + * {@link AudioAttributes#USAGE_ASSISTANCE_SONIFICATION}, + * {@link AudioAttributes#USAGE_GAME}. + * @return the same Builder instance. + */ + public Builder setUsage(@AttributeUsage int usage) { + switch (usage) { + case USAGE_UNKNOWN: + case USAGE_MEDIA: + case USAGE_VOICE_COMMUNICATION: + case USAGE_VOICE_COMMUNICATION_SIGNALLING: + case USAGE_ALARM: + case USAGE_NOTIFICATION: + case USAGE_NOTIFICATION_TELEPHONY_RINGTONE: + case USAGE_NOTIFICATION_COMMUNICATION_REQUEST: + case USAGE_NOTIFICATION_COMMUNICATION_INSTANT: + case USAGE_NOTIFICATION_COMMUNICATION_DELAYED: + case USAGE_NOTIFICATION_EVENT: + case USAGE_ASSISTANCE_ACCESSIBILITY: + case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: + case USAGE_ASSISTANCE_SONIFICATION: + case USAGE_GAME: + mUsage = usage; + break; + default: + mUsage = USAGE_UNKNOWN; + } + return this; + } + + /** + * Sets the attribute describing the content type of the audio signal, such as speech, + * or music. + * @param contentType the content type values, one of + * {@link AudioAttributes#CONTENT_TYPE_MOVIE}, + * {@link AudioAttributes#CONTENT_TYPE_MUSIC}, + * {@link AudioAttributes#CONTENT_TYPE_SONIFICATION}, + * {@link AudioAttributes#CONTENT_TYPE_SPEECH}, + * {@link AudioAttributes#CONTENT_TYPE_UNKNOWN}. + * @return the same Builder instance. + */ + public Builder setContentType(@AttributeContentType int contentType) { + switch (contentType) { + case CONTENT_TYPE_UNKNOWN: + case CONTENT_TYPE_MOVIE: + case CONTENT_TYPE_MUSIC: + case CONTENT_TYPE_SONIFICATION: + case CONTENT_TYPE_SPEECH: + mContentType = contentType; + break; + default: + mUsage = CONTENT_TYPE_UNKNOWN; + } + return this; + } + + /** + * Sets the combination of flags. + * @param flags the {@link AudioAttributes#FLAG_AUDIBILITY_ENFORCED} flag. + * @return the same Builder instance. + */ + public Builder setFlags(int flags) { + flags &= (AudioAttributes.FLAG_AUDIBILITY_ENFORCED | AudioAttributes.FLAG_SCO + | AudioAttributes.FLAG_SECURE); + mFlags |= flags; + return this; + } + + /** + * Add a custom tag stored as a string + * @param tag + * @return the same Builder instance. + */ + public Builder addTag(String tag) { + mTags.add(tag); + return this; + } + + /** + * Adds attributes inferred from the legacy stream types. + * @param streamType one of {@link AudioManager#STREAM_VOICE_CALL}, + * {@link AudioManager#STREAM_SYSTEM}, {@link AudioManager#STREAM_RING}, + * {@link AudioManager#STREAM_MUSIC}, {@link AudioManager#STREAM_ALARM}, + * or {@link AudioManager#STREAM_NOTIFICATION}. + * @return the same Builder instance. + */ + public Builder setLegacyStreamType(int streamType) { + return setInternalLegacyStreamType(streamType); + } + + /** + * @hide + * For internal framework use only, enables building from hidden stream types. + * @param streamType + * @return the same Builder instance. + */ + public Builder setInternalLegacyStreamType(int streamType) { + switch(streamType) { + case AudioSystem.STREAM_VOICE_CALL: + mContentType = CONTENT_TYPE_SPEECH; + mUsage = USAGE_VOICE_COMMUNICATION; + break; + case AudioSystem.STREAM_SYSTEM_ENFORCED: + mFlags |= FLAG_AUDIBILITY_ENFORCED; + // intended fall through, attributes in common with STREAM_SYSTEM + case AudioSystem.STREAM_SYSTEM: + mContentType = CONTENT_TYPE_SONIFICATION; + mUsage = USAGE_ASSISTANCE_SONIFICATION; + break; + case AudioSystem.STREAM_RING: + mContentType = CONTENT_TYPE_SONIFICATION; + mUsage = USAGE_NOTIFICATION_TELEPHONY_RINGTONE; + break; + case AudioSystem.STREAM_MUSIC: + mContentType = CONTENT_TYPE_MUSIC; + mUsage = USAGE_MEDIA; + break; + case AudioSystem.STREAM_ALARM: + mContentType = CONTENT_TYPE_SONIFICATION; + mUsage = USAGE_ALARM; + break; + case AudioSystem.STREAM_NOTIFICATION: + mContentType = CONTENT_TYPE_SONIFICATION; + mUsage = USAGE_NOTIFICATION; + break; + case AudioSystem.STREAM_BLUETOOTH_SCO: + mContentType = CONTENT_TYPE_SPEECH; + mUsage = USAGE_VOICE_COMMUNICATION; + mFlags |= FLAG_SCO; + break; + case AudioSystem.STREAM_DTMF: + mContentType = CONTENT_TYPE_SONIFICATION; + mUsage = USAGE_VOICE_COMMUNICATION_SIGNALLING; + break; + case AudioSystem.STREAM_TTS: + mContentType = CONTENT_TYPE_SPEECH; + mUsage = USAGE_ASSISTANCE_ACCESSIBILITY; + break; + default: + Log.e(TAG, "Invalid stream type " + streamType + " in for AudioAttributes"); + } + return this; + } + }; + + /** @hide */ + @Override + public String toString () { + return new String("AudioAttributes:" + + " usage=" + mUsage + + " content=" + mContentType + + " flags=0x" + Integer.toHexString(mFlags) + + " tags=" + mTags); + } + + /** @hide */ + @IntDef({ + USAGE_UNKNOWN, + USAGE_MEDIA, + USAGE_VOICE_COMMUNICATION, + USAGE_VOICE_COMMUNICATION_SIGNALLING, + USAGE_ALARM, + USAGE_NOTIFICATION, + USAGE_NOTIFICATION_TELEPHONY_RINGTONE, + USAGE_NOTIFICATION_COMMUNICATION_REQUEST, + USAGE_NOTIFICATION_COMMUNICATION_INSTANT, + USAGE_NOTIFICATION_COMMUNICATION_DELAYED, + USAGE_NOTIFICATION_EVENT, + USAGE_ASSISTANCE_ACCESSIBILITY, + USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, + USAGE_ASSISTANCE_SONIFICATION, + USAGE_GAME + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AttributeUsage {} + + /** @hide */ + @IntDef({ + CONTENT_TYPE_UNKNOWN, + CONTENT_TYPE_SPEECH, + CONTENT_TYPE_MUSIC, + CONTENT_TYPE_MOVIE, + CONTENT_TYPE_SONIFICATION + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AttributeContentType {} +} diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl index 7a8c22eb2304..e341647b8465 100644 --- a/media/java/android/media/session/ISessionManager.aidl +++ b/media/java/android/media/session/ISessionManager.aidl @@ -25,6 +25,6 @@ import android.os.Bundle; * @hide */ interface ISessionManager { - ISession createSession(String packageName, in ISessionCallback cb, String tag); - List<IBinder> getSessions(in ComponentName compName); + ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId); + List<IBinder> getSessions(in ComponentName compName, int userId); }
\ No newline at end of file diff --git a/media/java/android/media/session/SessionManager.java b/media/java/android/media/session/SessionManager.java index fd022fcc65a5..1eb3b7a9f462 100644 --- a/media/java/android/media/session/SessionManager.java +++ b/media/java/android/media/session/SessionManager.java @@ -22,6 +22,7 @@ import android.media.session.ISessionManager; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.service.notification.NotificationListenerService; import android.util.Log; @@ -65,10 +66,25 @@ public final class SessionManager { * @return a {@link Session} for the new session */ public Session createSession(String tag) { + return createSessionAsUser(tag, UserHandle.myUserId()); + } + + /** + * Creates a new session as the specified user. To create a session as a + * user other than your own you must hold the + * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} + * permission. + * + * @param tag A short name for debugging purposes + * @param userId The user id to create the session as. + * @return a {@link Session} for the new session + * @hide + */ + public Session createSessionAsUser(String tag, int userId) { try { Session.CallbackStub cbStub = new Session.CallbackStub(); Session session = new Session(mService - .createSession(mContext.getPackageName(), cbStub, tag), cbStub); + .createSession(mContext.getPackageName(), cbStub, tag, userId), cbStub); cbStub.setMediaSession(session); return session; @@ -91,9 +107,27 @@ public final class SessionManager { * @return A list of controllers for ongoing sessions */ public List<SessionController> getActiveSessions(ComponentName notificationListener) { + return getActiveSessionsForUser(notificationListener, UserHandle.myUserId()); + } + + /** + * Get active sessions for a specific user. To retrieve actions for a user + * other than your own you must hold the + * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission + * in addition to any other requirements. If you are an enabled notification + * listener you may only get sessions for the users you are enabled for. + * + * @param notificationListener The enabled notification listener component. + * May be null. + * @param userId The user id to fetch sessions for. + * @return A list of controllers for ongoing sessions. + * @hide + */ + public List<SessionController> getActiveSessionsForUser(ComponentName notificationListener, + int userId) { ArrayList<SessionController> controllers = new ArrayList<SessionController>(); try { - List<IBinder> binders = mService.getSessions(notificationListener); + List<IBinder> binders = mService.getSessions(notificationListener, userId); for (int i = binders.size() - 1; i >= 0; i--) { SessionController controller = SessionController.fromBinder(ISessionController.Stub .asInterface(binders.get(i))); diff --git a/packages/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml index e8944ec2ad72..8c9030dc3403 100644 --- a/packages/DocumentsUI/res/values-km-rKH/strings.xml +++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml @@ -20,14 +20,14 @@ <string name="title_open" msgid="4353228937663917801">"បើកពី"</string> <string name="title_save" msgid="2433679664882857999">"រក្សាទុកទៅ"</string> <string name="menu_create_dir" msgid="5947289605844398389">"បង្កើតថត"</string> - <string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាពក្រឡា"</string> + <string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាពក្រឡា"</string> <string name="menu_list" msgid="7279285939892417279">"ទិដ្ឋភាពបញ្ជី"</string> <string name="menu_sort" msgid="7677740407158414452">"តម្រៀបតាម"</string> <string name="menu_search" msgid="3816712084502856974">"ស្វែងរក"</string> <string name="menu_settings" msgid="6008033148948428823">"ការកំណត់"</string> <string name="menu_open" msgid="432922957274920903">"បើក"</string> <string name="menu_save" msgid="2394743337684426338">"រក្សាទុក"</string> - <string name="menu_share" msgid="3075149983979628146">"ចែករំលែក"</string> + <string name="menu_share" msgid="3075149983979628146">"ចែករំលែក"</string> <string name="menu_delete" msgid="8138799623850614177">"លុប"</string> <string name="menu_select" msgid="8711270657353563424">"ជ្រើស \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string> <string name="mode_selected_count" msgid="459111894725594625">"បានជ្រើស <xliff:g id="COUNT">%1$d</xliff:g>"</string> @@ -48,7 +48,7 @@ <string name="pref_advanced_devices" msgid="903257239609301276">"បង្ហាញឧបករណ៍កម្រិតខ្ពស់"</string> <string name="pref_file_size" msgid="2826879315743961459">"បង្ហាញទំហំឯកសារ"</string> <string name="pref_device_size" msgid="3542106883278997222">"បង្ហាញទំហំឧបករណ៍"</string> - <string name="empty" msgid="7858882803708117596">"គ្មានធាតុ"</string> + <string name="empty" msgid="7858882803708117596">"គ្មានធាតុ"</string> <string name="toast_no_application" msgid="1339885974067891667">"មិនអាចបើកឯកសារ"</string> <string name="toast_failed_delete" msgid="2180678019407244069">"មិនអាចលុបឯកសារមួយចំនួន"</string> <string name="share_via" msgid="8966594246261344259">"ចែករំលែកតាម"</string> diff --git a/packages/Keyguard/res/values-km-rKH/strings.xml b/packages/Keyguard/res/values-km-rKH/strings.xml index a2e54a76234f..18b59f117605 100644 --- a/packages/Keyguard/res/values-km-rKH/strings.xml +++ b/packages/Keyguard/res/values-km-rKH/strings.xml @@ -83,7 +83,7 @@ <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string> <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string> <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string> - <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះបង់"</string> + <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះបង់"</string> <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"លុប"</string> <string name="keyboardview_keycode_done" msgid="1992571118466679775">"រួចរាល់"</string> <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ប្ដូររបៀប"</string> @@ -120,7 +120,7 @@ <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ព្យាយាមលំនាំច្រើនពេក"</string> <string name="kg_login_instructions" msgid="1100551261265506448">"ដើម្បីដោះសោ ចូលក្នុងគណនី Google ។"</string> <string name="kg_login_username_hint" msgid="5718534272070920364">"ឈ្មោះអ្នកប្រើ (អ៊ីម៉ែល)"</string> - <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string> + <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string> <string name="kg_login_submit_button" msgid="5355904582674054702">"ចូល"</string> <string name="kg_login_invalid_input" msgid="5754664119319872197">"ឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់មិនត្រឹមត្រូវ។"</string> <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ភ្លេចឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់របស់អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string> diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_contrast_alpha.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_contrast_alpha.png Binary files differdeleted file mode 100644 index 0f9dfc7fdcec..000000000000 --- a/packages/SystemUI/res/drawable-hdpi/ic_qs_contrast_alpha.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_contrast_alpha.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_contrast_alpha.png Binary files differdeleted file mode 100644 index a4dd0878879d..000000000000 --- a/packages/SystemUI/res/drawable-mdpi/ic_qs_contrast_alpha.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_contrast_alpha.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_contrast_alpha.png Binary files differdeleted file mode 100644 index 9331e529ea20..000000000000 --- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_contrast_alpha.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_contrast_alpha.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_contrast_alpha.png Binary files differdeleted file mode 100644 index 82c3842ff3d9..000000000000 --- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_contrast_alpha.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable/ic_settings_24dp.xml b/packages/SystemUI/res/drawable/ic_settings_24dp.xml index 5c38a22221ac..a2f78225b77b 100644 --- a/packages/SystemUI/res/drawable/ic_settings_24dp.xml +++ b/packages/SystemUI/res/drawable/ic_settings_24dp.xml @@ -20,10 +20,10 @@ android:height="24dp"/> <viewport android:viewportWidth="24.0" android:viewportHeight="24.0"/> -<group> + <path android:pathData="M19.4,13.0c0.0,-0.3 0.1,-0.6 0.1,-1.0s0.0,-0.7 -0.1,-1.0l2.1,-1.7c0.2,-0.2 0.2,-0.4 0.1,-0.6l-2.0,-3.5C19.5,5.1 19.3,5.0 19.0,5.1l-2.5,1.0c-0.5,-0.4 -1.1,-0.7 -1.7,-1.0l-0.4,-2.6C14.5,2.2 14.2,2.0 14.0,2.0l-4.0,0.0C9.8,2.0 9.5,2.2 9.5,2.4L9.1,5.1C8.5,5.3 8.0,5.7 7.4,6.1L5.0,5.1C4.7,5.0 4.5,5.1 4.3,5.3l-2.0,3.5C2.2,8.9 2.3,9.2 2.5,9.4L4.6,11.0c0.0,0.3 -0.1,0.6 -0.1,1.0s0.0,0.7 0.1,1.0l-2.1,1.7c-0.2,0.2 -0.2,0.4 -0.1,0.6l2.0,3.5C4.5,18.9 4.7,19.0 5.0,18.9l2.5,-1.0c0.5,0.4 1.1,0.7 1.7,1.0l0.4,2.6c0.0,0.2 0.2,0.4 0.5,0.4l4.0,0.0c0.2,0.0 0.5,-0.2 0.5,-0.4l0.4,-2.6c0.6,-0.3 1.2,-0.6 1.7,-1.0l2.5,1.0c0.2,0.1 0.5,0.0 0.6,-0.2l2.0,-3.5c0.1,-0.2 0.1,-0.5 -0.1,-0.6L19.4,13.0zM12.0,15.5c-1.9,0.0 -3.5,-1.6 -3.5,-3.5s1.6,-3.5 3.5,-3.5s3.5,1.6 3.5,3.5S13.9,15.5 12.0,15.5z" android:fill="#ffffffff" /> -</group> + </vector> diff --git a/packages/SystemUI/res/drawable/recents_dismiss_dark.xml b/packages/SystemUI/res/drawable/recents_dismiss_dark.xml index c015cc84a910..744795e5e889 100644 --- a/packages/SystemUI/res/drawable/recents_dismiss_dark.xml +++ b/packages/SystemUI/res/drawable/recents_dismiss_dark.xml @@ -24,12 +24,11 @@ android:viewportHeight="100" android:viewportWidth="100" /> - <group> - <path - android:name="x" - android:pathData="M0,0L100,100M0,100L100,0z" - android:stroke="@color/recents_task_bar_dark_dismiss_color" - android:strokeWidth="8.0" - android:strokeLineCap="square" /> - </group> + <path + android:name="x" + android:pathData="M0,0L100,100M0,100L100,0z" + android:stroke="@color/recents_task_bar_dark_dismiss_color" + android:strokeWidth="8.0" + android:strokeLineCap="square" /> + </vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/recents_dismiss_light.xml b/packages/SystemUI/res/drawable/recents_dismiss_light.xml index 9c93db95e5c5..96bfbe119451 100644 --- a/packages/SystemUI/res/drawable/recents_dismiss_light.xml +++ b/packages/SystemUI/res/drawable/recents_dismiss_light.xml @@ -24,12 +24,12 @@ android:viewportHeight="100" android:viewportWidth="100" /> - <group> - <path - android:name="x" - android:pathData="M0,0L100,100M0,100L100,0z" - android:stroke="@color/recents_task_bar_light_dismiss_color" - android:strokeWidth="8.0" - android:strokeLineCap="square" /> - </group> + + <path + android:name="x" + android:pathData="M0,0L100,100M0,100L100,0z" + android:stroke="@color/recents_task_bar_light_dismiss_color" + android:strokeWidth="8.0" + android:strokeLineCap="square" /> + </vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 8ec48c8144c3..eb66a59ae73f 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Onlangse programme"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Deursoek"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Foon"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Knoppie vir wissel van invoermetode."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Versoenbaarheid-zoem se knoppie."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoem kleiner na groter skerm."</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 7aaaaf302f60..a3eca6a5af2d 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"የቅርብ ጊዜ መተግበሪያዎች"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"ፈልግ"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"ካሜራ"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"ስልክ"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"የግቤት ስልት አዝራር ቀይር"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"የተኳኋኝአጉላ አዝራር።"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"አነስተኛውን ማያ ወደ ትልቅ አጉላ።"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index febcab67f6af..e03a2f921b94 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Скорошни приложения"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Търсене"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Камера"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Телефон"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Бутон за превключване на метода на въвеждане."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Бутон за промяна на мащаба с цел съвместимост."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Промяна на мащаба на екрана от по-малък до по-голям."</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 658ece049914..e50a155daff1 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Aplicacions recents"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Cerca"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Càmera"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telèfon"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Botó de canvi del mètode d\'entrada."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botó de zoom de compatibilitat."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Amplia menys com més gran sigui la pantalla."</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index c7d2703908f6..a2d9d9c4a086 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Nové aplikace"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Hledat"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Fotoaparát"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Tlačítko přepnutí metody zadávání"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Tlačítko úpravy velikosti z důvodu kompatibility"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zvětšit menší obrázek na větší obrazovku."</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 51198e6b0b21..00041a224d0d 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Aplicaciones recientes"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Buscar"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Cámara"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Teléfono"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Botón Cambiar método de entrada"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botón de zoom de compatibilidad"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de pantalla más pequeña a más grande"</string> diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml index 0f95c60566a6..4c42a321e6f8 100644 --- a/packages/SystemUI/res/values-et-rEE/strings.xml +++ b/packages/SystemUI/res/values-et-rEE/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Hiljutised rakendused"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Otsing"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Kaamera"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Sisestusmeetodi vahetamise nupp."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Sobivussuumi nupp."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Suumi suuremale ekraanile vähem."</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index f36d04bcdabd..a9c6af7a03f8 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"برنامههای اخیر"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"جستجو"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"دوربین"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"تلفن"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"کلید تغییر روش ورود متن."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"دکمه بزرگنمایی سازگار."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"بزرگنمایی از صفحههای کوچک تا بزرگ."</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 4ae3851e1c37..8ddf070ae7aa 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Viimeaikaiset sovellukset"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Haku"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Puhelin"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Syöttötavan vaihtopainike."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Yhteensopivuuszoomaus-painike."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoomaa pienemmältä suuremmalle ruudulle."</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index c55a5fc375a1..69b5acc35ccb 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Applications récentes"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Rechercher"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Appareil photo"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Téléphone"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Bouton \"Changer le mode de saisie\""</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Bouton \"Zoom de compatibilité\""</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de compatibilité avec la taille de l\'écran"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 112191800e52..85f817b6b5f8 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Applications récentes"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Rechercher"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Appareil photo"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Téléphoner"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Bouton \"Changer le mode de saisie\""</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Bouton \"Zoom de compatibilité\""</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de compatibilité avec la taille de l\'écran"</string> diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml index 7a8ab101dd22..8c26a8a7c824 100644 --- a/packages/SystemUI/res/values-hy-rAM/strings.xml +++ b/packages/SystemUI/res/values-hy-rAM/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Վերջին ծրագրերը"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Որոնել"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Ֆոտոխցիկ"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Հեռախոս"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Միացնել մուտքագրման եղանակի կոճակը:"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Համատեղելիության խոշորացման կոճակը:"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Դիտափոխել փոքրից ավելի մեծ էկրան:"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 08ffc542f408..e1625076a2d2 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"אפליקציות אחרונות"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"חפש"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"מצלמה"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"טלפון"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"לחצן החלפת שיטת קלט."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"לחצן מרחק מתצוגה של תאימות."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"שנה מרחק מתצוגה של מסך קטן לגדול יותר."</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 2caa069392f8..a9e79352e082 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"最近使ったアプリ"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"検索"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"カメラ"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"電話"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"入力方法の切り替えボタン。"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"互換ズームボタン。"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"小さい画面から大きい画面に拡大。"</string> diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml index 62e40d06eb47..c56864ba99c6 100644 --- a/packages/SystemUI/res/values-ka-rGE/strings.xml +++ b/packages/SystemUI/res/values-ka-rGE/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"ბოლოს გამოყენებული აპები"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"ძიება"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"კამერა"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"ტელეფონი"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"შეყვანის მეთოდის გადართვის ღილაკი."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"თავსებადი მასშტაბირების ღილაკი."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"შეცვალეთ პატარა ეკრანი უფრო დიდით."</string> diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml index 35a6917ba861..a5008b3c9156 100644 --- a/packages/SystemUI/res/values-km-rKH/strings.xml +++ b/packages/SystemUI/res/values-km-rKH/strings.xml @@ -64,7 +64,7 @@ <string name="screenshot_saving_ticker" msgid="7403652894056693515">"កំពុងរក្សាទុករូបថតអេក្រង់…"</string> <string name="screenshot_saving_title" msgid="8242282144535555697">"កំពុងរក្សាទុករូបថតអេក្រង់..."</string> <string name="screenshot_saving_text" msgid="2419718443411738818">"រូបថតអេក្រង់កំពុងត្រូវបានរក្សាទុក។"</string> - <string name="screenshot_saved_title" msgid="6461865960961414961">"បានចាប់យករូបថតអេក្រង់។"</string> + <string name="screenshot_saved_title" msgid="6461865960961414961">"បានចាប់យករូបថតអេក្រង់។"</string> <string name="screenshot_saved_text" msgid="1152839647677558815">"ប៉ះ ដើម្បីមើលរូបថតអេក្រង់របស់អ្នក។"</string> <string name="screenshot_failed_title" msgid="705781116746922771">"មិនអាចចាប់យករូបថតអេក្រង់។"</string> <string name="screenshot_failed_text" msgid="8134011269572415402">"មិនអាចរក្សាទុករូបថតអេក្រង់។ ឧបករណ៍ផ្ទុកអាចកំពុងប្រើ។"</string> @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"កម្មវិធីថ្មីៗ"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"ស្វែងរក"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"ម៉ាស៊ីនថត"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"ទូរស័ព្ទ"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"ប្ដូរប៊ូតុងវិធីសាស្ត្របញ្ចូល។"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ប៊ូតុងពង្រីកត្រូវគ្នា។"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ពង្រីក/បង្រួមអេក្រង់ពីទៅធំ"</string> @@ -141,7 +140,7 @@ <string name="accessibility_remove_notification" msgid="3603099514902182350">"សម្អាតការជូនដំណឹង។"</string> <string name="accessibility_gps_enabled" msgid="3511469499240123019">"បានបើក GPS ។"</string> <string name="accessibility_gps_acquiring" msgid="8959333351058967158">"ទទួល GPS ។"</string> - <string name="accessibility_tty_enabled" msgid="4613200365379426561">"បានបើកម៉ាស៊ីនអង្គុលីលេខ"</string> + <string name="accessibility_tty_enabled" msgid="4613200365379426561">"បានបើកម៉ាស៊ីនអង្គុលីលេខ"</string> <string name="accessibility_ringer_vibrate" msgid="666585363364155055">"កម្មវិធីរោទ៍ញ័រ។"</string> <string name="accessibility_ringer_silent" msgid="9061243307939135383">"កម្មវិធីរោទ៍ស្ងាត់។"</string> <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> បដិសេធ។"</string> @@ -188,7 +187,7 @@ <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"ចាក់សោបញ្ឈរ"</string> <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"ចាក់សោផ្ដេក"</string> <string name="quick_settings_ime_label" msgid="7073463064369468429">"វិធីសាស្ត្របញ្ចូល"</string> - <string name="quick_settings_location_label" msgid="5011327048748762257">"ទីតាំង"</string> + <string name="quick_settings_location_label" msgid="5011327048748762257">"ទីតាំង"</string> <string name="quick_settings_location_off_label" msgid="7464544086507331459">"ទីតាំងបានបិទ"</string> <string name="quick_settings_media_device_label" msgid="1302906836372603762">"ឧបករណ៍មេឌៀ"</string> <string name="quick_settings_rssi_label" msgid="7725671335550695589">"RSSI"</string> @@ -209,7 +208,7 @@ <string name="recents_empty_message" msgid="2269156590813544104">"ថ្មីៗ"</string> <string name="recents_app_info_button_label" msgid="2890317189376000030">"ព័ត៌មានកម្មវិធី"</string> <string name="recents_search_bar_label" msgid="8074997400187836677">"ស្វែងរក"</string> - <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"បណ្ដាញអាច\nត្រូវបានត្រួតពិនិត្យ"</string> + <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"បណ្ដាញអាច\nត្រូវបានត្រួតពិនិត្យ"</string> <string name="description_target_search" msgid="3091587249776033139">"ស្វែងរក"</string> <string name="description_direction_up" msgid="7169032478259485180">"រុញឡើងលើដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ។"</string> <string name="description_direction_left" msgid="7207478719805562165">"រុញទៅឆ្វេងដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ។"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index d7cd13b048a7..bd7a257c8a10 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"최근에 사용한 앱"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"검색"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"카메라"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"전화"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"입력 방법 버튼을 전환합니다."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"호환성 확대/축소 버튼입니다."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"작은 화면을 큰 화면으로 확대합니다."</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 87087b46de69..b160a5acb228 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -181,7 +181,7 @@ <string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"Bluetooth"</string> <string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"„Bluetooth“ (<xliff:g id="NUMBER">%d</xliff:g> įreng.)"</string> <string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"„Bluetooth“ išjungta"</string> - <string name="quick_settings_brightness_label" msgid="6968372297018755815">"Skaistis"</string> + <string name="quick_settings_brightness_label" msgid="6968372297018755815">"Šviesumas"</string> <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Automatiškai sukti"</string> <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Sukimas užrakintas"</string> <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Užrakinta stačia padėtis"</string> @@ -200,7 +200,7 @@ <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Tinklo nėra"</string> <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"„Wi-Fi“ išjungta"</string> <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Perduoti ekraną"</string> - <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Skaistis"</string> + <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Šviesumas"</string> <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATINIS"</string> <string name="quick_settings_inversion_label" msgid="1666358784283020762">"Spalvų inversijos režimas"</string> <string name="quick_settings_contrast_label" msgid="3319507551689108692">"Patobulinto kontrasto režimas"</string> diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml index 3e7b660ed0b0..4669d991de07 100644 --- a/packages/SystemUI/res/values-mn-rMN/strings.xml +++ b/packages/SystemUI/res/values-mn-rMN/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Сүүлийн апп"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Хайх"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Камер"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Утас"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Оруулах аргыг сэлгэх товч."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Тохиромжтой өсгөх товч."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Жижгээс том дэлгэцрүү өсгөх."</string> diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml index af50786dd5db..60e55273a799 100644 --- a/packages/SystemUI/res/values-ms-rMY/strings.xml +++ b/packages/SystemUI/res/values-ms-rMY/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Aplikasi terbaharu"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Cari"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Butang tukar kaedah input."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Butang zum keserasian."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Skrin zum lebih kecil kepada lebih besar."</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index e51341dadcaa..00bea9ceb65f 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Ostatnie aplikacje"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Szukaj"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Aparat"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Przycisk przełączania metody wprowadzania."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Przycisk powiększenia na potrzeby zgodności."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Powiększa mniejszy ekran do większego."</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 162d7634d9bb..2337a7d38b35 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Aplicativos recentes"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Pesquisar"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Câmera"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefone"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Alterar botão do método de entrada."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botão de zoom da compatibilidade."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Aumentar a tela com zoom."</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 75ba35b6490a..4f2c471475fe 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Aplicaţii recente"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Căutați"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Cameră foto"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Buton pentru comutarea metodei de introducere."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Buton zoom pentru compatibilitate."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Faceţi zoom de la o imagine mai mică la una mai mare."</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 6bdc6f4caf04..837e98e68c88 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Недавние приложения"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Поиск"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Камера"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Телефон."</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Кнопка переключения способа ввода."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Кнопка масштабирования (режим совместимости)"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Уменьшение изображения для увеличения свободного места на экране."</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 12c90ecd2406..ae70ed62fe67 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Nové aplikácie"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Hľadať"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Fotoaparát"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefón"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Tlačidlo prepnutia metódy vstupu."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Tlačidlo úpravy veľkosti z dôvodu kompatibility."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zväčšiť menší obrázok na väčšiu obrazovku."</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index b626f78c83d9..2713cf96c144 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -76,8 +76,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Programu za hivi karibuni"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Tafuta"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Simu"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Swichi kitufe cha mbinu ingizi."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Kichupo cha kukuza kwa utangamanifu"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Kuza kidogo kwa skrini kubwa."</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 6cb4a4874ab1..4dc3d22b38aa 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"แอปพลิเคชันล่าสุด"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"ค้นหา"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"กล้องถ่ายรูป"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"โทรศัพท์"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"ปุ่มสลับวิธีการป้อนข้อมูล"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ปุ่มซูมที่ใช้งานร่วมกันได้"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ซูมหน้าจอให้มีขนาดใหญ่ขึ้น"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 17e720e3747e..e50f72356e9d 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Kamakailang apps"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Hanapin"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Camera"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telepono"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Ilipat ang button na pamamaraan ng pag-input."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Button ng zoom ng pagiging tugma."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Mag-zoom nang mas maliit sa mas malaking screen."</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 1bf033f55ab5..9100055dbdef 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Son uygulamalar"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Ara"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Giriş yöntemini değiştirme düğmesi."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Uyumluluk zum düğmesi."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Daha büyük ekrana daha küçük yakınlaştır."</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 8df2449ebd46..a4aadb3a1c32 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"Ứng dụng gần đây"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"Tìm kiếm"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"Máy ảnh"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"Điện thoại"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Nút chuyển phương thức nhập."</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Nút thu phóng khả năng tương thích."</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Thu phóng màn hình lớn hơn hoặc nhỏ hơn."</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 8643ca81fcb6..a5ae2b7141c6 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"最近运行的应用"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"搜索"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"相机"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"电话"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"输入法切换按钮。"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"兼容性缩放按钮。"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"将小屏幕的图片放大在较大屏幕上显示。"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 07b7841b6d60..1e3f4553ceb4 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -78,8 +78,7 @@ <string name="accessibility_recent" msgid="8571350598987952883">"最近使用的應用程式"</string> <string name="accessibility_search_light" msgid="1103867596330271848">"搜尋"</string> <string name="accessibility_camera_button" msgid="8064671582820358152">"相機"</string> - <!-- no translation found for accessibility_phone_button (6738112589538563574) --> - <skip /> + <string name="accessibility_phone_button" msgid="6738112589538563574">"電話"</string> <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"切換輸入法按鈕。"</string> <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"相容性縮放按鈕。"</string> <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"將較小螢幕的畫面放大在較大螢幕上顯示。"</string> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 3f0a60f4ba35..3d3cdf6f2d2f 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -504,8 +504,6 @@ <string name="quick_settings_brightness_dialog_auto_brightness_label">AUTO</string> <!-- QuickSettings: Label for the toggle that controls whether display inversion is enabled. [CHAR LIMIT=NONE] --> <string name="quick_settings_inversion_label">Color inversion mode</string> - <!-- QuickSettings: Label for the toggle that controls whether display contrast enhancement is enabled. [CHAR LIMIT=NONE] --> - <string name="quick_settings_contrast_label">Enhanced contrast mode</string> <!-- QuickSettings: Label for the toggle that controls whether display color correction is enabled. [CHAR LIMIT=NONE] --> <string name="quick_settings_color_space_label">Color correction mode</string> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java index 281bd2d47a07..4bd0e1cf8d45 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java @@ -112,7 +112,7 @@ public abstract class ExpandableView extends FrameLayout { * @return The desired notification height. */ public int getIntrinsicHeight() { - return mActualHeight; + return getHeight(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java new file mode 100644 index 000000000000..0606a9415b1c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java @@ -0,0 +1,129 @@ +/* + * 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.systemui.statusbar; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; +import android.view.animation.PathInterpolator; + +/** + * Utility class to calculate general fling animation when the finger is released. + */ +public class FlingAnimationUtils { + + private static final float LINEAR_OUT_SLOW_IN_Y2 = 0.35f; + private static final float MAX_LENGTH_SECONDS = 0.4f; + private static final float MIN_VELOCITY_DP_PER_SECOND = 250; + + /** + * Crazy math. http://en.wikipedia.org/wiki/B%C3%A9zier_curve + */ + private static final float LINEAR_OUT_SLOW_IN_START_GRADIENT = 1/LINEAR_OUT_SLOW_IN_Y2; + + private Interpolator mLinearOutSlowIn; + private Interpolator mFastOutSlowIn; + private float mMinVelocityPxPerSecond; + + public FlingAnimationUtils(Context ctx) { + mLinearOutSlowIn = new PathInterpolator(0, 0, LINEAR_OUT_SLOW_IN_Y2, 1); + mFastOutSlowIn + = AnimationUtils.loadInterpolator(ctx, android.R.interpolator.fast_out_slow_in); + mMinVelocityPxPerSecond + = MIN_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density; + } + + /** + * Applies the interpolator and length to the animator, such that the fling animation is + * consistent with the finger motion. + * + * @param animator the animator to apply + * @param currValue the current value + * @param endValue the end value of the animator + * @param velocity the current velocity of the motion + */ + public void apply(ValueAnimator animator, float currValue, float endValue, float velocity) { + float diff = Math.abs(endValue - currValue); + float velAbs = Math.abs(velocity); + float durationSeconds = LINEAR_OUT_SLOW_IN_START_GRADIENT * diff / velAbs; + if (durationSeconds <= MAX_LENGTH_SECONDS) { + animator.setInterpolator(mLinearOutSlowIn); + } else if (velAbs >= mMinVelocityPxPerSecond) { + + // Cross fade between fast-out-slow-in and linear interpolator with current velocity. + durationSeconds = MAX_LENGTH_SECONDS; + VelocityInterpolator velocityInterpolator + = new VelocityInterpolator(durationSeconds, velAbs, diff); + InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator( + velocityInterpolator, mLinearOutSlowIn, mLinearOutSlowIn); + animator.setInterpolator(superInterpolator); + } else { + + // Just use a normal interpolator which doesn't take the velocity into account. + durationSeconds = MAX_LENGTH_SECONDS; + animator.setInterpolator(mFastOutSlowIn); + } + animator.setDuration((long) (durationSeconds * 1000)); + } + + /** + * An interpolator which interpolates two interpolators with an interpolator. + */ + private static final class InterpolatorInterpolator implements Interpolator { + + private Interpolator mInterpolator1; + private Interpolator mInterpolator2; + private Interpolator mCrossfader; + + InterpolatorInterpolator(Interpolator interpolator1, Interpolator interpolator2, + Interpolator crossfader) { + mInterpolator1 = interpolator1; + mInterpolator2 = interpolator2; + mCrossfader = crossfader; + } + + @Override + public float getInterpolation(float input) { + float t = mCrossfader.getInterpolation(input); + return (1 - t) * mInterpolator1.getInterpolation(input) + + t * mInterpolator2.getInterpolation(input); + } + } + + /** + * An interpolator which interpolates with a fixed velocity. + */ + private static final class VelocityInterpolator implements Interpolator { + + private float mDurationSeconds; + private float mVelocity; + private float mDiff; + + private VelocityInterpolator(float durationSeconds, float velocity, float diff) { + mDurationSeconds = durationSeconds; + mVelocity = velocity; + mDiff = diff; + } + + @Override + public float getInterpolation(float input) { + float time = input * mDurationSeconds; + return time * mVelocity / mDiff; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java index 864c5976993e..451c5c560171 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java @@ -34,21 +34,6 @@ public class NotificationOverflowContainer extends ActivatableNotificationView { } @Override - public void setActualHeight(int currentHeight, boolean notifyListeners) { - // noop - } - - @Override - public int getActualHeight() { - return getHeight(); - } - - @Override - public void setClipTopAmount(int clipTopAmount) { - // noop - } - - @Override protected void onFinishInflate() { super.onFinishInflate(); mIconsView = (NotificationOverflowIconsView) findViewById(R.id.overflow_icons_view); 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 2d96373a18bb..150db63979f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -32,6 +32,7 @@ import android.widget.LinearLayout; import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableView; +import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.statusbar.GestureRecorder; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; @@ -53,15 +54,21 @@ public class NotificationPanelView extends PanelView implements private int mNotificationTopPadding; private boolean mAnimateNextTopPaddingChange; - private Interpolator mExpansionInterpolator; - private int mTrackingPointer; private VelocityTracker mVelocityTracker; private boolean mTracking; + + /** + * Whether we are currently handling a motion gesture in #onInterceptTouchEvent, but haven't + * intercepted yet. + */ + private boolean mIntercepting; private boolean mQsExpanded; private float mInitialHeightOnTouch; private float mInitialTouchX; private float mInitialTouchY; + private float mLastTouchX; + private float mLastTouchY; private float mQsExpansionHeight; private int mQsMinExpansionHeight; private int mQsMaxExpansionHeight; @@ -70,6 +77,7 @@ public class NotificationPanelView extends PanelView implements private int mStackScrollerIntrinsicPadding; private boolean mQsExpansionEnabled = true; private ValueAnimator mQsExpansionAnimator; + private FlingAnimationUtils mFlingAnimationUtils; public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); @@ -107,8 +115,7 @@ public class NotificationPanelView extends PanelView implements mNotificationTopPadding = getResources().getDimensionPixelSize( R.dimen.notifications_top_padding); mMinStackHeight = getResources().getDimensionPixelSize(R.dimen.collapsed_stack_height); - mExpansionInterpolator = AnimationUtils.loadInterpolator( - getContext(), android.R.interpolator.fast_out_slow_in); + mFlingAnimationUtils = new FlingAnimationUtils(getContext()); } @Override @@ -195,6 +202,7 @@ public class NotificationPanelView extends PanelView implements switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: + mIntercepting = true; mInitialTouchY = y; mInitialTouchX = x; initVelocityTracker(); @@ -217,6 +225,16 @@ public class NotificationPanelView extends PanelView implements case MotionEvent.ACTION_MOVE: final float h = y - mInitialTouchY; trackMovement(event); + if (mTracking) { + + // Already tracking because onOverscrolled was called. We need to update here + // so we don't stop for a frame until the next touch event gets handled in + // onTouchEvent. + setQsExpansion(h + mInitialHeightOnTouch); + trackMovement(event); + mIntercepting = false; + return true; + } if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX) && shouldIntercept(mInitialTouchX, mInitialTouchY, h)) { onQsExpansionStarted(); @@ -224,14 +242,41 @@ public class NotificationPanelView extends PanelView implements mInitialTouchY = y; mInitialTouchX = x; mTracking = true; + mIntercepting = false; return true; } break; + + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + trackMovement(event); + if (mTracking) { + flingWithCurrentVelocity(); + mTracking = false; + } + mIntercepting = false; + break; } return !mQsExpanded && super.onInterceptTouchEvent(event); } @Override + public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { + + // Block request so we can still intercept the scrolling when QS is expanded. + if (!mQsExpanded) { + super.requestDisallowInterceptTouchEvent(disallowIntercept); + } + } + + private void flingWithCurrentVelocity() { + float vel = getCurrentVelocity(); + + // TODO: Better logic whether we should expand or not. + flingSettings(vel, vel > 0); + } + + @Override public boolean onTouchEvent(MotionEvent event) { // TODO: Handle doublefinger swipe to notifications again. Look at history for a reference // implementation. @@ -280,12 +325,7 @@ public class NotificationPanelView extends PanelView implements mTracking = false; mTrackingPointer = -1; trackMovement(event); - - float vel = getCurrentVelocity(); - - // TODO: Better logic whether we should expand or not. - flingSettings(vel, vel > 0); - + flingWithCurrentVelocity(); if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; @@ -299,11 +339,26 @@ public class NotificationPanelView extends PanelView implements return mQsExpanded || super.onTouchEvent(event); } + @Override + public void onOverscrolled(int amount) { + if (mIntercepting) { + onQsExpansionStarted(amount); + mInitialHeightOnTouch = mQsExpansionHeight; + mInitialTouchY = mLastTouchY; + mInitialTouchX = mLastTouchX; + mTracking = true; + } + } + private void onQsExpansionStarted() { + onQsExpansionStarted(0); + } + + private void onQsExpansionStarted(int overscrollAmount) { cancelAnimation(); // Reset scroll position and apply that position to the expanded height. - float height = mQsExpansionHeight - mScrollView.getScrollY(); + float height = mQsExpansionHeight - mScrollView.getScrollY() - overscrollAmount; mScrollView.scrollTo(0, 0); setQsExpansion(height); } @@ -361,6 +416,8 @@ public class NotificationPanelView extends PanelView implements private void trackMovement(MotionEvent event) { if (mVelocityTracker != null) mVelocityTracker.addMovement(event); + mLastTouchX = event.getX(); + mLastTouchY = event.getY(); } private void initVelocityTracker() { @@ -384,13 +441,9 @@ public class NotificationPanelView extends PanelView implements } } private void flingSettings(float vel, boolean expand) { - - // TODO: Actually use velocity. - float target = expand ? mQsMaxExpansionHeight : mQsMinExpansionHeight; ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target); - animator.setDuration(EXPANSION_ANIMATION_LENGTH); - animator.setInterpolator(mExpansionInterpolator); + mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { @@ -414,11 +467,8 @@ public class NotificationPanelView extends PanelView implements if (!mQsExpansionEnabled) { return false; } - View headerView = mStatusBar.getBarState() == StatusBarState.KEYGUARD && !mQsExpanded - ? mKeyguardStatusView - : mHeader; - boolean onHeader = x >= headerView.getLeft() && x <= headerView.getRight() - && y >= headerView.getTop() && y <= headerView.getBottom(); + boolean onHeader = x >= mHeader.getLeft() && x <= mHeader.getRight() + && y >= mHeader.getTop() && y <= mHeader.getBottom(); if (mQsExpanded) { return onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java index 46484f3acd11..ba0b66e47b75 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java @@ -27,6 +27,7 @@ import android.widget.ScrollView; public class ObservableScrollView extends ScrollView { private Listener mListener; + private int mLastOverscrollAmount; public ObservableScrollView(Context context, AttributeSet attrs) { super(context, attrs); @@ -58,7 +59,25 @@ public class ObservableScrollView extends ScrollView { } } + @Override + protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, + int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, + boolean isTouchEvent) { + mLastOverscrollAmount = Math.max(0, scrollY + deltaY - getMaxScrollY()); + return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, + maxOverScrollX, maxOverScrollY, isTouchEvent); + } + + @Override + protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { + super.onOverScrolled(scrollX, scrollY, clampedX, clampedY); + if (mListener != null && mLastOverscrollAmount > 0) { + mListener.onOverscrolled(mLastOverscrollAmount); + } + } + public interface Listener { void onScrollChanged(); + void onOverscrolled(int amount); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java index f3ebf1ba5431..8ce7279d32ae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java @@ -323,14 +323,6 @@ class QuickSettings { mModel.addInversionTile(inversionTile, inversionTile.getRefreshCallback()); parent.addView(inversionTile); - // Contrast enhancement tile - final SystemSettingTile contrastTile = new SystemSettingTile(mContext); - contrastTile.setUri(Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED, - SystemSettingTile.TYPE_SECURE); - contrastTile.setFragment("Settings$AccessibilityContrastSettingsActivity"); - mModel.addContrastTile(contrastTile, contrastTile.getRefreshCallback()); - parent.addView(contrastTile); - // Color space adjustment tile final SystemSettingTile colorSpaceTile = new SystemSettingTile(mContext); colorSpaceTile.setUri(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java index 005b0d14dcf3..9b90d3d86ce1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java @@ -95,12 +95,6 @@ class QuickSettingsModel implements BluetoothStateChangeCallback, } static class InversionState extends State { boolean toggled; - int type; - } - static class ContrastState extends State { - boolean toggled; - float contrast; - float brightness; } static class ColorSpaceState extends State { boolean toggled; @@ -237,38 +231,6 @@ class QuickSettingsModel implements BluetoothStateChangeCallback, cr.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_QUICK_SETTING_ENABLED), false, this, mUserTracker.getCurrentUserId()); - cr.registerContentObserver(Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION), - false, this, mUserTracker.getCurrentUserId()); - } - } - - /** ContentObserver to watch display contrast */ - private class DisplayContrastObserver extends ContentObserver { - public DisplayContrastObserver(Handler handler) { - super(handler); - } - - @Override - public void onChange(boolean selfChange) { - onContrastChanged(); - } - - public void startObserving() { - final ContentResolver cr = mContext.getContentResolver(); - cr.unregisterContentObserver(this); - cr.registerContentObserver(Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED), - false, this, mUserTracker.getCurrentUserId()); - cr.registerContentObserver(Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_QUICK_SETTING_ENABLED), - false, this, mUserTracker.getCurrentUserId()); - cr.registerContentObserver(Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST), - false, this, mUserTracker.getCurrentUserId()); - cr.registerContentObserver(Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_DISPLAY_BRIGHTNESS), - false, this, mUserTracker.getCurrentUserId()); } } @@ -348,7 +310,6 @@ class QuickSettingsModel implements BluetoothStateChangeCallback, private final BugreportObserver mBugreportObserver; private final BrightnessObserver mBrightnessObserver; private final DisplayInversionObserver mInversionObserver; - private final DisplayContrastObserver mContrastObserver; private final DisplayColorSpaceObserver mColorSpaceObserver; private final ZenModeObserver mZenModeObserver; @@ -417,10 +378,6 @@ class QuickSettingsModel implements BluetoothStateChangeCallback, private RefreshCallback mInversionCallback; private InversionState mInversionState = new InversionState(); - private QuickSettingsTileView mContrastTile; - private RefreshCallback mContrastCallback; - private ContrastState mContrastState = new ContrastState(); - private QuickSettingsTileView mColorSpaceTile; private RefreshCallback mColorSpaceCallback; private ColorSpaceState mColorSpaceState = new ColorSpaceState(); @@ -448,12 +405,10 @@ class QuickSettingsModel implements BluetoothStateChangeCallback, public void onUserSwitched(int newUserId) { mBrightnessObserver.startObserving(); mInversionObserver.startObserving(); - mContrastObserver.startObserving(); mColorSpaceObserver.startObserving(); refreshRotationLockTile(); onBrightnessLevelChanged(); onInversionChanged(); - onContrastChanged(); onColorSpaceChanged(); onNextAlarmChanged(); onBugreportChanged(); @@ -470,8 +425,6 @@ class QuickSettingsModel implements BluetoothStateChangeCallback, mBrightnessObserver.startObserving(); mInversionObserver = new DisplayInversionObserver(mHandler); mInversionObserver.startObserving(); - mContrastObserver = new DisplayContrastObserver(mHandler); - mContrastObserver.startObserving(); mColorSpaceObserver = new DisplayColorSpaceObserver(mHandler); mColorSpaceObserver.startObserving(); mZenModeObserver = new ZenModeObserver(mHandler); @@ -1037,11 +990,8 @@ class QuickSettingsModel implements BluetoothStateChangeCallback, currentUserId) == 1; final boolean enabled = Settings.Secure.getIntForUser( cr, Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, currentUserId) == 1; - final int type = Settings.Secure.getIntForUser( - cr, Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION, 0, currentUserId); mInversionState.enabled = quickSettingEnabled; mInversionState.toggled = enabled; - mInversionState.type = type; // TODO: Add real icon assets. mInversionState.iconId = enabled ? R.drawable.ic_qs_inversion_on : R.drawable.ic_qs_inversion_off; @@ -1049,36 +999,6 @@ class QuickSettingsModel implements BluetoothStateChangeCallback, mInversionCallback.refreshView(mInversionTile, mInversionState); } - // Contrast enhancement - void addContrastTile(QuickSettingsTileView view, RefreshCallback cb) { - mContrastTile = view; - mContrastCallback = cb; - onContrastChanged(); - } - public void onContrastChanged() { - final Resources res = mContext.getResources(); - final ContentResolver cr = mContext.getContentResolver(); - final int currentUserId = mUserTracker.getCurrentUserId(); - final boolean quickSettingEnabled = Settings.Secure.getIntForUser( - cr, Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_QUICK_SETTING_ENABLED, 0, - currentUserId) == 1; - final boolean enabled = Settings.Secure.getIntForUser( - cr, Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED, 0, currentUserId) == 1; - final float contrast = Settings.Secure.getFloatForUser( - cr, Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST, 1, currentUserId); - final float brightness = Settings.Secure.getFloatForUser( - cr, Settings.Secure.ACCESSIBILITY_DISPLAY_BRIGHTNESS, 0, currentUserId); - mContrastState.enabled = quickSettingEnabled; - mContrastState.toggled = enabled; - mContrastState.contrast = contrast; - mContrastState.brightness = brightness; - // TODO: Add real icon assets. - mContrastState.iconId = enabled ? R.drawable.ic_qs_contrast_on - : R.drawable.ic_qs_contrast_off; - mContrastState.label = res.getString(R.string.quick_settings_contrast_label); - mContrastCallback.refreshView(mContrastTile, mContrastState); - } - // Color space adjustment void addColorSpaceTile(QuickSettingsTileView view, RefreshCallback cb) { mColorSpaceTile = view; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java index 2b78a15d6afb..552747353a94 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java @@ -18,6 +18,8 @@ package com.android.systemui.statusbar.phone; import android.content.Context; import android.content.Intent; +import android.graphics.Outline; +import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; @@ -59,6 +61,9 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL private ActivityStarter mActivityStarter; private BrightnessController mBrightnessController; + private final Rect mClipBounds = new Rect(); + private final Outline mOutline = new Outline(); + public StatusBarHeaderView(Context context, AttributeSet attrs) { super(context, attrs); } @@ -114,10 +119,11 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL } private void updateHeights() { + boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded; int height; if (mExpanded) { height = mExpandedHeight; - } else if (mKeyguardShowing) { + } else if (onKeyguardAndCollapsed) { height = mKeyguardHeight; } else { height = mCollapsedHeight; @@ -127,7 +133,7 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL lp.height = height; setLayoutParams(lp); } - int systemIconsContainerHeight = mKeyguardShowing ? mKeyguardHeight : mCollapsedHeight; + int systemIconsContainerHeight = onKeyguardAndCollapsed ? mKeyguardHeight : mCollapsedHeight; lp = mSystemIconsContainer.getLayoutParams(); if (lp.height != systemIconsContainerHeight) { lp.height = systemIconsContainerHeight; @@ -150,9 +156,10 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL } private void updateVisibilities() { - mBackground.setVisibility(mKeyguardShowing ? View.INVISIBLE : View.VISIBLE); - mDateTime.setVisibility(mKeyguardShowing ? View.INVISIBLE : View.VISIBLE); - mKeyguardCarrierText.setVisibility(mKeyguardShowing ? View.VISIBLE : View.GONE); + boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded; + mBackground.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE); + mDateTime.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE); + mKeyguardCarrierText.setVisibility(onKeyguardAndCollapsed ? View.VISIBLE : View.GONE); mDate.setVisibility(mExpanded ? View.VISIBLE : View.GONE); mSettingsButton.setVisibility(mExpanded ? View.VISIBLE : View.GONE); mBrightnessContainer.setVisibility(mExpanded ? View.VISIBLE : View.GONE); @@ -191,6 +198,14 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL } else { mBackground.setTranslationY(0); } + setClipping(height); + } + + private void setClipping(float height) { + mClipBounds.set(getPaddingLeft(), 0, getWidth() - getPaddingRight(), (int) height); + setClipBounds(mClipBounds); + mOutline.setRect(mClipBounds); + setOutline(mOutline); } public View getBackgroundView() { 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 27f661992cb1..5849afbfad8a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -80,9 +80,12 @@ public class NotificationStackScrollLayout extends ViewGroup private Paint mDebugPaint; private int mContentHeight; private int mCollapsedSize; + private int mBottomStackSlowDownHeight; private int mBottomStackPeekSize; private int mEmptyMarginBottom; private int mPaddingBetweenElements; + private int mPaddingBetweenElementsDimmed; + private int mPaddingBetweenElementsNormal; private int mTopPadding; /** @@ -154,7 +157,7 @@ public class NotificationStackScrollLayout extends ViewGroup int y = mCollapsedSize; canvas.drawLine(0, y, getWidth(), y, mDebugPaint); y = (int) (getLayoutHeight() - mBottomStackPeekSize - - mStackScrollAlgorithm.getBottomStackSlowDownLength()); + - mBottomStackSlowDownHeight); canvas.drawLine(0, y, getWidth(), y, mDebugPaint); y = (int) (getLayoutHeight() - mBottomStackPeekSize); canvas.drawLine(0, y, getWidth(), y, mDebugPaint); @@ -186,9 +189,20 @@ public class NotificationStackScrollLayout extends ViewGroup .getDimensionPixelSize(R.dimen.bottom_stack_peek_amount); mEmptyMarginBottom = context.getResources().getDimensionPixelSize( R.dimen.notification_stack_margin_bottom); - mPaddingBetweenElements = context.getResources() - .getDimensionPixelSize(R.dimen.notification_padding); mStackScrollAlgorithm = new StackScrollAlgorithm(context); + mPaddingBetweenElementsDimmed = context.getResources() + .getDimensionPixelSize(R.dimen.notification_padding_dimmed); + mPaddingBetweenElementsNormal = context.getResources() + .getDimensionPixelSize(R.dimen.notification_padding); + updatePadding(false); + } + + private void updatePadding(boolean dimmed) { + mPaddingBetweenElements = dimmed + ? mPaddingBetweenElementsDimmed + : mPaddingBetweenElementsNormal; + mBottomStackSlowDownHeight = mStackScrollAlgorithm.getBottomStackSlowDownLength(); + updateContentHeight(); } @Override @@ -742,15 +756,10 @@ public class NotificationStackScrollLayout extends ViewGroup if (firstChild != null) { int contentHeight = getContentHeight(); int firstChildMaxExpandHeight = getMaxExpandHeight(firstChild); - - scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight + mBottomStackPeekSize); + scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight + mBottomStackPeekSize + + mBottomStackSlowDownHeight); if (scrollRange > 0) { View lastChild = getLastChildNotGone(); - if (isViewExpanded(lastChild)) { - // last child is expanded, so we have to ensure that it can exit the - // bottom stack - scrollRange += mCollapsedSize + mPaddingBetweenElements; - } // We want to at least be able collapse the first item and not ending in a weird // end state. scrollRange = Math.max(scrollRange, firstChildMaxExpandHeight - mCollapsedSize); @@ -1184,7 +1193,7 @@ public class NotificationStackScrollLayout extends ViewGroup public int getEmptyBottomMargin() { int emptyMargin = mMaxLayoutHeight - mContentHeight; if (needsHeightAdaption()) { - emptyMargin = emptyMargin - mCollapsedSize - mBottomStackPeekSize; + emptyMargin = emptyMargin - mBottomStackSlowDownHeight - mBottomStackPeekSize; } return Math.max(emptyMargin, 0); } @@ -1231,6 +1240,7 @@ public class NotificationStackScrollLayout extends ViewGroup public void setDimmed(boolean dimmed, boolean animate) { mStackScrollAlgorithm.setDimmed(dimmed); mAmbientState.setDimmed(dimmed); + updatePadding(dimmed); if (animate) { mDimmedNeedsAnimation = true; mNeedsAnimation = true; diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 4a59a8f063bf..36b5cfb4045f 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -3698,17 +3698,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private final Uri mEnhancedWebAccessibilityUri = Settings.Secure .getUriFor(Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION); - private final Uri mDisplayContrastEnabledUri = Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED); - private final Uri mDisplayContrastUri = Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST); - private final Uri mDisplayBrightnessUri = Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_DISPLAY_BRIGHTNESS); - private final Uri mDisplayInversionEnabledUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED); - private final Uri mDisplayInversionUri = Settings.Secure.getUriFor( - Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION); private final Uri mDisplayDaltonizerEnabledUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED); @@ -3734,16 +3725,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { contentResolver.registerContentObserver(mEnhancedWebAccessibilityUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( - mDisplayContrastEnabledUri, false, this, UserHandle.USER_ALL); - contentResolver.registerContentObserver( - mDisplayContrastUri, false, this, UserHandle.USER_ALL); - contentResolver.registerContentObserver( - mDisplayBrightnessUri, false, this, UserHandle.USER_ALL); - contentResolver.registerContentObserver( mDisplayInversionEnabledUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( - mDisplayInversionUri, false, this, UserHandle.USER_ALL); - contentResolver.registerContentObserver( mDisplayDaltonizerEnabledUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( mDisplayDaltonizerUri, false, this, UserHandle.USER_ALL); @@ -3823,12 +3806,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } } - } else if (mDisplayContrastEnabledUri.equals(uri) - || mDisplayInversionEnabledUri.equals(uri) + } else if (mDisplayInversionEnabledUri.equals(uri) || mDisplayDaltonizerEnabledUri.equals(uri) - || mDisplayContrastUri.equals(uri) - || mDisplayBrightnessUri.equals(uri) - || mDisplayInversionUri.equals(uri) || mDisplayDaltonizerUri.equals(uri)) { synchronized (mLock) { // Profiles share the accessibility state of the parent. Therefore, diff --git a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java b/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java index 52bdedadb210..394c196c9a60 100644 --- a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java +++ b/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java @@ -41,22 +41,6 @@ class DisplayAdjustmentUtils { 0, 0, 0, 1 }; - /** Matrix and offset used for standard display inversion. */ - private static final float[] INVERSION_MATRIX_STANDARD = new float[] { - -1, 0, 0, 0, - 0, -1, 0, 0, - 0, 0, -1, 0, - 1, 1, 1, 1 - }; - - /** Matrix and offset used for hue-only display inversion. */ - private static final float[] INVERSION_MATRIX_HUE_ONLY = new float[] { - 0, .5f, .5f, 0, - .5f, 0, .5f, 0, - .5f, .5f, 0, 0, - 0, 0, 0, 1 - }; - /** Matrix and offset used for value-only display inversion. */ private static final float[] INVERSION_MATRIX_VALUE_ONLY = new float[] { 0, -.5f, -.5f, 0, @@ -65,15 +49,6 @@ class DisplayAdjustmentUtils { 1, 1, 1, 1 }; - /** Default contrast for display contrast enhancement. */ - private static final float DEFAULT_DISPLAY_CONTRAST = 2; - - /** Default brightness for display contrast enhancement. */ - private static final float DEFAULT_DISPLAY_BRIGHTNESS = 0; - - /** Default inversion mode for display color inversion. */ - private static final int DEFAULT_DISPLAY_INVERSION = AccessibilityManager.INVERSION_STANDARD; - /** Default inversion mode for display color correction. */ private static final int DEFAULT_DISPLAY_DALTONIZER = AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY; @@ -90,11 +65,6 @@ class DisplayAdjustmentUtils { if (!hasColorTransform) { hasColorTransform |= Settings.Secure.getIntForUser( - cr, Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED, 0, userId) == 1; - } - - if (!hasColorTransform) { - hasColorTransform |= Settings.Secure.getIntForUser( cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, userId) == 1; } @@ -115,21 +85,7 @@ class DisplayAdjustmentUtils { final boolean inversionEnabled = Settings.Secure.getIntForUser( cr, Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, userId) == 1; if (inversionEnabled) { - final int inversionMode = Settings.Secure.getIntForUser(cr, - Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION, DEFAULT_DISPLAY_INVERSION, - userId); - final float[] inversionMatrix; - switch (inversionMode) { - case AccessibilityManager.INVERSION_HUE_ONLY: - inversionMatrix = INVERSION_MATRIX_HUE_ONLY; - break; - case AccessibilityManager.INVERSION_VALUE_ONLY: - inversionMatrix = INVERSION_MATRIX_VALUE_ONLY; - break; - default: - inversionMatrix = INVERSION_MATRIX_STANDARD; - } - + final float[] inversionMatrix = INVERSION_MATRIX_VALUE_ONLY; Matrix.multiplyMM(outputMatrix, 0, colorMatrix, 0, inversionMatrix, 0); colorMatrix = outputMatrix; @@ -138,31 +94,6 @@ class DisplayAdjustmentUtils { hasColorTransform = true; } - final boolean contrastEnabled = Settings.Secure.getIntForUser( - cr, Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED, 0, userId) == 1; - if (contrastEnabled) { - final float contrast = Settings.Secure.getFloatForUser(cr, - Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST, DEFAULT_DISPLAY_CONTRAST, - userId); - final float brightness = Settings.Secure.getFloatForUser(cr, - Settings.Secure.ACCESSIBILITY_DISPLAY_BRIGHTNESS, DEFAULT_DISPLAY_BRIGHTNESS, - userId); - final float off = brightness * contrast - 0.5f * contrast + 0.5f; - final float[] contrastMatrix = { - contrast, 0, 0, 0, - 0, contrast, 0, 0, - 0, 0, contrast, 0, - off, off, off, 1 - }; - - Matrix.multiplyMM(outputMatrix, 0, colorMatrix, 0, contrastMatrix, 0); - - colorMatrix = outputMatrix; - outputMatrix = colorMatrix; - - hasColorTransform = true; - } - final boolean daltonizerEnabled = Settings.Secure.getIntForUser( cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, userId) != 0; if (daltonizerEnabled) { diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index dfffa8a46370..ce2aefb6b5ac 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -65,6 +65,7 @@ import android.net.LinkProperties; import android.net.LinkProperties.CompareResult; import android.net.LinkQualityInfo; import android.net.MobileDataStateTracker; +import android.net.Network; import android.net.NetworkConfig; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; @@ -165,6 +166,8 @@ import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSession; +import static android.net.ConnectivityManager.INVALID_NET_ID; + /** * @hide */ @@ -442,6 +445,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { TelephonyManager mTelephonyManager; + private final static int MIN_NET_ID = 10; // some reserved marks + private final static int MAX_NET_ID = 65535; + private int mNextNetId = MIN_NET_ID; + public ConnectivityService(Context context, INetworkManagementService netd, INetworkStatsService statsService, INetworkPolicyManager policyManager) { // Currently, omitting a NetworkFactory will create one internally @@ -706,6 +713,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); } + private synchronized int nextNetId() { + int netId = mNextNetId; + if (++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID; + return netId; + } + /** * Factory that creates {@link NetworkStateTracker} instances using given * {@link NetworkConfig}. @@ -1104,7 +1117,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (tracker != null) { final NetworkInfo info = getFilteredNetworkInfo(tracker, uid); result.add(new NetworkState( - info, tracker.getLinkProperties(), tracker.getLinkCapabilities())); + info, tracker.getLinkProperties(), tracker.getNetworkCapabilities())); } } } @@ -1116,7 +1129,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { final NetworkStateTracker tracker = mNetTrackers[networkType]; if (tracker != null) { return new NetworkState(tracker.getNetworkInfo(), tracker.getLinkProperties(), - tracker.getLinkCapabilities()); + tracker.getNetworkCapabilities()); } } return null; @@ -1984,6 +1997,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { int prevNetType = info.getType(); mNetTrackers[prevNetType].setTeardownRequested(false); + int thisNetId = mNetTrackers[prevNetType].getNetwork().netId; // Remove idletimer previously setup in {@code handleConnect} if (mNetConfigs[prevNetType].isDefault()) { @@ -2069,6 +2083,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { sendConnectedBroadcastDelayed(mNetTrackers[mActiveDefaultNetwork].getNetworkInfo(), getConnectivityChangeDelay()); } + try { +// mNetd.removeNetwork(thisNetId); + } catch (Exception e) { + loge("Exception removing network: " + e); + } finally { + mNetTrackers[prevNetType].setNetId(INVALID_NET_ID); + } } private void tryFailover(int prevNetType) { @@ -2336,17 +2357,23 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (mNetConfigs[newNetType].isDefault()) { if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != newNetType) { if (isNewNetTypePreferredOverCurrentNetType(newNetType)) { - // tear down the other - NetworkStateTracker otherNet = - mNetTrackers[mActiveDefaultNetwork]; - if (DBG) { - log("Policy requires " + otherNet.getNetworkInfo().getTypeName() + - " teardown"); - } - if (!teardown(otherNet)) { - loge("Network declined teardown request"); - teardown(thisNet); - return; + String teardownPolicy = SystemProperties.get("net.teardownPolicy"); + if (TextUtils.equals(teardownPolicy, "keep") == false) { + // tear down the other + NetworkStateTracker otherNet = + mNetTrackers[mActiveDefaultNetwork]; + if (DBG) { + log("Policy requires " + otherNet.getNetworkInfo().getTypeName() + + " teardown"); + } + if (!teardown(otherNet)) { + loge("Network declined teardown request"); + teardown(thisNet); + return; + } + } else { + //TODO - remove + loge("network teardown skipped due to net.teardownPolicy setting"); } } else { // don't accept this one @@ -2358,6 +2385,15 @@ public class ConnectivityService extends IConnectivityManager.Stub { return; } } + int thisNetId = nextNetId(); + thisNet.setNetId(thisNetId); + try { +// mNetd.createNetwork(thisNetId, thisIface); + } catch (Exception e) { + loge("Exception creating network :" + e); + teardown(thisNet); + return; + } setupDataActivityTracking(newNetType); synchronized (ConnectivityService.this) { // have a new default network, release the transition wakelock in a second @@ -2380,6 +2416,16 @@ public class ConnectivityService extends IConnectivityManager.Stub { // Don't do this - if we never sign in stay, grey //reportNetworkCondition(mActiveDefaultNetwork, 100); updateNetworkSettings(thisNet); + } else { + int thisNetId = nextNetId(); + thisNet.setNetId(thisNetId); + try { +// mNetd.createNetwork(thisNetId, thisIface); + } catch (Exception e) { + loge("Exception creating network :" + e); + teardown(thisNet); + return; + } } thisNet.setTeardownRequested(false); updateMtuSizeSettings(thisNet); diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 10315a713261..0b9570d695b7 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -1464,6 +1464,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private boolean needsToShowImeSwitchOngoingNotification() { if (!mShowOngoingImeSwitcherForPhones) return false; + if (mSwitchingDialog != null) return false; if (isScreenLocked()) return false; synchronized (mMethodMap) { List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked(); @@ -2812,6 +2813,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mSwitchingDialog.getWindow().getAttributes().privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; mSwitchingDialog.getWindow().getAttributes().setTitle("Select input method"); + updateImeWindowStatusLocked(); mSwitchingDialog.show(); } } @@ -2869,6 +2871,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mSwitchingDialog = null; } + updateImeWindowStatusLocked(); mDialogBuilder = null; mIms = null; } diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index d6ecb4663da3..e54e5d0d462c 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -427,7 +427,7 @@ public class LocationManagerService extends ILocationManager.Stub { } // bind to fused provider if supported - if (FlpHardwareProvider.isSupported()) { + if (FlpHardwareProvider.getInstance(mContext).isSupported()) { FlpHardwareProvider flpHardwareProvider = FlpHardwareProvider.getInstance(mContext); FusedProxy fusedProxy = FusedProxy.createAndBind( mContext, diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java index 0d1e122afc25..96f9ab061c10 100644 --- a/services/core/java/com/android/server/NativeDaemonConnector.java +++ b/services/core/java/com/android/server/NativeDaemonConnector.java @@ -50,6 +50,8 @@ import java.util.LinkedList; final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor { private static final boolean LOGD = false; + private final static boolean VDBG = false; + private final String TAG; private String mSocket; @@ -409,7 +411,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo loge("timed-out waiting for response to " + logCmd); throw new NativeDaemonFailureException(logCmd, event); } - log("RMV <- {" + event + "}"); + if (VDBG) log("RMV <- {" + event + "}"); events.add(event); } while (event.isClassContinue()); diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 7ce45f7edff8..b9c86dc5a9e2 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -2029,4 +2029,24 @@ public class NetworkManagementService extends INetworkManagementService.Stub pw.print("Firewall enabled: "); pw.println(mFirewallEnabled); } + + public void createNetwork(int netId, String iface) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + + try { + mConnector.execute("network", "create", netId, iface); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + } + + public void removeNetwork(int netId) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + + try { + mConnector.execute("network", "destroy", netId); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + } } diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java index 4f0c9b537ff2..512ebc6599f3 100644 --- a/services/core/java/com/android/server/NetworkScoreService.java +++ b/services/core/java/com/android/server/NetworkScoreService.java @@ -171,6 +171,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub { return; } writer.println("Current scorer: " + currentScorer); + writer.flush(); for (INetworkScoreCache scoreCache : getScoreCaches()) { try { diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index d4565b61bca1..cfaf016f7a1e 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -22,8 +22,8 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; -import android.net.LinkCapabilities; import android.net.LinkProperties; +import android.net.NetworkCapabilities; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -120,7 +120,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { private LinkProperties mDataConnectionLinkProperties; - private LinkCapabilities mDataConnectionLinkCapabilities; + private NetworkCapabilities mDataConnectionNetworkCapabilities; private Bundle mCellLocation = new Bundle(); @@ -553,7 +553,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { public void notifyDataConnection(int state, boolean isDataConnectivityPossible, String reason, String apn, String apnType, LinkProperties linkProperties, - LinkCapabilities linkCapabilities, int networkType, boolean roaming) { + NetworkCapabilities networkCapabilities, int networkType, boolean roaming) { if (!checkNotifyPermission("notifyDataConnection()" )) { return; } @@ -587,7 +587,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { mDataConnectionPossible = isDataConnectivityPossible; mDataConnectionReason = reason; mDataConnectionLinkProperties = linkProperties; - mDataConnectionLinkCapabilities = linkCapabilities; + mDataConnectionNetworkCapabilities = networkCapabilities; if (mDataConnectionNetworkType != networkType) { mDataConnectionNetworkType = networkType; // need to tell registered listeners about the new network type @@ -624,7 +624,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { handleRemoveListLocked(); } broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn, - apnType, linkProperties, linkCapabilities, roaming); + apnType, linkProperties, networkCapabilities, roaming); broadcastPreciseDataConnectionStateChanged(state, networkType, apnType, apn, reason, linkProperties, ""); } @@ -794,7 +794,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println(" mDataConnectionReason=" + mDataConnectionReason); pw.println(" mDataConnectionApn=" + mDataConnectionApn); pw.println(" mDataConnectionLinkProperties=" + mDataConnectionLinkProperties); - pw.println(" mDataConnectionLinkCapabilities=" + mDataConnectionLinkCapabilities); + pw.println(" mDataConnectionNetworkCapabilities=" + + mDataConnectionNetworkCapabilities); pw.println(" mCellLocation=" + mCellLocation); pw.println(" mCellInfo=" + mCellInfo); pw.println(" mDcRtInfo=" + mDcRtInfo); @@ -862,7 +863,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { private void broadcastDataConnectionStateChanged(int state, boolean isDataConnectivityPossible, String reason, String apn, String apnType, LinkProperties linkProperties, - LinkCapabilities linkCapabilities, boolean roaming) { + NetworkCapabilities networkCapabilities, boolean roaming) { // Note: not reporting to the battery stats service here, because the // status bar takes care of that after taking into account all of the // required info. @@ -882,8 +883,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { intent.putExtra(PhoneConstants.DATA_IFACE_NAME_KEY, iface); } } - if (linkCapabilities != null) { - intent.putExtra(PhoneConstants.DATA_LINK_CAPABILITIES_KEY, linkCapabilities); + if (networkCapabilities != null) { + intent.putExtra(PhoneConstants.DATA_NETWORK_CAPABILITIES_KEY, networkCapabilities); } if (roaming) intent.putExtra(PhoneConstants.DATA_NETWORK_ROAMING_KEY, true); diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index f587cccdd419..b2aaf74b7115 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -34,6 +34,7 @@ import android.app.AppGlobals; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; +import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentValues; @@ -857,7 +858,7 @@ public class AccountManagerService checkManageAccountsPermission(); UserHandle user = Binder.getCallingUserHandle(); UserAccounts accounts = getUserAccountsForCaller(); - if (!canUserModifyAccounts(Binder.getCallingUid())) { + if (!canUserModifyAccounts(Binder.getCallingUid(), account.type)) { try { response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION, "User cannot modify accounts"); @@ -1512,7 +1513,7 @@ public class AccountManagerService checkManageAccountsPermission(); // Is user disallowed from modifying accounts? - if (!canUserModifyAccounts(Binder.getCallingUid())) { + if (!canUserModifyAccounts(Binder.getCallingUid(), accountType)) { try { response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, "User is not allowed to add an account!"); @@ -2758,7 +2759,7 @@ public class AccountManagerService Manifest.permission.USE_CREDENTIALS); } - private boolean canUserModifyAccounts(int callingUid) { + private boolean canUserModifyAccounts(int callingUid, String accountType) { if (callingUid != Process.myUid()) { if (getUserManager().getUserRestrictions( new UserHandle(UserHandle.getUserId(callingUid))) @@ -2766,6 +2767,15 @@ public class AccountManagerService return false; } } + + DevicePolicyManager dpm = (DevicePolicyManager) mContext + .getSystemService(Context.DEVICE_POLICY_SERVICE); + String[] typesArray = dpm.getAccountTypesWithManagementDisabled(); + for (String forbiddenType : typesArray) { + if (forbiddenType.equals(accountType)) { + return false; + } + } return true; } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 5358c1aaca52..344268aa962e 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1328,14 +1328,12 @@ public final class ActivityManagerService extends ActivityManagerNative String host = ""; String port = ""; String exclList = ""; - String pacFileUrl = ""; + Uri pacFileUrl = Uri.EMPTY; if (proxy != null) { host = proxy.getHost(); port = Integer.toString(proxy.getPort()); exclList = proxy.getExclusionListAsString(); - if (proxy.getPacFileUrl() != null) { - pacFileUrl = proxy.getPacFileUrl().toString(); - } + pacFileUrl = proxy.getPacFileUrl(); } synchronized (ActivityManagerService.this) { for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) { diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 7c29d85c70ad..2e439aeb323e 100755 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -1421,7 +1421,8 @@ final class ActivityStack { final TaskRecord nextTask = next.task; final TaskRecord prevTask = prev != null ? prev.task : null; - if (prevTask != null && prevTask.mOnTopOfHome && prev.finishing && prev.frontOfTask) { + if (prevTask != null && prevTask.stack == this && + prevTask.mOnTopOfHome && prev.finishing && prev.frontOfTask) { if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked(); if (prevTask == nextTask) { prevTask.setFrontOfTask(); diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java index 0749f242311b..63178eb0e06b 100644 --- a/services/core/java/com/android/server/connectivity/PacManager.java +++ b/services/core/java/com/android/server/connectivity/PacManager.java @@ -25,6 +25,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.net.ProxyInfo; +import android.net.Uri; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; @@ -32,7 +33,6 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.provider.Settings; -import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -71,7 +71,7 @@ public class PacManager { public static final String KEY_PROXY = "keyProxy"; private String mCurrentPac; @GuardedBy("mProxyLock") - private String mPacUrl; + private Uri mPacUrl; private AlarmManager mAlarmManager; @GuardedBy("mProxyLock") @@ -100,7 +100,7 @@ public class PacManager { public void run() { String file; synchronized (mProxyLock) { - if (mPacUrl == null) return; + if (Uri.EMPTY.equals(mPacUrl)) return; try { file = get(mPacUrl); } catch (IOException ioe) { @@ -158,13 +158,13 @@ public class PacManager { * @return Returns true when the broadcast should not be sent */ public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) { - if (proxy.getPacFileUrl() != null) { + if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) { if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) { // Allow to send broadcast, nothing to do. return false; } synchronized (mProxyLock) { - mPacUrl = proxy.getPacFileUrl().toString(); + mPacUrl = proxy.getPacFileUrl(); } mCurrentDelay = DELAY_1; mHasSentBroadcast = false; @@ -196,8 +196,8 @@ public class PacManager { * * @throws IOException */ - private static String get(String urlString) throws IOException { - URL url = new URL(urlString); + private static String get(Uri pacUri) throws IOException { + URL url = new URL(pacUri.toString()); URLConnection urlConnection = url.openConnection(java.net.Proxy.NO_PROXY); return new String(Streams.readFully(urlConnection.getInputStream())); } diff --git a/services/core/java/com/android/server/location/FlpHardwareProvider.java b/services/core/java/com/android/server/location/FlpHardwareProvider.java index 09f1c567dd59..51ee93bbb266 100644 --- a/services/core/java/com/android/server/location/FlpHardwareProvider.java +++ b/services/core/java/com/android/server/location/FlpHardwareProvider.java @@ -67,9 +67,9 @@ public class FlpHardwareProvider { public static FlpHardwareProvider getInstance(Context context) { if (sSingletonInstance == null) { sSingletonInstance = new FlpHardwareProvider(context); + sSingletonInstance.nativeInit(); } - nativeInit(); return sSingletonInstance; } @@ -96,8 +96,7 @@ public class FlpHardwareProvider { Looper.myLooper()); } - public static boolean isSupported() { - nativeInit(); + public boolean isSupported() { return nativeIsSupported(); } @@ -218,9 +217,9 @@ public class FlpHardwareProvider { // Core members private static native void nativeClassInit(); private static native boolean nativeIsSupported(); - private static native void nativeInit(); // FlpLocationInterface members + private native void nativeInit(); private native int nativeGetBatchSize(); private native void nativeStartBatching(int requestId, FusedBatchOptions options); private native void nativeUpdateBatchingOptions(int requestId, FusedBatchOptions optionsObject); diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 015032b2b6e0..41ab626990de 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -16,6 +16,7 @@ package com.android.server.media; +import android.app.ActivityManager; import android.content.Intent; import android.content.pm.PackageManager; import android.media.routeprovider.RouteRequest; @@ -42,6 +43,7 @@ import android.os.Message; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.SystemClock; +import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.util.Pair; @@ -80,7 +82,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private final MessageHandler mHandler; - private final int mPid; + private final int mOwnerPid; + private final int mOwnerUid; + private final int mUserId; private final SessionInfo mSessionInfo; private final String mTag; private final ControllerStub mController; @@ -110,10 +114,12 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private boolean mIsActive = false; - public MediaSessionRecord(int pid, String packageName, ISessionCallback cb, String tag, - MediaSessionService service, Handler handler) { - mPid = pid; - mSessionInfo = new SessionInfo(UUID.randomUUID().toString(), packageName); + public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName, + ISessionCallback cb, String tag, MediaSessionService service, Handler handler) { + mOwnerPid = ownerPid; + mOwnerUid = ownerUid; + mUserId = userId; + mSessionInfo = new SessionInfo(UUID.randomUUID().toString(), ownerPackageName); mTag = tag; mController = new ControllerStub(); mSession = new SessionStub(); @@ -187,6 +193,15 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } /** + * Get the user id this session was created for. + * + * @return The user id for this session. + */ + public int getUserId() { + return mUserId; + } + + /** * Check if this session has system priorty and should receive media buttons * before any other sessions. * @@ -305,7 +320,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { pw.println(prefix + mTag + " " + this); final String indent = prefix + " "; - pw.println(indent + "pid=" + mPid); + pw.println(indent + "ownerPid=" + mOwnerPid + ", ownerUid=" + mOwnerUid + + ", userId=" + mUserId); pw.println(indent + "info=" + mSessionInfo.toString()); pw.println(indent + "published=" + mIsActive); pw.println(indent + "flags=" + mFlags); diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index fb858fcde05a..008f9be39f99 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -63,7 +63,6 @@ public class MediaSessionService extends SystemService implements Monitor { private final ArrayList<MediaRouteProviderProxy> mProviders = new ArrayList<MediaRouteProviderProxy>(); private final Object mLock = new Object(); - // TODO do we want a separate thread for handling mediasession messages? private final Handler mHandler = new Handler(); private MediaSessionRecord mPrioritySession; @@ -72,8 +71,8 @@ public class MediaSessionService extends SystemService implements Monitor { // session so we drop late callbacks properly. private int mShowRoutesRequestId = 0; - // TODO refactor to have per user state. See MediaRouterService for an - // example + // TODO refactor to have per user state for providers. See + // MediaRouterService for an example public MediaSessionService(Context context) { super(context); @@ -211,25 +210,42 @@ public class MediaSessionService extends SystemService implements Monitor { * <ul> * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL * permission</li> - * <li>the caller's listener is one of the enabled notification listeners</li> + * <li>the caller's listener is one of the enabled notification listeners + * for the caller's user</li> * </ul> */ - private void enforceMediaPermissions(ComponentName compName, int pid, int uid) { + private void enforceMediaPermissions(ComponentName compName, int pid, int uid, + int resolvedUserId) { if (getContext() .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid) != PackageManager.PERMISSION_GRANTED - && !isEnabledNotificationListener(compName)) { + && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid), + resolvedUserId)) { throw new SecurityException("Missing permission to control media."); } } - private boolean isEnabledNotificationListener(ComponentName compName) { + /** + * This checks if the component is an enabled notification listener for the + * specified user. Enabled components may only operate on behalf of the user + * they're running as. + * + * @param compName The component that is enabled. + * @param userId The user id of the caller. + * @param forUserId The user id they're making the request on behalf of. + * @return True if the component is enabled, false otherwise + */ + private boolean isEnabledNotificationListener(ComponentName compName, int userId, + int forUserId) { + if (userId != forUserId) { + // You may not access another user's content as an enabled listener. + return false; + } if (compName != null) { - final int currentUser = ActivityManager.getCurrentUser(); final String enabledNotifListeners = Settings.Secure.getStringForUser( getContext().getContentResolver(), Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, - currentUser); + userId); if (enabledNotifListeners != null) { final String[] components = enabledNotifListeners.split(":"); for (int i = 0; i < components.length; i++) { @@ -248,23 +264,23 @@ public class MediaSessionService extends SystemService implements Monitor { } if (DEBUG) { Log.d(TAG, "not ok to get sessions, " + compName + - " is not in list of ENABLED_NOTIFICATION_LISTENERS"); + " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId); } } return false; } - private MediaSessionRecord createSessionInternal(int pid, String packageName, - ISessionCallback cb, String tag, boolean forCalls) { + private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId, + String callerPackageName, ISessionCallback cb, String tag) { synchronized (mLock) { - return createSessionLocked(pid, packageName, cb, tag); + return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag); } } - private MediaSessionRecord createSessionLocked(int pid, String packageName, - ISessionCallback cb, String tag) { - final MediaSessionRecord session = new MediaSessionRecord(pid, packageName, cb, tag, this, - mHandler); + private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId, + String callerPackageName, ISessionCallback cb, String tag) { + final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId, + callerPackageName, cb, tag, this, mHandler); try { cb.asBinder().linkToDeath(session, 0); } catch (RemoteException e) { @@ -273,7 +289,7 @@ public class MediaSessionService extends SystemService implements Monitor { mRecords.add(session); mPriorityStack.addSession(session); if (DEBUG) { - Log.d(TAG, "Created session for package " + packageName + " with tag " + tag); + Log.d(TAG, "Created session for package " + callerPackageName + " with tag " + tag); } return session; } @@ -358,41 +374,50 @@ public class MediaSessionService extends SystemService implements Monitor { // ActivityManagerNative.handleIncomingUser and stash result for use // when starting services on that session's behalf. @Override - public ISession createSession(String packageName, ISessionCallback cb, String tag) - throws RemoteException { + public ISession createSession(String packageName, ISessionCallback cb, String tag, + int userId) throws RemoteException { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { enforcePackageName(packageName, uid); + int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, + false /* allowAll */, true /* requireFull */, "createSession", packageName); if (cb == null) { throw new IllegalArgumentException("Controller callback cannot be null"); } - return createSessionInternal(pid, packageName, cb, tag, false).getSessionBinder(); + return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag) + .getSessionBinder(); } finally { Binder.restoreCallingIdentity(token); } } @Override - public List<IBinder> getSessions(ComponentName componentName) { + public List<IBinder> getSessions(ComponentName componentName, int userId) { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { + String packageName = null; if (componentName != null) { // If they gave us a component name verify they own the // package - enforcePackageName(componentName.getPackageName(), uid); + packageName = componentName.getPackageName(); + enforcePackageName(packageName, uid); } - // Then check if they have the permissions or their component is - // allowed - enforceMediaPermissions(componentName, pid, uid); + // Check that they can make calls on behalf of the user and + // get the final user id + int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId, + true /* allowAll */, true /* requireFull */, "getSessions", packageName); + // Check if they have the permissions or their component is + // enabled for the user they're calling from. + enforceMediaPermissions(componentName, pid, uid, resolvedUserId); ArrayList<IBinder> binders = new ArrayList<IBinder>(); synchronized (mLock) { ArrayList<MediaSessionRecord> records = mPriorityStack - .getActiveSessions(); + .getActiveSessions(resolvedUserId); int size = records.size(); for (int i = 0; i < size; i++) { binders.add(records.get(i).getControllerBinder().asBinder()); @@ -428,7 +453,7 @@ public class MediaSessionService extends SystemService implements Monitor { mRecords.get(i).dump(pw, ""); pw.println(); } - mPriorityStack.dumpLocked(pw, ""); + mPriorityStack.dump(pw, ""); pw.println("Providers:"); count = mProviders.size(); diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java index f9f004d55c4c..1e1818d10df8 100644 --- a/services/core/java/com/android/server/media/MediaSessionStack.java +++ b/services/core/java/com/android/server/media/MediaSessionStack.java @@ -18,6 +18,7 @@ package com.android.server.media; import android.media.session.PlaybackState; import android.media.session.Session; +import android.os.UserHandle; import android.text.TextUtils; import java.io.PrintWriter; @@ -104,11 +105,12 @@ public class MediaSessionStack { * Get the current priority sorted list of active sessions. The most * important session is at index 0 and the least important at size - 1. * + * @param userId The user to check. * @return All the active sessions in priority order. */ - public ArrayList<MediaSessionRecord> getActiveSessions() { + public ArrayList<MediaSessionRecord> getActiveSessions(int userId) { if (mCachedActiveList == null) { - mCachedActiveList = getPriorityListLocked(true, 0); + mCachedActiveList = getPriorityListLocked(true, 0, userId); } return mCachedActiveList; } @@ -118,13 +120,14 @@ public class MediaSessionStack { * transport controls. The most important session is at index 0 and the * least important at size -1. * + * @param userId The user to check. * @return All the active sessions that handle transport controls in * priority order. */ - public ArrayList<MediaSessionRecord> getTransportControlSessions() { + public ArrayList<MediaSessionRecord> getTransportControlSessions(int userId) { if (mCachedTransportControlList == null) { mCachedTransportControlList = getPriorityListLocked(true, - Session.FLAG_HANDLES_TRANSPORT_CONTROLS); + Session.FLAG_HANDLES_TRANSPORT_CONTROLS, userId); } return mCachedTransportControlList; } @@ -132,13 +135,14 @@ public class MediaSessionStack { /** * Get the highest priority active session. * + * @param userId The user to check. * @return The current highest priority session or null. */ - public MediaSessionRecord getDefaultSession() { + public MediaSessionRecord getDefaultSession(int userId) { if (mCachedDefault != null) { return mCachedDefault; } - ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0); + ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId); if (records.size() > 0) { return records.get(0); } @@ -148,22 +152,24 @@ public class MediaSessionStack { /** * Get the highest priority session that can handle media buttons. * + * @param userId The user to check. * @return The default media button session or null. */ - public MediaSessionRecord getDefaultMediaButtonSession() { + public MediaSessionRecord getDefaultMediaButtonSession(int userId) { if (mCachedButtonReceiver != null) { return mCachedButtonReceiver; } ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, - Session.FLAG_HANDLES_MEDIA_BUTTONS); + Session.FLAG_HANDLES_MEDIA_BUTTONS, userId); if (records.size() > 0) { mCachedButtonReceiver = records.get(0); } return mCachedButtonReceiver; } - public void dumpLocked(PrintWriter pw, String prefix) { - ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0); + public void dump(PrintWriter pw, String prefix) { + ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0, + UserHandle.USER_ALL); int count = sortedSessions.size(); pw.println(prefix + "Sessions Stack - have " + count + " sessions:"); String indent = prefix + " "; @@ -182,9 +188,12 @@ public class MediaSessionStack { * all sessions. * @param withFlags Only return sessions with all the specified flags set. 0 * returns all sessions. + * @param userId The user to get sessions for. {@link UserHandle#USER_ALL} + * will return sessions for all users. * @return The priority sorted list of sessions. */ - private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags) { + private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags, + int userId) { ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>(); int lastLocalIndex = 0; int lastActiveIndex = 0; @@ -194,7 +203,12 @@ public class MediaSessionStack { for (int i = 0; i < size; i++) { final MediaSessionRecord session = mSessions.get(i); + if (userId != UserHandle.USER_ALL && userId != session.getUserId()) { + // Filter out sessions for the wrong user + continue; + } if ((session.getFlags() & withFlags) != withFlags) { + // Filter out sessions with the wrong flags continue; } if (!session.isActive()) { diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java new file mode 100644 index 000000000000..c8b1ba06d7b0 --- /dev/null +++ b/services/core/java/com/android/server/notification/NotificationComparator.java @@ -0,0 +1,44 @@ +/* + * 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 java.util.Comparator; + +/** + * Sorts notificaitons into attention-relelvant order. + */ +public class NotificationComparator + implements Comparator<NotificationManagerService.NotificationRecord> { + + @Override + public int compare(NotificationManagerService.NotificationRecord lhs, + NotificationManagerService.NotificationRecord rhs) { + final int leftScore = lhs.sbn.getScore(); + final int rightScore = rhs.sbn.getScore(); + if (leftScore != rightScore) { + // by priority, high to low + return -1 * Integer.compare(leftScore, rightScore); + } + final float leftPeple = lhs.getContactAffinity(); + final float rightPeople = rhs.getContactAffinity(); + if (leftPeple != rightPeople) { + // by contact proximity, close to far + return -1 * Float.compare(leftPeple, rightPeople); + } + // then break ties by time, most recent first + return -1 * Long.compare(lhs.sbn.getPostTime(), rhs.sbn.getPostTime()); + } +} diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 46985874045d..7a4f9519011e 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -49,8 +49,10 @@ import android.net.Uri; import android.os.Binder; import android.os.Environment; import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; import android.os.IInterface; +import android.os.Looper; import android.os.Message; import android.os.Process; import android.os.RemoteException; @@ -61,6 +63,7 @@ import android.service.notification.INotificationListener; import android.service.notification.IConditionListener; import android.service.notification.IConditionProvider; import android.service.notification.NotificationListenerService; +import android.service.notification.NotificationOrderUpdate; import android.service.notification.StatusBarNotification; import android.service.notification.Condition; import android.service.notification.ZenModeConfig; @@ -76,7 +79,6 @@ import android.view.accessibility.AccessibilityManager; import android.widget.Toast; import com.android.internal.R; -import com.android.internal.notification.NotificationScorer; import com.android.internal.util.FastXmlSerializer; import com.android.server.EventLogTags; import com.android.server.SystemService; @@ -104,9 +106,12 @@ import java.lang.reflect.Array; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.NoSuchElementException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; /** {@hide} */ public class NotificationManagerService extends SystemService { @@ -118,6 +123,8 @@ public class NotificationManagerService extends SystemService { // message codes static final int MESSAGE_TIMEOUT = 2; static final int MESSAGE_SAVE_POLICY_FILE = 3; + static final int MESSAGE_RECONSIDER_RANKING = 4; + static final int MESSAGE_SEND_RANKING_UPDATE = 5; static final int LONG_DELAY = 3500; // 3.5 seconds static final int SHORT_DELAY = 2000; // 2 seconds @@ -147,6 +154,9 @@ public class NotificationManagerService extends SystemService { final IBinder mForegroundToken = new Binder(); private WorkerHandler mHandler; + private final HandlerThread mRankingThread = new HandlerThread("ranker", + Process.THREAD_PRIORITY_BACKGROUND); + private Handler mRankingHandler = null; private Light mNotificationLight; Light mAttentionLight; @@ -171,6 +181,7 @@ public class NotificationManagerService extends SystemService { // used as a mutex for access to all active notifications & listeners final ArrayList<NotificationRecord> mNotificationList = new ArrayList<NotificationRecord>(); + final NotificationComparator mRankingComparator = new NotificationComparator(); final ArrayMap<String, NotificationRecord> mNotificationsByKey = new ArrayMap<String, NotificationRecord>(); final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>(); @@ -193,7 +204,7 @@ public class NotificationManagerService extends SystemService { private static final String TAG_PACKAGE = "package"; private static final String ATTR_NAME = "name"; - final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>(); + final ArrayList<NotificationSignalExtractor> mSignalExtractors = new ArrayList<NotificationSignalExtractor>(); private final UserProfiles mUserProfiles = new UserProfiles(); private NotificationListeners mListeners; @@ -446,6 +457,7 @@ public class NotificationManagerService extends SystemService { final StatusBarNotification sbn; SingleNotificationStats stats; IBinder statusBarKey; + private float mContactAffinity; NotificationRecord(StatusBarNotification sbn) { @@ -528,6 +540,14 @@ public class NotificationManagerService extends SystemService { this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(), this.sbn.getNotification()); } + + public void setContactAffinity(float contactAffinity) { + mContactAffinity = contactAffinity; + } + + public float getContactAffinity() { + return mContactAffinity; + } } private static final class ToastRecord @@ -707,7 +727,7 @@ public class NotificationManagerService extends SystemService { boolean queryRemove = false; boolean packageChanged = false; boolean cancelNotifications = true; - + if (action.equals(Intent.ACTION_PACKAGE_ADDED) || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED)) || action.equals(Intent.ACTION_PACKAGE_RESTARTED) @@ -849,6 +869,8 @@ public class NotificationManagerService extends SystemService { mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE); mHandler = new WorkerHandler(); + mRankingThread.start(); + mRankingHandler = new RankingWorkerHandler(mRankingThread.getLooper()); mZenModeHelper = new ZenModeHelper(getContext(), mHandler); mZenModeHelper.addCallback(new ZenModeHelper.Callback() { @Override @@ -925,21 +947,22 @@ public class NotificationManagerService extends SystemService { mSettingsObserver = new SettingsObserver(mHandler); - // spin up NotificationScorers - String[] notificationScorerNames = resources.getStringArray( - R.array.config_notificationScorers); - for (String scorerName : notificationScorerNames) { + // spin up NotificationSignalExtractors + String[] extractorNames = resources.getStringArray( + R.array.config_notificationSignalExtractors); + for (String extractorName : extractorNames) { try { - Class<?> scorerClass = getContext().getClassLoader().loadClass(scorerName); - NotificationScorer scorer = (NotificationScorer) scorerClass.newInstance(); - scorer.initialize(getContext()); - mScorers.add(scorer); + Class<?> extractorClass = getContext().getClassLoader().loadClass(extractorName); + NotificationSignalExtractor extractor = + (NotificationSignalExtractor) extractorClass.newInstance(); + extractor.initialize(getContext()); + mSignalExtractors.add(extractor); } catch (ClassNotFoundException e) { - Slog.w(TAG, "Couldn't find scorer " + scorerName + ".", e); + Slog.w(TAG, "Couldn't find extractor " + extractorName + ".", e); } catch (InstantiationException e) { - Slog.w(TAG, "Couldn't instantiate scorer " + scorerName + ".", e); + Slog.w(TAG, "Couldn't instantiate extractor " + extractorName + ".", e); } catch (IllegalAccessException e) { - Slog.w(TAG, "Problem accessing scorer " + scorerName + ".", e); + Slog.w(TAG, "Problem accessing extractor " + extractorName + ".", e); } } @@ -1150,6 +1173,7 @@ public class NotificationManagerService extends SystemService { * System-only API for getting a list of current (i.e. not cleared) notifications. * * Requires ACCESS_NOTIFICATIONS which is signature|system. + * @returns A list of all the notifications, in natural order. */ @Override public StatusBarNotification[] getActiveNotifications(String callingPkg) { @@ -1306,6 +1330,9 @@ public class NotificationManagerService extends SystemService { * should be used. * * @param token The binder for the listener, to check that the caller is allowed + * @param keys the notification keys to fetch, or null for all active notifications. + * @returns The return value will contain the notifications specified in keys, in that + * order, or if keys is null, all the notifications, in natural order. */ @Override public StatusBarNotification[] getActiveNotificationsFromListener( @@ -1337,7 +1364,7 @@ public class NotificationManagerService extends SystemService { @Override public String[] getActiveNotificationKeysFromListener(INotificationListener token) { - return NotificationManagerService.this.getActiveNotificationKeysFromListener(token); + return NotificationManagerService.this.getActiveNotificationKeys(token); } @Override @@ -1409,19 +1436,21 @@ public class NotificationManagerService extends SystemService { } }; - private String[] getActiveNotificationKeysFromListener(INotificationListener token) { - synchronized (mNotificationList) { - final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); - final ArrayList<String> keys = new ArrayList<String>(); - final int N = mNotificationList.size(); - for (int i=0; i<N; i++) { - final StatusBarNotification sbn = mNotificationList.get(i).sbn; - if (info.enabledAndUserMatches(sbn.getUserId())) { - keys.add(sbn.getKey()); + private String[] getActiveNotificationKeys(INotificationListener token) { + final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); + final ArrayList<String> keys = new ArrayList<String>(); + if (info.isEnabledForCurrentProfiles()) { + synchronized (mNotificationList) { + final int N = mNotificationList.size(); + for (int i = 0; i < N; i++) { + final StatusBarNotification sbn = mNotificationList.get(i).sbn; + if (info.enabledAndUserMatches(sbn.getUserId())) { + keys.add(sbn.getKey()); + } } } - return keys.toArray(new String[keys.size()]); } + return keys.toArray(new String[keys.size()]); } void dumpImpl(PrintWriter pw) { @@ -1578,26 +1607,23 @@ public class NotificationManagerService extends SystemService { // 1. initial score: buckets of 10, around the app int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20] - // 2. Consult external heuristics (TBD) - - // 3. Apply local rules - - int initialScore = score; - if (!mScorers.isEmpty()) { - if (DBG) Slog.v(TAG, "Initial score is " + score + "."); - for (NotificationScorer scorer : mScorers) { + // 2. extract ranking signals from the notification data + final StatusBarNotification n = new StatusBarNotification( + pkg, opPkg, id, tag, callingUid, callingPid, score, notification, + user); + NotificationRecord r = new NotificationRecord(n); + if (!mSignalExtractors.isEmpty()) { + for (NotificationSignalExtractor extractor : mSignalExtractors) { try { - score = scorer.getScore(notification, score); + RankingFuture future = extractor.process(r); + scheduleRankingReconsideration(future); } catch (Throwable t) { - Slog.w(TAG, "Scorer threw on .getScore.", t); + Slog.w(TAG, "NotificationSignalExtractor failed.", t); } } - if (DBG) Slog.v(TAG, "Final score is " + score + "."); } - // add extra to indicate score modified by NotificationScorer - notification.extras.putBoolean(Notification.EXTRA_SCORE_MODIFIED, - score != initialScore); + // 3. Apply local rules // blocked apps if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) { @@ -1608,10 +1634,6 @@ public class NotificationManagerService extends SystemService { } } - if (DBG) { - Slog.v(TAG, "Assigned score=" + score + " to " + notification); - } - if (score < SCORE_DISPLAY_THRESHOLD) { // Notification will be blocked because the score is too low. return; @@ -1626,12 +1648,7 @@ public class NotificationManagerService extends SystemService { if (DBG || intercept) Slog.v(TAG, "pkg=" + pkg + " canInterrupt=" + canInterrupt + " intercept=" + intercept); synchronized (mNotificationList) { - final StatusBarNotification n = new StatusBarNotification( - pkg, opPkg, id, tag, callingUid, callingPid, score, notification, - user); - NotificationRecord r = new NotificationRecord(n); NotificationRecord old = null; - int index = indexOfNotificationLocked(pkg, tag, id, userId); if (index < 0) { mNotificationList.add(r); @@ -1651,6 +1668,8 @@ public class NotificationManagerService extends SystemService { } mNotificationsByKey.put(n.getKey(), r); + Collections.sort(mNotificationList, mRankingComparator); + // Ensure if this is a foreground service that the proper additional // flags are set. if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) { @@ -1948,6 +1967,57 @@ public class NotificationManagerService extends SystemService { } } + private void scheduleRankingReconsideration(RankingFuture future) { + if (future != null) { + Message m = Message.obtain(mRankingHandler, MESSAGE_RECONSIDER_RANKING, future); + long delay = future.getDelay(TimeUnit.MILLISECONDS); + mRankingHandler.sendMessageDelayed(m, delay); + } + } + + private void handleRankingReconsideration(Message message) { + if (!(message.obj instanceof RankingFuture)) return; + + RankingFuture future = (RankingFuture) message.obj; + future.run(); + try { + NotificationRecord record = future.get(); + synchronized (mNotificationList) { + int before = mNotificationList.indexOf(record); + if (before != -1) { + Collections.sort(mNotificationList, mRankingComparator); + int after = mNotificationList.indexOf(record); + + if (before != after) { + scheduleSendRankingUpdate(); + } + } + } + } catch (InterruptedException e) { + // we're running the future explicitly, so this should never happen + } catch (ExecutionException e) { + // we're running the future explicitly, so this should never happen + } + } + + private void scheduleSendRankingUpdate() { + mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE); + Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE); + mHandler.sendMessage(m); + } + + private void handleSendRankingUpdate() { + synchronized (mNotificationList) { + final int N = mNotificationList.size(); + ArrayList<StatusBarNotification> sbns = + new ArrayList<StatusBarNotification>(N); + for (int i = 0; i < N; i++ ) { + sbns.add(mNotificationList.get(i).sbn); + } + mListeners.notifyOrderUpdateLocked(sbns); + } + } + private final class WorkerHandler extends Handler { @Override @@ -1961,10 +2031,29 @@ public class NotificationManagerService extends SystemService { case MESSAGE_SAVE_POLICY_FILE: handleSavePolicyFile(); break; + case MESSAGE_SEND_RANKING_UPDATE: + handleSendRankingUpdate(); + break; } } + } + private final class RankingWorkerHandler extends Handler + { + public RankingWorkerHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MESSAGE_RECONSIDER_RANKING: + handleRankingReconsideration(msg); + break; + } + } + } // Notifications // ============================================================================ @@ -2346,9 +2435,9 @@ public class NotificationManagerService extends SystemService { @Override public void onServiceAdded(ManagedServiceInfo info) { final INotificationListener listener = (INotificationListener) info.service; - final String[] keys = getActiveNotificationKeysFromListener(listener); + final String[] keys = getActiveNotificationKeys(listener); try { - listener.onListenerConnected(keys); + listener.onListenerConnected(new NotificationOrderUpdate(keys)); } catch (RemoteException e) { // we tried } @@ -2361,12 +2450,18 @@ public class NotificationManagerService extends SystemService { // make a copy in case changes are made to the underlying Notification object final StatusBarNotification sbnClone = sbn.clone(); for (final ManagedServiceInfo info : mServices) { - mHandler.post(new Runnable() { - @Override - public void run() { - notifyPostedIfUserMatch(info, sbnClone); + if (info.isEnabledForCurrentProfiles()) { + final INotificationListener listener = (INotificationListener) info.service; + final String[] keys = getActiveNotificationKeys(listener); + if (keys.length > 0) { + mHandler.post(new Runnable() { + @Override + public void run() { + notifyPostedIfUserMatch(info, sbnClone, keys); + } + }); } - }); + } } } @@ -2378,39 +2473,83 @@ public class NotificationManagerService extends SystemService { // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the // notification final StatusBarNotification sbnLight = sbn.cloneLight(); - for (ManagedServiceInfo serviceInfo : mServices) { - final ManagedServiceInfo info = (ManagedServiceInfo) serviceInfo; + for (final ManagedServiceInfo info : mServices) { + if (info.isEnabledForCurrentProfiles()) { + final INotificationListener listener = (INotificationListener) info.service; + final String[] keys = getActiveNotificationKeys(listener); + mHandler.post(new Runnable() { + @Override + public void run() { + notifyRemovedIfUserMatch(info, sbnLight, keys); + } + }); + } + } + } + + /** + * asynchronously notify all listeners about a reordering of notifications + * @param sbns an array of {@link StatusBarNotification}s to consider. This code + * must not rely on mutable members of these objects, such as the + * {@link Notification}. + */ + public void notifyOrderUpdateLocked(final ArrayList<StatusBarNotification> sbns) { + for (final ManagedServiceInfo serviceInfo : mServices) { mHandler.post(new Runnable() { @Override public void run() { - notifyRemovedIfUserMatch(info, sbnLight); + notifyOrderUpdateIfUserMatch(serviceInfo, sbns); } }); } } - private void notifyPostedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) { + private void notifyPostedIfUserMatch(final ManagedServiceInfo info, + final StatusBarNotification sbn, String[] keys) { if (!info.enabledAndUserMatches(sbn.getUserId())) { return; } final INotificationListener listener = (INotificationListener)info.service; try { - listener.onNotificationPosted(sbn); + listener.onNotificationPosted(sbn, new NotificationOrderUpdate(keys)); } catch (RemoteException ex) { Log.e(TAG, "unable to notify listener (posted): " + listener, ex); } } - private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) { + private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn, + String[] keys) { if (!info.enabledAndUserMatches(sbn.getUserId())) { return; } final INotificationListener listener = (INotificationListener)info.service; try { - listener.onNotificationRemoved(sbn); + listener.onNotificationRemoved(sbn, new NotificationOrderUpdate(keys)); } catch (RemoteException ex) { Log.e(TAG, "unable to notify listener (removed): " + listener, ex); } } + + /** + * @param sbns an array of {@link StatusBarNotification}s to consider. This code + * must not rely on mutable members of these objects, such as the + * {@link Notification}. + */ + public void notifyOrderUpdateIfUserMatch(ManagedServiceInfo info, + ArrayList<StatusBarNotification> sbns) { + ArrayList<String> keys = new ArrayList<String>(sbns.size()); + for (StatusBarNotification sbn: sbns) { + if (info.enabledAndUserMatches(sbn.getUserId())) { + keys.add(sbn.getKey()); + } + } + final INotificationListener listener = (INotificationListener)info.service; + try { + listener.onNotificationOrderUpdate( + new NotificationOrderUpdate(keys.toArray(new String[keys.size()]))); + } catch (RemoteException ex) { + Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex); + } + } } } diff --git a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java new file mode 100644 index 000000000000..a41fdfee1997 --- /dev/null +++ b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java @@ -0,0 +1,41 @@ +/* +* 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.content.Context; + +/** + * Extracts signals that will be useful to the {@link NotificationComparator} and caches them + * on the {@link NotificationManagerService.NotificationRecord} object. These annotations will + * not be passed on to {@link android.service.notification.NotificationListenerService}s. + */ +public interface NotificationSignalExtractor { + + /** One-time initialization. */ + public void initialize(Context context); + + /** + * Called once per notification that is posted or updated. + * + * @return null if the work is done, or a future if there is more to do. The + * {@link RankingFuture} will be run on a worker thread, and if notifications are re-ordered + * by that execution, the {@link NotificationManagerService} may send order update + * events to the {@link android.service.notification.NotificationListenerService}s. + */ + public RankingFuture process(NotificationManagerService.NotificationRecord notification); + +} diff --git a/services/core/java/com/android/server/notification/RankingFuture.java b/services/core/java/com/android/server/notification/RankingFuture.java new file mode 100644 index 000000000000..33aad8d88227 --- /dev/null +++ b/services/core/java/com/android/server/notification/RankingFuture.java @@ -0,0 +1,118 @@ +/* + * 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 java.util.concurrent.Delayed; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public abstract class RankingFuture + implements ScheduledFuture<NotificationManagerService.NotificationRecord> { + private static final long IMMEDIATE = 0l; + + private static final int START = 0; + private static final int RUNNING = 1; + private static final int DONE = 2; + private static final int CANCELLED = 3; + + private int mState; + private long mDelay; + protected NotificationManagerService.NotificationRecord mRecord; + + public RankingFuture(NotificationManagerService.NotificationRecord record) { + this(record, IMMEDIATE); + } + + public RankingFuture(NotificationManagerService.NotificationRecord record, long delay) { + mDelay = delay; + mRecord = record; + mState = START; + } + + public void run() { + if (mState == START) { + mState = RUNNING; + + work(); + + mState = DONE; + synchronized (this) { + notifyAll(); + } + } + } + + @Override + public long getDelay(TimeUnit unit) { + return unit.convert(mDelay, TimeUnit.MILLISECONDS); + } + + @Override + public int compareTo(Delayed another) { + return Long.compare(getDelay(TimeUnit.MICROSECONDS), + another.getDelay(TimeUnit.MICROSECONDS)); + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + if (mState == START) { // can't cancel if running or done + mState = CANCELLED; + return true; + } + return false; + } + + @Override + public boolean isCancelled() { + return mState == CANCELLED; + } + + @Override + public boolean isDone() { + return mState == DONE; + } + + @Override + public NotificationManagerService.NotificationRecord get() + throws InterruptedException, ExecutionException { + while (!isDone()) { + synchronized (this) { + this.wait(); + } + } + return mRecord; + } + + @Override + public NotificationManagerService.NotificationRecord get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + long timeoutMillis = unit.convert(timeout, TimeUnit.MILLISECONDS); + long start = System.currentTimeMillis(); + long now = System.currentTimeMillis(); + while (!isDone() && (now - start) < timeoutMillis) { + try { + wait(timeoutMillis - (now - start)); + } catch (InterruptedException e) { + now = System.currentTimeMillis(); + } + } + return mRecord; + } + + public abstract void work(); +} diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java new file mode 100644 index 000000000000..8cd2f9b24282 --- /dev/null +++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java @@ -0,0 +1,298 @@ +/* +* 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.Notification; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.provider.ContactsContract; +import android.provider.ContactsContract.Contacts; +import android.provider.Settings; +import android.text.TextUtils; +import android.util.LruCache; +import android.util.Slog; + +import com.android.server.notification.NotificationManagerService.NotificationRecord; + +import java.util.ArrayList; +import java.util.LinkedList; + +/** + * This {@link NotificationSignalExtractor} attempts to validate + * people references. Also elevates the priority of real people. + */ +public class ValidateNotificationPeople implements NotificationSignalExtractor { + private static final String TAG = "ValidateNotificationPeople"; + private static final boolean INFO = true; + private static final boolean DEBUG = false; + + private static final boolean ENABLE_PEOPLE_VALIDATOR = true; + private static final String SETTING_ENABLE_PEOPLE_VALIDATOR = + "validate_notification_people_enabled"; + private static final String[] LOOKUP_PROJECTION = { Contacts._ID }; + private static final int MAX_PEOPLE = 10; + private static final int PEOPLE_CACHE_SIZE = 200; + + private static final float NONE = 0f; + private static final float VALID_CONTACT = 0.5f; + // TODO private static final float STARRED_CONTACT = 1f; + + protected boolean mEnabled; + private Context mContext; + + // maps raw person handle to resolved person object + private LruCache<String, LookupResult> mPeopleCache; + + private RankingFuture validatePeople(NotificationRecord record) { + float affinity = NONE; + Bundle extras = record.getNotification().extras; + if (extras == null) { + return null; + } + + final String[] people = getExtraPeople(extras); + if (people == null || people.length == 0) { + return null; + } + + if (INFO) Slog.i(TAG, "Validating: " + record.sbn.getKey()); + final LinkedList<String> pendingLookups = new LinkedList<String>(); + for (int personIdx = 0; personIdx < people.length && personIdx < MAX_PEOPLE; personIdx++) { + final String handle = people[personIdx]; + if (TextUtils.isEmpty(handle)) continue; + + synchronized (mPeopleCache) { + LookupResult lookupResult = mPeopleCache.get(handle); + if (lookupResult == null || lookupResult.isExpired()) { + pendingLookups.add(handle); + } else { + if (DEBUG) Slog.d(TAG, "using cached lookupResult: " + lookupResult.mId); + } + if (lookupResult != null) { + affinity = Math.max(affinity, lookupResult.getAffinity()); + } + } + } + + // record the best available data, so far: + record.setContactAffinity(affinity); + + if (pendingLookups.isEmpty()) { + if (INFO) Slog.i(TAG, "final affinity: " + affinity); + return null; + } + + if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + record.sbn.getKey()); + return new RankingFuture(record) { + @Override + public void work() { + if (INFO) Slog.i(TAG, "Executing: validation for: " + mRecord.sbn.getKey()); + float affinity = NONE; + LookupResult lookupResult = null; + for (final String handle: pendingLookups) { + final Uri uri = Uri.parse(handle); + if ("tel".equals(uri.getScheme())) { + if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle); + lookupResult = resolvePhoneContact(handle, uri.getSchemeSpecificPart()); + } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) { + if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle); + lookupResult = resolveContactsUri(handle, uri); + } else { + Slog.w(TAG, "unsupported URI " + handle); + } + } + if (lookupResult != null) { + affinity = Math.max(affinity, lookupResult.getAffinity()); + } + + float affinityBound = mRecord.getContactAffinity(); + affinity = Math.max(affinity, affinityBound); + mRecord.setContactAffinity(affinity); + if (INFO) Slog.i(TAG, "final affinity: " + affinity); + } + }; + } + + private String[] getExtraPeople(Bundle extras) { + String[] people = extras.getStringArray(Notification.EXTRA_PEOPLE); + if (people != null) { + return people; + } + + ArrayList<String> stringArray = extras.getStringArrayList(Notification.EXTRA_PEOPLE); + if (stringArray != null) { + return (String[]) stringArray.toArray(); + } + + String string = extras.getString(Notification.EXTRA_PEOPLE); + if (string != null) { + people = new String[1]; + people[0] = string; + return people; + } + char[] charArray = extras.getCharArray(Notification.EXTRA_PEOPLE); + if (charArray != null) { + people = new String[1]; + people[0] = new String(charArray); + return people; + } + + CharSequence charSeq = extras.getCharSequence(Notification.EXTRA_PEOPLE); + if (charSeq != null) { + people = new String[1]; + people[0] = charSeq.toString(); + return people; + } + + CharSequence[] charSeqArray = extras.getCharSequenceArray(Notification.EXTRA_PEOPLE); + if (charSeqArray != null) { + final int N = charSeqArray.length; + people = new String[N]; + for (int i = 0; i < N; i++) { + people[i] = charSeqArray[i].toString(); + } + return people; + } + + ArrayList<CharSequence> charSeqList = + extras.getCharSequenceArrayList(Notification.EXTRA_PEOPLE); + if (charSeqList != null) { + final int N = charSeqList.size(); + people = new String[N]; + for (int i = 0; i < N; i++) { + people[i] = charSeqList.get(i).toString(); + } + return people; + } + return null; + } + + private LookupResult resolvePhoneContact(final String handle, final String number) { + LookupResult lookupResult = null; + Cursor c = null; + try { + Uri numberUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, + Uri.encode(number)); + c = mContext.getContentResolver().query(numberUri, LOOKUP_PROJECTION, null, null, null); + if (c != null && c.getCount() > 0) { + c.moveToFirst(); + final int idIdx = c.getColumnIndex(Contacts._ID); + final int id = c.getInt(idIdx); + if (DEBUG) Slog.d(TAG, "is valid: " + id); + lookupResult = new LookupResult(id); + } + } catch(Throwable t) { + Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); + } finally { + if (c != null) { + c.close(); + } + } + if (lookupResult == null) { + lookupResult = new LookupResult(LookupResult.INVALID_ID); + } + synchronized (mPeopleCache) { + mPeopleCache.put(handle, lookupResult); + } + return lookupResult; + } + + private LookupResult resolveContactsUri(String handle, final Uri personUri) { + LookupResult lookupResult = null; + Cursor c = null; + try { + c = mContext.getContentResolver().query(personUri, LOOKUP_PROJECTION, null, null, null); + if (c != null && c.getCount() > 0) { + c.moveToFirst(); + final int idIdx = c.getColumnIndex(Contacts._ID); + final int id = c.getInt(idIdx); + if (DEBUG) Slog.d(TAG, "is valid: " + id); + lookupResult = new LookupResult(id); + } + } catch(Throwable t) { + Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t); + } finally { + if (c != null) { + c.close(); + } + } + if (lookupResult == null) { + lookupResult = new LookupResult(LookupResult.INVALID_ID); + } + synchronized (mPeopleCache) { + mPeopleCache.put(handle, lookupResult); + } + return lookupResult; + } + + public void initialize(Context context) { + if (DEBUG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + "."); + mContext = context; + mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE); + mEnabled = ENABLE_PEOPLE_VALIDATOR && 1 == Settings.Global.getInt( + mContext.getContentResolver(), SETTING_ENABLE_PEOPLE_VALIDATOR, 1); + } + + public RankingFuture process(NotificationManagerService.NotificationRecord record) { + if (!mEnabled) { + if (INFO) Slog.i(TAG, "disabled"); + return null; + } + if (record == null || record.getNotification() == null) { + if (INFO) Slog.i(TAG, "skipping empty notification"); + return null; + } + return validatePeople(record); + } + + private static class LookupResult { + private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr + public static final int INVALID_ID = -1; + + private final long mExpireMillis; + private int mId; + + public LookupResult(int id) { + mId = id; + mExpireMillis = System.currentTimeMillis() + CONTACT_REFRESH_MILLIS; + } + + public boolean isExpired() { + return mExpireMillis < System.currentTimeMillis(); + } + + public boolean isInvalid() { + return mId == INVALID_ID || isExpired(); + } + + public float getAffinity() { + if (isInvalid()) { + return NONE; + } else { + return VALID_CONTACT; // TODO: finer grained result: stars + } + } + + public LookupResult setId(int id) { + mId = id; + return this; + } + } +} + diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index a7f4b287650e..d1333b2d78fb 100755 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -621,7 +621,7 @@ public class PackageManagerService extends IPackageManager.Stub { void write(boolean force) { if (force) { - write(); + writeInternal(); return; } if (SystemClock.elapsedRealtime() - mLastWritten.get() < WRITE_INTERVAL @@ -633,7 +633,7 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public void run() { try { - write(true); + writeInternal(); } finally { mBackgroundWriteRunning.set(false); } @@ -642,7 +642,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private void write() { + private void writeInternal() { synchronized (mPackages) { synchronized (mFileLock) { AtomicFile file = getFile(); @@ -4554,7 +4554,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (updateUsage) { p.mLastPackageUsageTimeInMills = System.currentTimeMillis(); } - mPackageUsage.write(); + mPackageUsage.write(false); if (!p.mDexOptNeeded) { return false; } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 47a8b2e58726..a5eccb363d1e 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -168,9 +168,9 @@ public final class PowerManagerService extends com.android.server.SystemService // Poll interval in milliseconds for watching boot animation finished. private static final int BOOT_ANIMATION_POLL_INTERVAL = 200; - //powerHint + // Used to send the hint to the PowerHAL indicating transitions + // from and to the low power mode. private static final int POWER_HINT_LOW_POWER_MODE = 5; - private static boolean mLowPowerModeEnabled; private final Context mContext; private LightsManager mLightsManager; @@ -399,6 +399,9 @@ public final class PowerManagerService extends com.android.server.SystemService // Time when we last logged a warning about calling userActivity() without permission. private long mLastWarningAboutUserActivityPermission = Long.MIN_VALUE; + // If true, the device is in low power mode. + private static boolean mLowPowerModeEnabled; + private native void nativeInit(); private static native void nativeAcquireSuspendBlocker(String name); @@ -617,12 +620,11 @@ public final class PowerManagerService extends com.android.server.SystemService Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT); - boolean mIsEnabled = Settings.Global.getInt(resolver, - Settings.Global.LOW_POWER_MODE, 0) != 0; - if (mIsEnabled != mLowPowerModeEnabled) { - BinderService bs = new BinderService(); - bs.powerHint(POWER_HINT_LOW_POWER_MODE, mIsEnabled ? 1 : 0); - mLowPowerModeEnabled = mIsEnabled; + boolean lowPowerModeEnabled = Settings.Global.getInt(resolver, + Settings.Global.LOW_POWER_MODE, 0) != 0; + if (lowPowerModeEnabled != mLowPowerModeEnabled) { + powerHintInternal(POWER_HINT_LOW_POWER_MODE, lowPowerModeEnabled ? 1 : 0); + mLowPowerModeEnabled = lowPowerModeEnabled; } mDirty |= DIRTY_SETTINGS; @@ -2020,6 +2022,10 @@ public final class PowerManagerService extends com.android.server.SystemService } } + private void powerHintInternal(int hintId, int data) { + nativeSendPowerHint(hintId, data); + } + /** * Low-level function turn the device off immediately, without trying * to be clean. Most people should use {@link ShutdownThread} for a clean shutdown. @@ -2529,7 +2535,7 @@ public final class PowerManagerService extends com.android.server.SystemService @Override // Binder call public void powerHint(int hintId, int data) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null); - nativeSendPowerHint(hintId, data); + powerHintInternal(hintId, data); } @Override // Binder call diff --git a/services/core/java/com/android/server/task/StateChangedListener.java b/services/core/java/com/android/server/task/StateChangedListener.java new file mode 100644 index 000000000000..a87bf95636a9 --- /dev/null +++ b/services/core/java/com/android/server/task/StateChangedListener.java @@ -0,0 +1,40 @@ +/* + * 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.task; + +import com.android.server.task.controllers.TaskStatus; + +/** + * Interface through which a {@link StateController} informs the + * {@link com.android.server.task.TaskManagerService} that there are some tasks potentially ready + * to be run. + */ +public interface StateChangedListener { + /** + * Called by the controller to notify the TaskManager that it should check on the state of a + * task. + * @param taskStatus The state of the task which has changed. + */ + public void onTaskStateChanged(TaskStatus taskStatus); + + /** + * Called by the controller to notify the TaskManager that regardless of the state of the task, + * it must be run immediately. + * @param taskStatus The state of the task which is to be run immediately. + */ + public void onTaskDeadlineExpired(TaskStatus taskStatus); +} diff --git a/services/core/java/com/android/server/task/TaskList.java b/services/core/java/com/android/server/task/TaskList.java new file mode 100644 index 000000000000..d2b8440524ee --- /dev/null +++ b/services/core/java/com/android/server/task/TaskList.java @@ -0,0 +1,78 @@ +/* + * 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.task; + +import android.content.ComponentName; +import android.content.Task; + +import com.android.server.task.controllers.TaskStatus; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Maintain a list of classes, and accessor methods/logic for these tasks. + * This class offers the following functionality: + * - When a task is added, it will determine if the task requirements have changed (update) and + * whether the controllers need to be updated. + * - Persists Tasks, figures out when to to rewrite the Task to disk. + * - Is threadsafe. + * - Handles rescheduling of tasks. + * - When a periodic task is executed and must be re-added. + * - When a task fails and the client requests that it be retried with backoff. + */ +public class TaskList { + + final List<TaskStatus> mTasks; + + TaskList() { + mTasks = intialiseTaskMapFromDisk(); + } + + /** + * Add a task to the master list, persisting it if necessary. + * @param task Task to add. + * @param persistable true if the TaskQueue should persist this task to the disk. + * @return true if this operation was successful. If false, this task was neither added nor + * persisted. + */ + // TODO: implement this when i decide whether i want to key by TaskStatus + public boolean add(Task task, boolean persistable) { + return true; + } + + /** + * Remove the provided task. Will also delete the task if it was persisted. Note that this + * function does not return the validity of the operation, as we assume a delete will always + * succeed. + * @param task Task to remove. + */ + public void remove(Task task) { + + } + + /** + * + * @return + */ + // TODO: Implement this. + private List<TaskStatus> intialiseTaskMapFromDisk() { + return new ArrayList<TaskStatus>(); + } +} diff --git a/services/core/java/com/android/server/task/TaskManagerService.java b/services/core/java/com/android/server/task/TaskManagerService.java new file mode 100644 index 000000000000..5df4b2a25948 --- /dev/null +++ b/services/core/java/com/android/server/task/TaskManagerService.java @@ -0,0 +1,128 @@ +/* + * 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.task; + +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.SparseArray; + +import com.android.server.task.controllers.TaskStatus; + +import java.util.ArrayList; +import java.util.List; + +/** + * Responsible for taking tasks representing work to be performed by a client app, and determining + * based on the criteria specified when that task should be run against the client application's + * endpoint. + * @hide + */ +public class TaskManagerService extends com.android.server.SystemService + implements StateChangedListener { + + /** Master list of tasks. */ + private final TaskList mTaskList; + + /** + * Track Services that have currently active or pending tasks. The index is provided by + * {@link TaskStatus#getServiceToken()} + */ + private final SparseArray<TaskServiceContext> mPendingTaskServices = + new SparseArray<TaskServiceContext>(); + + private final TaskHandler mHandler; + + private class TaskHandler extends Handler { + /** Check the pending queue and start any tasks. */ + static final int MSG_RUN_PENDING = 0; + /** Initiate the stop task flow. */ + static final int MSG_STOP_TASK = 1; + + public TaskHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message message) { + switch (message.what) { + case MSG_RUN_PENDING: + + break; + case MSG_STOP_TASK: + + break; + } + } + + /** + * Helper to post a message to this handler that will run through the pending queue and + * start any tasks it can. + */ + void sendRunPendingTasksMessage() { + Message m = Message.obtain(this, MSG_RUN_PENDING); + m.sendToTarget(); + } + + void sendOnStopMessage(TaskStatus taskStatus) { + + } + } + + /** + * Initializes the system service. + * <p> + * Subclasses must define a single argument constructor that accepts the context + * and passes it to super. + * </p> + * + * @param context The system server context. + */ + public TaskManagerService(Context context) { + super(context); + mTaskList = new TaskList(); + mHandler = new TaskHandler(context.getMainLooper()); + } + + @Override + public void onStart() { + + } + + /** + * Offboard work to our handler thread as quickly as possible, b/c this call is probably being + * made on the main thread. + * @param taskStatus The state of the task which has changed. + */ + @Override + public void onTaskStateChanged(TaskStatus taskStatus) { + if (taskStatus.isReady()) { + + } else { + if (mPendingTaskServices.get(taskStatus.getServiceToken()) != null) { + // The task is either pending or being executed, which we have to cancel. + } + } + + } + + @Override + public void onTaskDeadlineExpired(TaskStatus taskStatus) { + + } +} diff --git a/services/core/java/com/android/server/task/TaskServiceContext.java b/services/core/java/com/android/server/task/TaskServiceContext.java new file mode 100644 index 000000000000..65c6fa59c8bd --- /dev/null +++ b/services/core/java/com/android/server/task/TaskServiceContext.java @@ -0,0 +1,94 @@ +/* + * 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.task; + +import android.app.task.ITaskCallback; +import android.app.task.ITaskService; +import android.content.ComponentName; +import android.content.ServiceConnection; +import android.content.Task; +import android.os.IBinder; + +import com.android.server.task.controllers.TaskStatus; + +/** + * Maintains information required to bind to a {@link android.app.task.TaskService}. This binding + * can then be reused to start concurrent tasks on the TaskService. Information here is unique + * within this service. + * Functionality provided by this class: + * - Managages wakelock for the service. + * - Sends onStartTask() and onStopTask() messages to client app, and handles callbacks. + * - + */ +public class TaskServiceContext extends ITaskCallback.Stub implements ServiceConnection { + + final ComponentName component; + int uid; + ITaskService service; + + /** Whether this service is actively bound. */ + boolean mBound; + + TaskServiceContext(Task task) { + this.component = task.getService(); + } + + public void stopTask() { + + } + + public void startTask(Task task) { + + } + + @Override + public void taskFinished(int taskId, boolean reschedule) { + + } + + @Override + public void acknowledgeStopMessage(int taskId) { + + } + + @Override + public void acknowledgeStartMessage(int taskId) { + + } + + /** + * @return true if this task is pending or active within this context. + */ + public boolean hasTaskPending(TaskStatus taskStatus) { + return true; + } + + public boolean isBound() { + return mBound; + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + + mBound = true; + } + + @Override + public void onServiceDisconnected(ComponentName name) { + mBound = false; + } +} diff --git a/services/core/java/com/android/server/task/controllers/ConnectivityController.java b/services/core/java/com/android/server/task/controllers/ConnectivityController.java new file mode 100644 index 000000000000..5cca77c3272a --- /dev/null +++ b/services/core/java/com/android/server/task/controllers/ConnectivityController.java @@ -0,0 +1,119 @@ +/* + * 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.task.controllers; + + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.UserHandle; +import android.util.Log; + +import com.android.server.task.TaskManagerService; + +import java.util.LinkedList; +import java.util.List; + +/** + * + */ +public class ConnectivityController extends StateController { + private static final String TAG = "TaskManager.Connectivity"; + + private final List<TaskStatus> mTrackedTasks = new LinkedList<TaskStatus>(); + private final BroadcastReceiver mConnectivityChangedReceiver = + new ConnectivityChangedReceiver(); + + public ConnectivityController(TaskManagerService service) { + super(service); + // Register connectivity changed BR. + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + mContext.registerReceiverAsUser( + mConnectivityChangedReceiver, UserHandle.ALL, intentFilter, null, null); + } + + @Override + public void maybeTrackTaskState(TaskStatus taskStatus) { + if (taskStatus.hasConnectivityConstraint() || taskStatus.hasMeteredConstraint()) { + mTrackedTasks.add(taskStatus); + } + } + + @Override + public void removeTaskStateIfTracked(TaskStatus taskStatus) { + mTrackedTasks.remove(taskStatus); + } + + /** + * @param isConnected Whether the active network is connected for the given uid + * @param isMetered Whether the active network is metered for the given uid. This is + * necessarily false if <code>isConnected</code> is false. + * @param userId Id of the user for whom we are updating the connectivity state. + */ + private void updateTrackedTasks(boolean isConnected, boolean isMetered, int userId) { + for (TaskStatus ts : mTrackedTasks) { + if (ts.userId != userId) { + continue; + } + boolean prevIsConnected = ts.connectivityConstraintSatisfied.getAndSet(isConnected); + boolean prevIsMetered = ts.meteredConstraintSatisfied.getAndSet(isMetered); + if (prevIsConnected != isConnected || prevIsMetered != isMetered) { + mStateChangedListener.onTaskStateChanged(ts); + } + } + } + + class ConnectivityChangedReceiver extends BroadcastReceiver { + /** + * We'll receive connectivity changes for each user here, which we'll process independently. + * We are only interested in the active network here. We're only interested in the active + * network, b/c the end result of this will be for apps to try to hit the network. + * @param context The Context in which the receiver is running. + * @param intent The Intent being received. + */ + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { + final int networkType = + intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, + ConnectivityManager.TYPE_NONE); + // Connectivity manager for THIS context - important! + final ConnectivityManager connManager = (ConnectivityManager) + context.getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo activeNetwork = connManager.getActiveNetworkInfo(); + // This broadcast gets sent a lot, only update if the active network has changed. + if (activeNetwork.getType() == networkType) { + final int userid = context.getUserId(); + boolean isMetered = false; + boolean isConnected = + !intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); + if (isConnected) { // No point making the call if we know there's no conn. + isMetered = connManager.isActiveNetworkMetered(); + } + updateTrackedTasks(isConnected, isMetered, userid); + } + } else { + Log.w(TAG, "Unrecognised action in intent: " + action); + } + } + }; +} diff --git a/services/core/java/com/android/server/task/controllers/StateController.java b/services/core/java/com/android/server/task/controllers/StateController.java new file mode 100644 index 000000000000..e1cd6621f288 --- /dev/null +++ b/services/core/java/com/android/server/task/controllers/StateController.java @@ -0,0 +1,51 @@ +/* + * 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.task.controllers; + +import android.content.Context; + +import com.android.server.task.StateChangedListener; +import com.android.server.task.TaskManagerService; + +/** + * Incorporates shared controller logic between the various controllers of the TaskManager. + * These are solely responsible for tracking a list of tasks, and notifying the TM when these + * are ready to run, or whether they must be stopped. + */ +public abstract class StateController { + + protected Context mContext; + protected StateChangedListener mStateChangedListener; + + public StateController(TaskManagerService service) { + mStateChangedListener = service; + mContext = service.getContext(); + } + + /** + * Implement the logic here to decide whether a task should be tracked by this controller. + * This logic is put here so the TaskManger can be completely agnostic of Controller logic. + * Also called when updating a task, so implementing controllers have to be aware of + * preexisting tasks. + */ + public abstract void maybeTrackTaskState(TaskStatus taskStatus); + /** + * Remove task - this will happen if the task is cancelled, completed, etc. + */ + public abstract void removeTaskStateIfTracked(TaskStatus taskStatus); + +} diff --git a/services/core/java/com/android/server/task/controllers/TaskStatus.java b/services/core/java/com/android/server/task/controllers/TaskStatus.java new file mode 100644 index 000000000000..230b04936946 --- /dev/null +++ b/services/core/java/com/android/server/task/controllers/TaskStatus.java @@ -0,0 +1,154 @@ +/* + * 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.task.controllers; + +import android.content.ComponentName; +import android.content.Task; +import android.os.SystemClock; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Uniquely identifies a task internally. + * Created from the public {@link android.content.Task} object when it lands on the scheduler. + * Contains current state of the requirements of the task, as well as a function to evaluate + * whether it's ready to run. + * This object is shared among the various controllers - hence why the different fields are atomic. + * This isn't strictly necessary because each controller is only interested in a specific field, + * and the receivers that are listening for global state change will all run on the main looper, + * but we don't enforce that so this is safer. + * @hide + */ +public class TaskStatus { + final int taskId; + final int userId; + ComponentName component; + + final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean(); + final AtomicBoolean timeConstraintSatisfied = new AtomicBoolean(); + final AtomicBoolean idleConstraintSatisfied = new AtomicBoolean(); + final AtomicBoolean meteredConstraintSatisfied = new AtomicBoolean(); + final AtomicBoolean connectivityConstraintSatisfied = new AtomicBoolean(); + + private final boolean hasChargingConstraint; + private final boolean hasTimingConstraint; + private final boolean hasIdleConstraint; + private final boolean hasMeteredConstraint; + private final boolean hasConnectivityConstraint; + + private long earliestRunTimeElapsedMillis; + private long latestRunTimeElapsedMillis; + + /** Provide a unique handle to the service that this task will be run on. */ + public int getServiceToken() { + return component.hashCode() + userId; + } + + /** Generate a TaskStatus object for a given task and uid. */ + // TODO: reimplement this to reuse these objects instead of creating a new one each time? + static TaskStatus getForTaskAndUid(Task task, int uId) { + return new TaskStatus(task, uId); + } + + /** Set up the state of a newly scheduled task. */ + TaskStatus(Task task, int userId) { + this.taskId = task.getTaskId(); + this.userId = userId; + this.component = task.getService(); + + hasChargingConstraint = task.isRequireCharging(); + hasIdleConstraint = task.isRequireDeviceIdle(); + + // Timing constraints + if (task.isPeriodic()) { + long elapsedNow = SystemClock.elapsedRealtime(); + earliestRunTimeElapsedMillis = elapsedNow; + latestRunTimeElapsedMillis = elapsedNow + task.getIntervalMillis(); + hasTimingConstraint = true; + } else if (task.getMinLatencyMillis() != 0L || task.getMaxExecutionDelayMillis() != 0L) { + earliestRunTimeElapsedMillis = task.getMinLatencyMillis() > 0L ? + task.getMinLatencyMillis() : Long.MAX_VALUE; + latestRunTimeElapsedMillis = task.getMaxExecutionDelayMillis() > 0L ? + task.getMaxExecutionDelayMillis() : Long.MAX_VALUE; + hasTimingConstraint = true; + } else { + hasTimingConstraint = false; + } + + // Networking constraints + hasMeteredConstraint = task.getNetworkCapabilities() == Task.NetworkType.UNMETERED; + hasConnectivityConstraint = task.getNetworkCapabilities() == Task.NetworkType.ANY; + } + + boolean hasConnectivityConstraint() { + return hasConnectivityConstraint; + } + + boolean hasMeteredConstraint() { + return hasMeteredConstraint; + } + + boolean hasChargingConstraint() { + return hasChargingConstraint; + } + + boolean hasTimingConstraint() { + return hasTimingConstraint; + } + + boolean hasIdleConstraint() { + return hasIdleConstraint; + } + + long getEarliestRunTime() { + return earliestRunTimeElapsedMillis; + } + + long getLatestRunTime() { + return latestRunTimeElapsedMillis; + } + + /** + * @return whether this task is ready to run, based on its requirements. + */ + public synchronized boolean isReady() { + return (!hasChargingConstraint || chargingConstraintSatisfied.get()) + && (!hasTimingConstraint || timeConstraintSatisfied.get()) + && (!hasConnectivityConstraint || connectivityConstraintSatisfied.get()) + && (!hasMeteredConstraint || meteredConstraintSatisfied.get()) + && (!hasIdleConstraint || idleConstraintSatisfied.get()); + } + + @Override + public int hashCode() { + int result = component.hashCode(); + result = 31 * result + taskId; + result = 31 * result + userId; + return result; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof TaskStatus)) return false; + + TaskStatus that = (TaskStatus) o; + return ((taskId == that.taskId) + && (userId == that.userId) + && (component.equals(that.component))); + } +} diff --git a/services/core/java/com/android/server/task/controllers/TimeController.java b/services/core/java/com/android/server/task/controllers/TimeController.java new file mode 100644 index 000000000000..6d97a5374045 --- /dev/null +++ b/services/core/java/com/android/server/task/controllers/TimeController.java @@ -0,0 +1,230 @@ +/* + * 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.task.controllers; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.SystemClock; +import android.util.Log; + +import com.android.server.task.TaskManagerService; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; + +/** + * This class sets an alarm for the next expiring task, and determines whether a task's minimum + * delay has been satisfied. + */ +public class TimeController extends StateController { + private static final String TAG = "TaskManager.Time"; + private static final String ACTION_TASK_EXPIRED = + "android.content.taskmanager.TASK_EXPIRED"; + private static final String ACTION_TASK_DELAY_EXPIRED = + "android.content.taskmanager.TASK_DELAY_EXPIRED"; + + /** Set an alarm for the next task expiry. */ + private final PendingIntent mTaskExpiredAlarmIntent; + /** Set an alarm for the next task delay expiry. This*/ + private final PendingIntent mNextDelayExpiredAlarmIntent; + + private long mNextTaskExpiredElapsedMillis; + private long mNextDelayExpiredElapsedMillis; + + private AlarmManager mAlarmService = null; + /** List of tracked tasks, sorted asc. by deadline */ + private final List<TaskStatus> mTrackedTasks = new LinkedList<TaskStatus>(); + + public TimeController(TaskManagerService service) { + super(service); + mTaskExpiredAlarmIntent = + PendingIntent.getBroadcast(mContext, 0 /* ignored */, + new Intent(ACTION_TASK_EXPIRED), 0); + mNextDelayExpiredAlarmIntent = + PendingIntent.getBroadcast(mContext, 0 /* ignored */, + new Intent(ACTION_TASK_DELAY_EXPIRED), 0); + + // Register BR for these intents. + IntentFilter intentFilter = new IntentFilter(ACTION_TASK_EXPIRED); + intentFilter.addAction(ACTION_TASK_DELAY_EXPIRED); + mContext.registerReceiver(mAlarmExpiredReceiver, intentFilter); + } + + /** + * Check if the task has a timing constraint, and if so determine where to insert it in our + * list. + */ + @Override + public synchronized void maybeTrackTaskState(TaskStatus task) { + if (task.hasTimingConstraint()) { + ListIterator<TaskStatus> it = mTrackedTasks.listIterator(mTrackedTasks.size()); + while (it.hasPrevious()) { + TaskStatus ts = it.previous(); + if (ts.equals(task)) { + // Update + it.remove(); + it.add(task); + break; + } else if (ts.getLatestRunTime() < task.getLatestRunTime()) { + // Insert + it.add(task); + break; + } + } + maybeUpdateAlarms(task.getEarliestRunTime(), task.getLatestRunTime()); + } + } + + /** + * If the task passed in is being tracked, figure out if we need to update our alarms, and if + * so, update them. + */ + @Override + public synchronized void removeTaskStateIfTracked(TaskStatus taskStatus) { + if (mTrackedTasks.remove(taskStatus)) { + if (mNextDelayExpiredElapsedMillis <= taskStatus.getEarliestRunTime()) { + handleTaskDelayExpired(); + } + if (mNextTaskExpiredElapsedMillis <= taskStatus.getLatestRunTime()) { + handleTaskDeadlineExpired(); + } + } + } + + /** + * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a task's + * delay will expire. + * This alarm <b>will not</b> wake up the phone. + */ + private void setDelayExpiredAlarm(long alarmTimeElapsedMillis) { + ensureAlarmService(); + mAlarmService.set(AlarmManager.ELAPSED_REALTIME, alarmTimeElapsedMillis, + mNextDelayExpiredAlarmIntent); + } + + /** + * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a task's + * deadline will expire. + * This alarm <b>will</b> wake up the phone. + */ + private void setDeadlineExpiredAlarm(long alarmTimeElapsedMillis) { + ensureAlarmService(); + mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTimeElapsedMillis, + mTaskExpiredAlarmIntent); + } + + /** + * Determines whether this controller can stop tracking the given task. + * The controller is no longer interested in a task once its time constraint is satisfied, and + * the task's deadline is fulfilled - unlike other controllers a time constraint can't toggle + * back and forth. + */ + private boolean canStopTrackingTask(TaskStatus taskStatus) { + final long elapsedNowMillis = SystemClock.elapsedRealtime(); + return taskStatus.timeConstraintSatisfied.get() && + (taskStatus.getLatestRunTime() == Long.MAX_VALUE || + taskStatus.getLatestRunTime() < elapsedNowMillis); + } + + private void maybeUpdateAlarms(long delayExpiredElapsed, long deadlineExpiredElapsed) { + if (delayExpiredElapsed < mNextDelayExpiredElapsedMillis) { + mNextDelayExpiredElapsedMillis = delayExpiredElapsed; + setDelayExpiredAlarm(mNextDelayExpiredElapsedMillis); + } + if (deadlineExpiredElapsed < mNextTaskExpiredElapsedMillis) { + mNextTaskExpiredElapsedMillis = deadlineExpiredElapsed; + setDeadlineExpiredAlarm(mNextTaskExpiredElapsedMillis); + } + } + + private void ensureAlarmService() { + if (mAlarmService == null) { + mAlarmService = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); + } + } + + /** + * Handles alarm that notifies that a task has expired. When this function is called at least + * one task must be run. + */ + private synchronized void handleTaskDeadlineExpired() { + long nextExpiryTime = Long.MAX_VALUE; + final long nowElapsedMillis = SystemClock.elapsedRealtime(); + + Iterator<TaskStatus> it = mTrackedTasks.iterator(); + while (it.hasNext()) { + TaskStatus ts = it.next(); + final long taskDeadline = ts.getLatestRunTime(); + + if (taskDeadline <= nowElapsedMillis) { + ts.timeConstraintSatisfied.set(true); + mStateChangedListener.onTaskDeadlineExpired(ts); + it.remove(); + } else { // Sorted by expiry time, so take the next one and stop. + nextExpiryTime = taskDeadline; + break; + } + } + maybeUpdateAlarms(Long.MAX_VALUE, nextExpiryTime); + } + + /** + * Handles alarm that notifies us that a task's delay has expired. Iterates through the list of + * tracked tasks and marks them as ready as appropriate. + */ + private synchronized void handleTaskDelayExpired() { + final long nowElapsedMillis = SystemClock.elapsedRealtime(); + long nextDelayTime = Long.MAX_VALUE; + + Iterator<TaskStatus> it = mTrackedTasks.iterator(); + while (it.hasNext()) { + final TaskStatus ts = it.next(); + final long taskDelayTime = ts.getEarliestRunTime(); + if (taskDelayTime < nowElapsedMillis) { + ts.timeConstraintSatisfied.set(true); + mStateChangedListener.onTaskStateChanged(ts); + if (canStopTrackingTask(ts)) { + it.remove(); + } + } else { // Keep going through list to get next delay time. + if (nextDelayTime > taskDelayTime) { + nextDelayTime = taskDelayTime; + } + } + } + maybeUpdateAlarms(nextDelayTime, Long.MAX_VALUE); + } + + private final BroadcastReceiver mAlarmExpiredReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // An task has just expired, so we run through the list of tasks that we have and + // notify our StateChangedListener. + if (ACTION_TASK_EXPIRED.equals(intent.getAction())) { + handleTaskDeadlineExpired(); + } else if (ACTION_TASK_DELAY_EXPIRED.equals(intent.getAction())) { + handleTaskDelayExpired(); + } + } + }; +} diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 50dd27da48c8..05f99475a495 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -134,7 +134,7 @@ public final class TvInputManagerService extends SystemService { private void buildTvInputListLocked(int userId) { UserState userState = getUserStateLocked(userId); - userState.inputList.clear(); + userState.inputMap.clear(); if (DEBUG) Slog.d(TAG, "buildTvInputList"); PackageManager pm = mContext.getPackageManager(); @@ -149,7 +149,7 @@ public final class TvInputManagerService extends SystemService { } TvInputInfo info = new TvInputInfo(ri); if (DEBUG) Slog.d(TAG, "add " + info.getId()); - userState.inputList.add(info); + userState.inputMap.put(info.getId(), info); } } @@ -179,9 +179,9 @@ public final class TvInputManagerService extends SystemService { } // Release created sessions. for (SessionState state : userState.sessionStateMap.values()) { - if (state.session != null) { + if (state.mSession != null) { try { - state.session.release(); + state.mSession.release(); } catch (RemoteException e) { Slog.e(TAG, "error in release", e); } @@ -191,15 +191,15 @@ public final class TvInputManagerService extends SystemService { // Unregister all callbacks and unbind all services. for (ServiceState serviceState : userState.serviceStateMap.values()) { - if (serviceState.callback != null) { + if (serviceState.mCallback != null) { try { - serviceState.service.unregisterCallback(serviceState.callback); + serviceState.mService.unregisterCallback(serviceState.mCallback); } catch (RemoteException e) { Slog.e(TAG, "error in unregisterCallback", e); } } - serviceState.clients.clear(); - mContext.unbindService(serviceState.connection); + serviceState.mClients.clear(); + mContext.unbindService(serviceState.mConnection); } userState.serviceStateMap.clear(); @@ -215,28 +215,33 @@ public final class TvInputManagerService extends SystemService { return userState; } - private ServiceState getServiceStateLocked(ComponentName name, int userId) { + private ServiceState getServiceStateLocked(String inputId, int userId) { UserState userState = getUserStateLocked(userId); - ServiceState serviceState = userState.serviceStateMap.get(name); + ServiceState serviceState = userState.serviceStateMap.get(inputId); if (serviceState == null) { - throw new IllegalStateException("Service state not found for " + name + " (userId=" + throw new IllegalStateException("Service state not found for " + inputId + " (userId=" + userId + ")"); } return serviceState; } - private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) { + private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) { UserState userState = getUserStateLocked(userId); SessionState sessionState = userState.sessionStateMap.get(sessionToken); if (sessionState == null) { throw new IllegalArgumentException("Session state not found for token " + sessionToken); } // Only the application that requested this session or the system can access it. - if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.callingUid) { + if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.mCallingUid) { throw new SecurityException("Illegal access to the session with token " + sessionToken + " from uid " + callingUid); } - ITvInputSession session = sessionState.session; + return sessionState; + } + + private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) { + SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId); + ITvInputSession session = sessionState.mSession; if (session == null) { throw new IllegalStateException("Session not yet created for token " + sessionToken); } @@ -249,38 +254,47 @@ public final class TvInputManagerService extends SystemService { false, methodName, null); } - private void updateServiceConnectionLocked(ComponentName name, int userId) { + private void updateServiceConnectionLocked(String inputId, int userId) { UserState userState = getUserStateLocked(userId); - ServiceState serviceState = userState.serviceStateMap.get(name); + ServiceState serviceState = userState.serviceStateMap.get(inputId); if (serviceState == null) { return; } - boolean isStateEmpty = serviceState.clients.isEmpty() - && serviceState.sessionTokens.isEmpty(); - if (serviceState.service == null && !isStateEmpty && userId == mCurrentUserId) { + if (serviceState.mReconnecting) { + if (!serviceState.mSessionTokens.isEmpty()) { + // wait until all the sessions are removed. + return; + } + serviceState.mReconnecting = false; + } + boolean isStateEmpty = serviceState.mClients.isEmpty() + && serviceState.mSessionTokens.isEmpty(); + if (serviceState.mService == null && !isStateEmpty && userId == mCurrentUserId) { // This means that the service is not yet connected but its state indicates that we // have pending requests. Then, connect the service. - if (serviceState.bound) { + if (serviceState.mBound) { // We have already bound to the service so we don't try to bind again until after we // unbind later on. return; } if (DEBUG) { - Slog.d(TAG, "bindServiceAsUser(name=" + name.getClassName() + ", userId=" + userId + Slog.d(TAG, "bindServiceAsUser(inputId=" + inputId + ", userId=" + userId + ")"); } - Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(name); - mContext.bindServiceAsUser(i, serviceState.connection, Context.BIND_AUTO_CREATE, + + Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent( + userState.inputMap.get(inputId).getComponent()); + mContext.bindServiceAsUser(i, serviceState.mConnection, Context.BIND_AUTO_CREATE, new UserHandle(userId)); - serviceState.bound = true; - } else if (serviceState.service != null && isStateEmpty) { + serviceState.mBound = true; + } else if (serviceState.mService != null && isStateEmpty) { // This means that the service is already connected but its state indicates that we have // nothing to do with it. Then, disconnect the service. if (DEBUG) { - Slog.d(TAG, "unbindService(name=" + name.getClassName() + ")"); + Slog.d(TAG, "unbindService(inputId=" + inputId + ")"); } - mContext.unbindService(serviceState.connection); - userState.serviceStateMap.remove(name); + mContext.unbindService(serviceState.mConnection); + userState.serviceStateMap.remove(inputId); } } @@ -289,8 +303,7 @@ public final class TvInputManagerService extends SystemService { final SessionState sessionState = getUserStateLocked(userId).sessionStateMap.get(sessionToken); if (DEBUG) { - Slog.d(TAG, "createSessionInternalLocked(name=" + sessionState.name.getClassName() - + ")"); + Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.mInputId + ")"); } final InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString()); @@ -300,17 +313,22 @@ public final class TvInputManagerService extends SystemService { @Override public void onSessionCreated(ITvInputSession session) { if (DEBUG) { - Slog.d(TAG, "onSessionCreated(name=" + sessionState.name.getClassName() + ")"); + Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.mInputId + ")"); } synchronized (mLock) { - sessionState.session = session; + sessionState.mSession = session; if (session == null) { removeSessionStateLocked(sessionToken, userId); - sendSessionTokenToClientLocked(sessionState.client, sessionState.name, null, - null, sessionState.seq, userId); + sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, + null, null, sessionState.mSeq, userId); } else { - sendSessionTokenToClientLocked(sessionState.client, sessionState.name, - sessionToken, channels[0], sessionState.seq, userId); + try { + session.asBinder().linkToDeath(sessionState, 0); + } catch (RemoteException e) { + Slog.e(TAG, "Session is already died."); + } + sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, + sessionToken, channels[0], sessionState.mSeq, userId); } channels[0].dispose(); } @@ -323,24 +341,32 @@ public final class TvInputManagerService extends SystemService { } catch (RemoteException e) { Slog.e(TAG, "error in createSession", e); removeSessionStateLocked(sessionToken, userId); - sendSessionTokenToClientLocked(sessionState.client, sessionState.name, null, null, - sessionState.seq, userId); + sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, null, null, + sessionState.mSeq, userId); } channels[1].dispose(); } - private void sendSessionTokenToClientLocked(ITvInputClient client, ComponentName name, + private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId, IBinder sessionToken, InputChannel channel, int seq, int userId) { try { - client.onSessionCreated(name, sessionToken, channel, seq); + client.onSessionCreated(inputId, sessionToken, channel, seq); } catch (RemoteException exception) { Slog.e(TAG, "error in onSessionCreated", exception); } + } - if (sessionToken == null) { - // This means that the session creation failed. We might want to disconnect the service. - updateServiceConnectionLocked(name, userId); + private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) { + SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId); + if (sessionState.mSession != null) { + try { + sessionState.mSession.release(); + } catch (RemoteException e) { + Slog.w(TAG, "session is already disapeared", e); + } + sessionState.mSession = null; } + removeSessionStateLocked(sessionToken, userId); } private void removeSessionStateLocked(IBinder sessionToken, int userId) { @@ -349,19 +375,31 @@ public final class TvInputManagerService extends SystemService { SessionState sessionState = userState.sessionStateMap.remove(sessionToken); // Close the open log entry, if any. - if (sessionState.logUri != null) { + if (sessionState.mLogUri != null) { SomeArgs args = SomeArgs.obtain(); - args.arg1 = sessionState.logUri; + args.arg1 = sessionState.mLogUri; args.arg2 = System.currentTimeMillis(); mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args).sendToTarget(); } // Also remove the session token from the session token list of the current service. - ServiceState serviceState = userState.serviceStateMap.get(sessionState.name); + ServiceState serviceState = userState.serviceStateMap.get(sessionState.mInputId); if (serviceState != null) { - serviceState.sessionTokens.remove(sessionToken); + serviceState.mSessionTokens.remove(sessionToken); + } + updateServiceConnectionLocked(sessionState.mInputId, userId); + } + + private void broadcastServiceAvailabilityChangedLocked(ServiceState serviceState) { + for (IBinder iBinder : serviceState.mClients) { + ITvInputClient client = ITvInputClient.Stub.asInterface(iBinder); + try { + client.onAvailabilityChanged( + serviceState.mTvInputInfo.getId(), serviceState.mAvailable); + } catch (RemoteException e) { + Slog.e(TAG, "error in onAvailabilityChanged", e); + } } - updateServiceConnectionLocked(sessionState.name, userId); } private final class BinderService extends ITvInputManager.Stub { @@ -373,7 +411,7 @@ public final class TvInputManagerService extends SystemService { try { synchronized (mLock) { UserState userState = getUserStateLocked(resolvedUserId); - return new ArrayList<TvInputInfo>(userState.inputList); + return new ArrayList<TvInputInfo>(userState.inputMap.values()); } } finally { Binder.restoreCallingIdentity(identity); @@ -381,7 +419,7 @@ public final class TvInputManagerService extends SystemService { } @Override - public boolean getAvailability(final ITvInputClient client, final ComponentName name, + public boolean getAvailability(final ITvInputClient client, final String inputId, int userId) { final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), Binder.getCallingUid(), userId, "getAvailability"); @@ -389,11 +427,11 @@ public final class TvInputManagerService extends SystemService { try { synchronized (mLock) { UserState userState = getUserStateLocked(resolvedUserId); - ServiceState serviceState = userState.serviceStateMap.get(name); + ServiceState serviceState = userState.serviceStateMap.get(inputId); if (serviceState != null) { // We already know the status of this input service. Return the cached // status. - return serviceState.available; + return serviceState.mAvailable; } } } finally { @@ -403,7 +441,7 @@ public final class TvInputManagerService extends SystemService { } @Override - public void registerCallback(final ITvInputClient client, final ComponentName name, + public void registerCallback(final ITvInputClient client, final String inputId, int userId) { final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), Binder.getCallingUid(), userId, "registerCallback"); @@ -413,28 +451,29 @@ public final class TvInputManagerService extends SystemService { // Create a new service callback and add it to the callback map of the current // service. UserState userState = getUserStateLocked(resolvedUserId); - ServiceState serviceState = userState.serviceStateMap.get(name); + ServiceState serviceState = userState.serviceStateMap.get(inputId); if (serviceState == null) { - serviceState = new ServiceState(resolvedUserId); - userState.serviceStateMap.put(name, serviceState); + serviceState = new ServiceState( + userState.inputMap.get(inputId), resolvedUserId); + userState.serviceStateMap.put(inputId, serviceState); } IBinder iBinder = client.asBinder(); - if (!serviceState.clients.contains(iBinder)) { - serviceState.clients.add(iBinder); + if (!serviceState.mClients.contains(iBinder)) { + serviceState.mClients.add(iBinder); } - if (serviceState.service != null) { - if (serviceState.callback != null) { + if (serviceState.mService != null) { + if (serviceState.mCallback != null) { // We already handled. return; } - serviceState.callback = new ServiceCallback(resolvedUserId); + serviceState.mCallback = new ServiceCallback(resolvedUserId); try { - serviceState.service.registerCallback(serviceState.callback); + serviceState.mService.registerCallback(serviceState.mCallback); } catch (RemoteException e) { Slog.e(TAG, "error in registerCallback", e); } } else { - updateServiceConnectionLocked(name, resolvedUserId); + updateServiceConnectionLocked(inputId, resolvedUserId); } } } finally { @@ -443,34 +482,34 @@ public final class TvInputManagerService extends SystemService { } @Override - public void unregisterCallback(ITvInputClient client, ComponentName name, int userId) { + public void unregisterCallback(ITvInputClient client, String inputId, int userId) { final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), Binder.getCallingUid(), userId, "unregisterCallback"); final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { UserState userState = getUserStateLocked(resolvedUserId); - ServiceState serviceState = userState.serviceStateMap.get(name); + ServiceState serviceState = userState.serviceStateMap.get(inputId); if (serviceState == null) { return; } // Remove this client from the client list and unregister the callback. - serviceState.clients.remove(client.asBinder()); - if (!serviceState.clients.isEmpty()) { + serviceState.mClients.remove(client.asBinder()); + if (!serviceState.mClients.isEmpty()) { // We have other clients who want to keep the callback. Do this later. return; } - if (serviceState.service == null || serviceState.callback == null) { + if (serviceState.mService == null || serviceState.mCallback == null) { return; } try { - serviceState.service.unregisterCallback(serviceState.callback); + serviceState.mService.unregisterCallback(serviceState.mCallback); } catch (RemoteException e) { Slog.e(TAG, "error in unregisterCallback", e); } finally { - serviceState.callback = null; - updateServiceConnectionLocked(name, resolvedUserId); + serviceState.mCallback = null; + updateServiceConnectionLocked(inputId, resolvedUserId); } } } finally { @@ -479,7 +518,7 @@ public final class TvInputManagerService extends SystemService { } @Override - public void createSession(final ITvInputClient client, final ComponentName name, + public void createSession(final ITvInputClient client, final String inputId, int seq, int userId) { final int callingUid = Binder.getCallingUid(); final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid, @@ -487,28 +526,35 @@ public final class TvInputManagerService extends SystemService { final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { + UserState userState = getUserStateLocked(resolvedUserId); + ServiceState serviceState = userState.serviceStateMap.get(inputId); + if (serviceState == null) { + serviceState = new ServiceState( + userState.inputMap.get(inputId), resolvedUserId); + userState.serviceStateMap.put(inputId, serviceState); + } + // Send a null token immediately while reconnecting. + if (serviceState.mReconnecting == true) { + sendSessionTokenToClientLocked(client, inputId, null, null, seq, userId); + return; + } + // Create a new session token and a session state. IBinder sessionToken = new Binder(); - SessionState sessionState = new SessionState(name, client, seq, callingUid); - sessionState.session = null; + SessionState sessionState = new SessionState( + sessionToken, inputId, client, seq, callingUid, resolvedUserId); // Add them to the global session state map of the current user. - UserState userState = getUserStateLocked(resolvedUserId); userState.sessionStateMap.put(sessionToken, sessionState); // Also, add them to the session state map of the current service. - ServiceState serviceState = userState.serviceStateMap.get(name); - if (serviceState == null) { - serviceState = new ServiceState(resolvedUserId); - userState.serviceStateMap.put(name, serviceState); - } - serviceState.sessionTokens.add(sessionToken); + serviceState.mSessionTokens.add(sessionToken); - if (serviceState.service != null) { - createSessionInternalLocked(serviceState.service, sessionToken, + if (serviceState.mService != null) { + createSessionInternalLocked(serviceState.mService, sessionToken, resolvedUserId); } else { - updateServiceConnectionLocked(name, resolvedUserId); + updateServiceConnectionLocked(inputId, resolvedUserId); } } } finally { @@ -524,14 +570,7 @@ public final class TvInputManagerService extends SystemService { final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { - // Release the session. - try { - getSessionLocked(sessionToken, callingUid, resolvedUserId).release(); - } catch (RemoteException e) { - Slog.e(TAG, "error in release", e); - } - - removeSessionStateLocked(sessionToken, resolvedUserId); + releaseSessionLocked(sessionToken, callingUid, resolvedUserId); } } finally { Binder.restoreCallingIdentity(identity); @@ -599,9 +638,9 @@ public final class TvInputManagerService extends SystemService { // Close the open log entry first, if any. UserState userState = getUserStateLocked(resolvedUserId); SessionState sessionState = userState.sessionStateMap.get(sessionToken); - if (sessionState.logUri != null) { + if (sessionState.mLogUri != null) { SomeArgs args = SomeArgs.obtain(); - args.arg1 = sessionState.logUri; + args.arg1 = sessionState.mLogUri; args.arg2 = currentTime; mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args) .sendToTarget(); @@ -614,10 +653,10 @@ public final class TvInputManagerService extends SystemService { values.put(TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS, 0); values.put(TvContract.WatchedPrograms.CHANNEL_ID, channelId); - sessionState.logUri = mContentResolver.insert( + sessionState.mLogUri = mContentResolver.insert( TvContract.WatchedPrograms.CONTENT_URI, values); SomeArgs args = SomeArgs.obtain(); - args.arg1 = sessionState.logUri; + args.arg1 = sessionState.mLogUri; args.arg2 = ContentUris.parseId(channelUri); args.arg3 = currentTime; mLogHandler.obtainMessage(LogHandler.MSG_OPEN_ENTRY, args).sendToTarget(); @@ -694,12 +733,12 @@ public final class TvInputManagerService extends SystemService { } private static final class UserState { - // A list of all known TV inputs on the system. - private final List<TvInputInfo> inputList = new ArrayList<TvInputInfo>(); + // A mapping from the TV input id to its TvInputInfo. + private final Map<String, TvInputInfo> inputMap = new HashMap<String,TvInputInfo>(); // A mapping from the name of a TV input service to its state. - private final Map<ComponentName, ServiceState> serviceStateMap = - new HashMap<ComponentName, ServiceState>(); + private final Map<String, ServiceState> serviceStateMap = + new HashMap<String, ServiceState>(); // A mapping from the token of a TV input session to its state. private final Map<IBinder, SessionState> sessionStateMap = @@ -707,66 +746,91 @@ public final class TvInputManagerService extends SystemService { } private final class ServiceState { - private final List<IBinder> clients = new ArrayList<IBinder>(); - private final List<IBinder> sessionTokens = new ArrayList<IBinder>(); - private final ServiceConnection connection; - - private ITvInputService service; - private ServiceCallback callback; - private boolean bound; - private boolean available; - - private ServiceState(int userId) { - this.connection = new InputServiceConnection(userId); + // TODO: need to implement DeathRecipient for clients. + private final List<IBinder> mClients = new ArrayList<IBinder>(); + private final List<IBinder> mSessionTokens = new ArrayList<IBinder>(); + private final ServiceConnection mConnection; + private final TvInputInfo mTvInputInfo; + + private ITvInputService mService; + private ServiceCallback mCallback; + private boolean mBound; + private boolean mAvailable; + private boolean mReconnecting; + + private ServiceState(TvInputInfo inputInfo, int userId) { + mTvInputInfo = inputInfo; + mConnection = new InputServiceConnection(inputInfo, userId); } } - private static final class SessionState { - private final ComponentName name; - private final ITvInputClient client; - private final int seq; - private final int callingUid; - - private ITvInputSession session; - private Uri logUri; + private final class SessionState implements IBinder.DeathRecipient { + private final String mInputId; + private final ITvInputClient mClient; + private final int mSeq; + private final int mCallingUid; + private final int mUserId; + private final IBinder mToken; + private ITvInputSession mSession; + private Uri mLogUri; + + private SessionState(IBinder token, String inputId, ITvInputClient client, int seq, + int callingUid, int userId) { + mToken = token; + mInputId = inputId; + mClient = client; + mSeq = seq; + mCallingUid = callingUid; + mUserId = userId; + } - private SessionState(ComponentName name, ITvInputClient client, int seq, int callingUid) { - this.name = name; - this.client = client; - this.seq = seq; - this.callingUid = callingUid; + @Override + public void binderDied() { + synchronized (mLock) { + mSession = null; + if (mClient != null) { + try { + mClient.onSessionReleased(mSeq); + } catch(RemoteException e) { + Slog.e(TAG, "error in onSessionReleased", e); + } + } + removeSessionStateLocked(mToken, mUserId); + } } } private final class InputServiceConnection implements ServiceConnection { + private final TvInputInfo mTvInputInfo; private final int mUserId; - private InputServiceConnection(int userId) { + private InputServiceConnection(TvInputInfo inputInfo, int userId) { mUserId = userId; + mTvInputInfo = inputInfo; } @Override public void onServiceConnected(ComponentName name, IBinder service) { if (DEBUG) { - Slog.d(TAG, "onServiceConnected(name=" + name.getClassName() + ")"); + Slog.d(TAG, "onServiceConnected(inputId=" + mTvInputInfo.getId() + ")"); } synchronized (mLock) { - ServiceState serviceState = getServiceStateLocked(name, mUserId); - serviceState.service = ITvInputService.Stub.asInterface(service); + ServiceState serviceState = getServiceStateLocked(mTvInputInfo.getId(), mUserId); + serviceState.mService = ITvInputService.Stub.asInterface(service); // Register a callback, if we need to. - if (!serviceState.clients.isEmpty() && serviceState.callback == null) { - serviceState.callback = new ServiceCallback(mUserId); + if (!serviceState.mClients.isEmpty() && serviceState.mCallback == null) { + serviceState.mCallback = new ServiceCallback(mUserId); try { - serviceState.service.registerCallback(serviceState.callback); + serviceState.mService.registerCallback(serviceState.mCallback); } catch (RemoteException e) { Slog.e(TAG, "error in registerCallback", e); } } // And create sessions, if any. - for (IBinder sessionToken : serviceState.sessionTokens) { - createSessionInternalLocked(serviceState.service, sessionToken, mUserId); + for (IBinder sessionToken : serviceState.mSessionTokens) { + createSessionInternalLocked(serviceState.mService, sessionToken, mUserId); } } } @@ -774,7 +838,38 @@ public final class TvInputManagerService extends SystemService { @Override public void onServiceDisconnected(ComponentName name) { if (DEBUG) { - Slog.d(TAG, "onServiceDisconnected(name=" + name.getClassName() + ")"); + Slog.d(TAG, "onServiceDisconnected(inputId=" + mTvInputInfo.getId() + ")"); + } + if (!mTvInputInfo.getComponent().equals(name)) { + throw new IllegalArgumentException("Mismatched ComponentName: " + + mTvInputInfo.getComponent() + " (expected), " + name + " (actual)."); + } + synchronized (mLock) { + UserState userState = getUserStateLocked(mUserId); + ServiceState serviceState = userState.serviceStateMap.get(mTvInputInfo.getId()); + if (serviceState != null) { + serviceState.mReconnecting = true; + serviceState.mBound = false; + serviceState.mService = null; + serviceState.mCallback = null; + + // Send null tokens for not finishing create session events. + for (IBinder sessionToken : serviceState.mSessionTokens) { + SessionState sessionState = userState.sessionStateMap.get(sessionToken); + if (sessionState.mSession == null) { + removeSessionStateLocked(sessionToken, sessionState.mUserId); + sendSessionTokenToClientLocked(sessionState.mClient, + sessionState.mInputId, null, null, sessionState.mSeq, + sessionState.mUserId); + } + } + + if (serviceState.mAvailable) { + serviceState.mAvailable = false; + broadcastServiceAvailabilityChangedLocked(serviceState); + } + updateServiceConnectionLocked(mTvInputInfo.getId(), mUserId); + } } } } @@ -787,18 +882,16 @@ public final class TvInputManagerService extends SystemService { } @Override - public void onAvailabilityChanged(ComponentName name, boolean isAvailable) - throws RemoteException { + public void onAvailabilityChanged(String inputId, boolean isAvailable) { if (DEBUG) { - Slog.d(TAG, "onAvailabilityChanged(name=" + name.getClassName() + ", isAvailable=" + Slog.d(TAG, "onAvailabilityChanged(inputId=" + inputId + ", isAvailable=" + isAvailable + ")"); } synchronized (mLock) { - ServiceState serviceState = getServiceStateLocked(name, mUserId); - serviceState.available = isAvailable; - for (IBinder iBinder : serviceState.clients) { - ITvInputClient client = ITvInputClient.Stub.asInterface(iBinder); - client.onAvailabilityChanged(name, isAvailable); + ServiceState serviceState = getServiceStateLocked(inputId, mUserId); + if (serviceState.mAvailable != isAvailable) { + serviceState.mAvailable = isAvailable; + broadcastServiceAvailabilityChangedLocked(serviceState); } } } diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl index 8ea9b0d66a98..b104c11c339b 100644 --- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -18,7 +18,7 @@ package com.android.internal.telephony; import android.content.Intent; import android.net.LinkProperties; -import android.net.LinkCapabilities; +import android.net.NetworkCapabilities; import android.os.Bundle; import android.telephony.CellInfo; import android.telephony.DataConnectionRealTimeInfo; @@ -37,7 +37,7 @@ interface ITelephonyRegistry { void notifyDataActivity(int state); void notifyDataConnection(int state, boolean isDataConnectivityPossible, String reason, String apn, String apnType, in LinkProperties linkProperties, - in LinkCapabilities linkCapabilities, int networkType, boolean roaming); + in NetworkCapabilities networkCapabilities, int networkType, boolean roaming); void notifyDataConnectionFailed(String reason, String apnType); void notifyCellLocation(in Bundle cellLocation); void notifyOtaspChanged(in int otaspMode); diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java index 8c42d25a2ddc..6ad57cf46dff 100644 --- a/telephony/java/com/android/internal/telephony/PhoneConstants.java +++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java @@ -78,7 +78,7 @@ public class PhoneConstants { public static final String DATA_APN_TYPE_KEY = "apnType"; public static final String DATA_APN_KEY = "apn"; public static final String DATA_LINK_PROPERTIES_KEY = "linkProperties"; - public static final String DATA_LINK_CAPABILITIES_KEY = "linkCapabilities"; + public static final String DATA_NETWORK_CAPABILITIES_KEY = "networkCapabilities"; public static final String DATA_IFACE_NAME_KEY = "iface"; public static final String NETWORK_UNAVAILABLE_KEY = "networkUnvailable"; diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java index 5c273de13346..1d0a8062af8f 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java @@ -16,6 +16,7 @@ package com.android.test.hwui; +import android.animation.TimeInterpolator; import android.app.Activity; import android.content.Context; import android.graphics.Canvas; @@ -27,6 +28,8 @@ import android.os.Trace; import android.view.HardwareCanvas; import android.view.RenderNodeAnimator; import android.view.View; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.OvershootInterpolator; import android.webkit.WebChromeClient; import android.webkit.WebView; import android.webkit.WebViewClient; @@ -122,8 +125,11 @@ public class CirclePropActivity extends Activity { mPaint, RenderNodeAnimator.PAINT_STROKE_WIDTH, RenderNodeAnimator.DELTA_TYPE_ABSOLUTE, mToggle ? 5.0f : 60.0f)); + TimeInterpolator interp = new OvershootInterpolator(3.0f); for (int i = 0; i < mRunningAnimations.size(); i++) { - mRunningAnimations.get(i).start(this); + RenderNodeAnimator anim = mRunningAnimations.get(i); + anim.setInterpolator(interp); + anim.start(this); } if (mToggle) { diff --git a/tests/Split/Android.mk b/tests/Split/Android.mk new file mode 100644 index 000000000000..7884d4dc9944 --- /dev/null +++ b/tests/Split/Android.mk @@ -0,0 +1,28 @@ +# +# 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. +# + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_PACKAGE_NAME := Split + +LOCAL_AAPT_FLAGS := --split fr,de +LOCAL_AAPT_FLAGS += -v + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_PACKAGE) diff --git a/tests/Split/AndroidManifest.xml b/tests/Split/AndroidManifest.xml new file mode 100644 index 000000000000..a4956a760553 --- /dev/null +++ b/tests/Split/AndroidManifest.xml @@ -0,0 +1,27 @@ +<?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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.example.split"> + <application android:label="@string/app_title"> + <activity android:name="ActivityMain"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/tests/Split/assets/blah.txt b/tests/Split/assets/blah.txt new file mode 100644 index 000000000000..1b37e40995d7 --- /dev/null +++ b/tests/Split/assets/blah.txt @@ -0,0 +1 @@ +This is some useful info. diff --git a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml b/tests/Split/assets/statement.xml index a01892905b75..91750d1cd2ce 100644 --- a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml +++ b/tests/Split/assets/statement.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2014 The Android Open Source Project +<!-- 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. @@ -15,6 +14,6 @@ limitations under the License. --> -<bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ic_qs_contrast_alpha" - android:tint="@color/ic_qs_on" /> +<statement> + <value>Hello</value> +</statement> diff --git a/tests/Split/res/layout-fr-sw600dp/main.xml b/tests/Split/res/layout-fr-sw600dp/main.xml new file mode 100644 index 000000000000..2461c8ce3827 --- /dev/null +++ b/tests/Split/res/layout-fr-sw600dp/main.xml @@ -0,0 +1,23 @@ +<?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. +--> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> +</FrameLayout> diff --git a/packages/SystemUI/res/drawable/ic_qs_contrast_off.xml b/tests/Split/res/layout/main.xml index 5f65d8aceee4..36992a2f193c 100644 --- a/packages/SystemUI/res/drawable/ic_qs_contrast_off.xml +++ b/tests/Split/res/layout/main.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2014 The Android Open Source Project +<!-- 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. @@ -15,6 +14,6 @@ limitations under the License. --> -<bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ic_qs_contrast_alpha" - android:tint="@color/ic_qs_off" /> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"/> diff --git a/tests/Split/res/values-de/values.xml b/tests/Split/res/values-de/values.xml new file mode 100644 index 000000000000..26d050736186 --- /dev/null +++ b/tests/Split/res/values-de/values.xml @@ -0,0 +1,19 @@ +<?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. +--> + +<resources> + <string name="test">Achtung!</string> +</resources> diff --git a/tests/Split/res/values-fr/values.xml b/tests/Split/res/values-fr/values.xml new file mode 100644 index 000000000000..16532da1df7f --- /dev/null +++ b/tests/Split/res/values-fr/values.xml @@ -0,0 +1,25 @@ +<?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. +--> + +<resources> + <string name="app_title">APK Divisé</string> + <string name="test">Bonjour, Monde!</string> + <string name="blah">Bleh..</string> + <string-array name="lotsofstrings"> + <item>Hé là</item> + <item>Au revoir</item> + </string-array> +</resources> diff --git a/tests/Split/res/values-sw600dp/values.xml b/tests/Split/res/values-sw600dp/values.xml new file mode 100644 index 000000000000..a8329bba168b --- /dev/null +++ b/tests/Split/res/values-sw600dp/values.xml @@ -0,0 +1,19 @@ +<?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. +--> + +<resources> + <dimen name="width">230dp</dimen> +</resources> diff --git a/tests/Split/res/values/values.xml b/tests/Split/res/values/values.xml new file mode 100644 index 000000000000..68edc77d47f8 --- /dev/null +++ b/tests/Split/res/values/values.xml @@ -0,0 +1,47 @@ +<?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. +--> + +<resources> + <string name="app_title">Split APK</string> + <string name="test">Hello, World!</string> + <string name="boom">Boom!</string> + <string name="blah">Blah...</string> + <string-array name="lotsofstrings"> + <item>Hello there</item> + <item>Good bye</item> + </string-array> + + <plurals name="plur"> + <item quantity="zero">I no haz :(</item> + <item quantity="one">I haz 1!1! :)</item> + <item quantity="many">I haz ALL!</item> + </plurals> + + <bool name="que">true</bool> + <color name="green">#00FF00</color> + <dimen name="width">23dp</dimen> + <item type="id" name="identifier" /> + <integer name="number">123</integer> + <integer-array name="numList"> + <item>1234</item> + </integer-array> + + <array name="ary"> + <item>@string/test</item> + <item>@string/boom</item> + <item>25dp</item> + </array> +</resources> diff --git a/tests/Split/src/java/com/android/example/split/ActivityMain.java b/tests/Split/src/java/com/android/example/split/ActivityMain.java new file mode 100644 index 000000000000..a15fb3c2647b --- /dev/null +++ b/tests/Split/src/java/com/android/example/split/ActivityMain.java @@ -0,0 +1,31 @@ +/* + * 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.example.split; + +import android.app.Activity; +import android.os.Bundle; +import android.widget.TextView; + +public class ActivityMain extends Activity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + TextView text = new TextView(this); + text.setText(R.string.test); + setContentView(text); + } +} diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml index d0f2a2db366a..118f25800dab 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml @@ -24,13 +24,12 @@ android:viewportHeight="480" android:viewportWidth="480" /> - <group> - <path - android:name="box1" - android:pathData="m20,200l100,90l180,-180l-35,-35l-145,145l-60,-60l-40,40z" - android:fill="?android:attr/colorControlActivated" - android:stroke="?android:attr/colorControlActivated" - android:strokeLineCap="round" - android:strokeLineJoin="round" /> - </group> -</vector> + <path + android:name="box1" + android:fill="?android:attr/colorControlActivated" + android:pathData="m20,200l100,90l180,-180l-35,-35l-145,145l-60,-60l-40,40z" + android:stroke="?android:attr/colorControlActivated" + android:strokeLineCap="round" + android:strokeLineJoin="round" /> + +</vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml index 728624a149ac..034f7a0b95c5 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml @@ -1,4 +1,5 @@ -<!-- Copyright (C) 2014 The Android Open Source Project +<!-- + 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. @@ -15,23 +16,23 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" > <size - android:width="64dp" - android:height="64dp"/> + android:height="64dp" + android:width="64dp" /> - <viewport android:viewportWidth="320" - android:viewportHeight="320"/> + <viewport + android:viewportHeight="320" + android:viewportWidth="320" /> - <group> - <path - android:name="house" - android:pathData="M 130,225 L 130,115 L 130,115 L 70,15 L 10,115 L 10,115 L 10,225 z" - android:fill="#ff440000" - android:stroke="#FF00FF00" - android:strokeWidth="10" - android:rotation="180" - android:pivotX="70" - android:pivotY="120" - android:trimPathStart=".1" - android:trimPathEnd=".9"/> - </group> -</vector> + <path + android:name="house" + android:fill="#ff440000" + android:pathData="M 130,225 L 130,115 L 130,115 L 70,15 L 10,115 L 10,115 L 10,225 z" + android:pivotX="70" + android:pivotY="120" + android:rotation="180" + android:stroke="#FF00FF00" + android:strokeWidth="10" + android:trimPathEnd=".9" + android:trimPathStart=".1" /> + +</vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml index 179268357815..451b28e30d3c 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml @@ -1,4 +1,5 @@ -<!-- Copyright (C) 2014 The Android Open Source Project +<!-- + 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. @@ -15,51 +16,47 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" > <size - android:width="64dp" - android:height="64dp"/> + android:height="64dp" + android:width="64dp" /> <viewport - android:viewportWidth="7.30625" - android:viewportHeight="12.25"/> + android:viewportHeight="12.25" + android:viewportWidth="7.30625" /> - <group> - - <path - android:name="clip1" - android:pathData=" + <path + android:name="clip1" + android:clipToPath="true" + android:pathData=" M 0, 0 l 7.3, 0 l 0, 0 l -7.3, 0 z" - android:clipToPath="true" - android:rotation="-30" - android:pivotX="3.65" - android:pivotY="6.125" - /> - <path - android:name="one" - android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125 + android:pivotX="3.65" + android:pivotY="6.125" + android:rotation="-30" /> + <path + android:name="one" + android:fill="#ff88ff" + android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125 l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0 - l -5.046875,0.0 0.0,-1.0Z" - android:fill="#ff88ff" - /> - <path - android:name="clip2" - android:pathData=" + l -5.046875,0.0 0.0,-1.0Z" /> + <path + android:name="clip2" + android:clipToPath="true" + android:pathData=" M 0, 0 l 7.3, 0 l 0, 12.25 l -7.3, 0 z" - android:clipToPath="true" - android:rotation="-30" - android:pivotX="3.65" - android:pivotY="6.125" - /> - <path - android:name="two" - android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375 + android:pivotX="3.65" + android:pivotY="6.125" + android:rotation="-30" /> + <path + android:name="two" + android:fill="#ff88ff" + android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375 q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625 q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625 q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875 @@ -67,8 +64,6 @@ q 0.625,-0.15625 1.140625,-0.15625 1.3593752,0.0 2.1718752,0.6875 q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625 q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375 - q -0.78125024,0.8125 -2.2187502,2.265625Z" - android:fill="#ff88ff" - /> - </group> -</vector> + q -0.78125024,0.8125 -2.2187502,2.265625Z" /> + +</vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml index 90694fbbc123..6f9caa828d8a 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml @@ -1,4 +1,5 @@ -<!-- Copyright (C) 2014 The Android Open Source Project +<!-- + 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. @@ -12,48 +13,44 @@ See the License for the specific language governing permissions and limitations under the License. --> -<vector xmlns:android="http://schemas.android.com/apk/res/android"> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > <size - android:width="64dp" - android:height="64dp"/> + android:height="64dp" + android:width="64dp" /> <viewport - android:viewportWidth="7.30625" - android:viewportHeight="12.25"/> + android:viewportHeight="12.25" + android:viewportWidth="7.30625" /> - <group> - <path - android:name="clip1" - android:pathData=" + <path + android:name="clip1" + android:clipToPath="true" + android:fill="#112233" + android:pathData=" M 3.65, 6.125 m -.001, 0 a .001,.001 0 1,0 .002,0 - a .001,.001 0 1,0 -.002,0z" - android:clipToPath="true" - android:fill="#112233" - /> - - <path - android:name="one" - android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125 + a .001,.001 0 1,0 -.002,0z" /> + <path + android:name="one" + android:fill="#ff88ff" + android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125 l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0 - l -5.046875,0.0 0.0,-1.0Z" - android:fill="#ff88ff" - /> - <path - android:name="clip2" - android:pathData=" + l -5.046875,0.0 0.0,-1.0Z" /> + <path + android:name="clip2" + android:clipToPath="true" + android:fill="#112233" + android:pathData=" M 3.65, 6.125 m -6, 0 a 6,6 0 1,0 12,0 - a 6,6 0 1,0 -12,0z" - android:clipToPath="true" - android:fill="#112233" - /> - <path - android:name="two" - android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375 + a 6,6 0 1,0 -12,0z" /> + <path + android:name="two" + android:fill="#ff88ff" + android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375 q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625 q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625 q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875 @@ -61,8 +58,6 @@ q 0.625,-0.15625 1.140625,-0.15625 1.3593752,0.0 2.1718752,0.6875 q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625 q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375 - q -0.78125024,0.8125 -2.2187502,2.265625Z" - android:fill="#ff88ff" - /> - </group> -</vector> + q -0.78125024,0.8125 -2.2187502,2.265625Z" /> + +</vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml index c6595facbe66..e6c25574a981 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml @@ -23,18 +23,17 @@ android:viewportHeight="12.25" android:viewportWidth="7.30625" /> - <group> - <path - android:name="one" - android:fill="#ffff00" - android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125 + <path + android:name="one" + android:fill="#ffff00" + android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125 l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0 l -5.046875,0.0 0.0,-1.0Z" /> - <path - android:name="two" - android:fill="#ffff00" - android:fillOpacity="0" - android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375 + <path + android:name="two" + android:fill="#ffff00" + android:fillOpacity="0" + android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375 q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625 q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625 q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875 @@ -43,5 +42,5 @@ q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625 q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375 q -0.78125024,0.8125 -2.2187502,2.265625Z" /> - </group> + </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml index 850de2896500..3f8cc09ec722 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml @@ -1,4 +1,5 @@ -<!-- Copyright (C) 2014 The Android Open Source Project +<!-- + 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. @@ -15,34 +16,38 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" > <size - android:width="64dp" - android:height="64dp"/> + android:height="64dp" + android:width="64dp" /> <viewport - android:viewportWidth="700" - android:viewportHeight="700"/> + android:viewportHeight="700" + android:viewportWidth="700" /> - <group> - <path android:pathData="M 569.374 461.472L 569.374 160.658L 160.658 160.658L 160.658 461.472L 569.374 461.472z" - android:name="path2451" - android:stroke="#FF000000" - android:strokeWidth="30.65500000000000"/> - <path android:pathData="M 365.015 311.066" - android:name="path2453" - android:stroke="#FF000000" - android:strokeWidth="30.655000000000001"/> - <path android:pathData="M 164.46 164.49L 340.78 343.158C 353.849 356.328 377.63 356.172 390.423 343.278L 566.622 165.928" - android:name="path2455" - android:stroke="#FF000000" - android:fill="#FFFFFFFF" - android:strokeWidth="30.655000000000001"/> - <path android:pathData="M 170.515 451.566L 305.61 313.46" - android:name="path2457" - android:stroke="#000000" - android:strokeWidth="30.655000000000001"/> - <path android:pathData="M 557.968 449.974L 426.515 315.375" - android:name="path2459" - android:stroke="#000000" - android:strokeWidth="30.655000000000001"/> - </group> -</vector> + <path + android:name="path2451" + android:pathData="M 569.374 461.472L 569.374 160.658L 160.658 160.658L 160.658 461.472L 569.374 461.472z" + android:stroke="#FF000000" + android:strokeWidth="30.65500000000000" /> + <path + android:name="path2453" + android:pathData="M 365.015 311.066" + android:stroke="#FF000000" + android:strokeWidth="30.655000000000001" /> + <path + android:name="path2455" + android:fill="#FFFFFFFF" + android:pathData="M 164.46 164.49L 340.78 343.158C 353.849 356.328 377.63 356.172 390.423 343.278L 566.622 165.928" + android:stroke="#FF000000" + android:strokeWidth="30.655000000000001" /> + <path + android:name="path2457" + android:pathData="M 170.515 451.566L 305.61 313.46" + android:stroke="#000000" + android:strokeWidth="30.655000000000001" /> + <path + android:name="path2459" + android:pathData="M 557.968 449.974L 426.515 315.375" + android:stroke="#000000" + android:strokeWidth="30.655000000000001" /> + +</vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml index 7c7e679a3d1e..4db5090dcf2b 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml @@ -1,4 +1,5 @@ -<!-- Copyright (C) 2014 The Android Open Source Project +<!-- + 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. @@ -12,21 +13,21 @@ See the License for the specific language governing permissions and limitations under the License. --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" > +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size - android:width="64dp" - android:height="64dp"/> + android:height="64dp" + android:width="64dp" /> - <viewport android:viewportWidth="140" - android:viewportHeight="110"/> + <viewport + android:viewportHeight="110" + android:viewportWidth="140" /> - <group> - <path - android:name="back" - android:pathData="M 20,55 l 35.3,-35.3 7.07,7.07 -35.3,35.3 z + <path + android:name="back" + android:fill="#ffffffff" + android:pathData="M 20,55 l 35.3,-35.3 7.07,7.07 -35.3,35.3 z M 27,50 l 97,0 0,10 -97,0 z - M 20,55 l 7.07,-7.07 35.3,35.3 -7.07,7.07 z" - android:fill="#ffffffff" - /> - </group> -</vector> + M 20,55 l 7.07,-7.07 35.3,35.3 -7.07,7.07 z" /> + +</vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml index 59f745942dc7..44ef9796bc0f 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml @@ -1,4 +1,5 @@ -<!-- Copyright (C) 2014 The Android Open Source Project +<!-- + 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. @@ -15,20 +16,18 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" > <size - android:width="64dp" - android:height="64dp"/> + android:height="64dp" + android:width="64dp" /> + <viewport + android:viewportHeight="600" + android:viewportWidth="600" /> - <viewport android:viewportWidth="600" - android:viewportHeight="600"/> + <path + android:name="pie1" + android:fill="#ffffcc00" + android:pathData="M535.441,412.339A280.868,280.868 0 1,1 536.186,161.733L284.493,286.29Z" + android:stroke="#FF00FF00" + android:strokeWidth="1" /> - <group> - <path - android:name="pie1" - android:pathData="M535.441,412.339A280.868,280.868 0 1,1 536.186,161.733L284.493,286.29Z" - android:fill="#ffffcc00" - android:stroke="#FF00FF00" - android:strokeWidth="1"/> - </group> - -</vector> +</vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml index 2e379d675b09..248a14361ac2 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml @@ -23,14 +23,12 @@ android:viewportHeight="200" android:viewportWidth="200" /> - <group> - <path - android:name="house" - android:fill="#ffffffff" - android:pathData="M 100,20 l 0,0 0,140 -80,0 z M 100,20 l 0,0 80,140 -80,0 z" - android:pivotX="100" - android:pivotY="100" - android:rotation="90" /> - </group> + <path + android:name="house" + android:fill="#ffffffff" + android:pathData="M 100,20 l 0,0 0,140 -80,0 z M 100,20 l 0,0 80,140 -80,0 z" + android:pivotX="100" + android:pivotY="100" + android:rotation="90" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml index 8484e9e2f107..56c29726eee0 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml @@ -21,26 +21,24 @@ android:width="64dp" /> <viewport - android:viewportWidth="200" - android:viewportHeight="200"/> + android:viewportHeight="200" + android:viewportWidth="200" /> - <group> - <path - android:name="bar3" - android:fill="#FFFFFFFF" - android:pathData="M49.001,60c-5.466,0 -9.899,4.478 -9.899,10s4.434,10,9.899,10c5.468,0,9.899 -4.478,9.899 -10S54.469,60,49.001,60z" /> - <path - android:name="bar2" - android:fill="#FFFFFFFF" - android:pathData="M28.001,48.787l7,7.07c7.731 -7.811,20.269 -7.81,28.001,0l6.999 -7.07C58.403,37.071,39.599,37.071,28.001,48.787z" /> - <path - android:name="bar1" - android:fill="#FF555555" - android:pathData="M14.001,34.645 L21,41.716c15.464 -15.621,40.536 -15.621,56,0l7.001 -7.071C64.672,15.119,33.33,15.119,14.001,34.645z" /> - <path - android:name="bar0" - android:fill="#FF555555" - android:pathData="M0,20.502l6.999,7.071 c23.196 -23.431,60.806 -23.431,84.002,0L98,20.503C70.938 -6.834,27.063 -6.834,0,20.502z" /> - </group> + <path + android:name="bar3" + android:fill="#FFFFFFFF" + android:pathData="M49.001,60c-5.466,0 -9.899,4.478 -9.899,10s4.434,10,9.899,10c5.468,0,9.899 -4.478,9.899 -10S54.469,60,49.001,60z" /> + <path + android:name="bar2" + android:fill="#FFFFFFFF" + android:pathData="M28.001,48.787l7,7.07c7.731 -7.811,20.269 -7.81,28.001,0l6.999 -7.07C58.403,37.071,39.599,37.071,28.001,48.787z" /> + <path + android:name="bar1" + android:fill="#FF555555" + android:pathData="M14.001,34.645 L21,41.716c15.464 -15.621,40.536 -15.621,56,0l7.001 -7.071C64.672,15.119,33.33,15.119,14.001,34.645z" /> + <path + android:name="bar0" + android:fill="#FF555555" + android:pathData="M0,20.502l6.999,7.071 c23.196 -23.431,60.806 -23.431,84.002,0L98,20.503C70.938 -6.834,27.063 -6.834,0,20.502z" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml index 2b6c5d31b32a..16d8b4824710 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml @@ -23,18 +23,16 @@ android:viewportHeight="80" android:viewportWidth="40" /> - <group> - <path - android:name="battery" - android:fill="#3388ff" - android:pathData="M 20.28125,2.0000002 C 17.352748,2.0000002 15,4.3527485 15,7.2812502 L 15,8.0000002 L 13.15625,8.0000002 C 9.7507553,8.0000002 7,10.750759 7,14.15625 L 7,39.84375 C 7,43.24924 9.7507558,46 13.15625,46 L 33.84375,46 C 37.249245,46 39.999999,43.24924 40,39.84375 L 40,14.15625 C 40,10.75076 37.249243,8.0000002 33.84375,8.0000002 L 32,8.0000002 L 32,7.2812502 C 32,4.3527485 29.647252,2.0000002 26.71875,2.0000002 L 20.28125,2.0000002 z" - android:rotation="0" - android:stroke="#ff8833" - android:strokeWidth="1" /> - <path - android:name="spark" - android:fill="#FFFF0000" - android:pathData="M 30,18.031528 L 25.579581,23.421071 L 29.370621,26.765348 L 20.096792,37 L 21.156922,28.014053 L 17,24.902844 L 20.880632,18 L 30,18.031528 z" /> - </group> + <path + android:name="battery" + android:fill="#3388ff" + android:pathData="M 20.28125,2.0000002 C 17.352748,2.0000002 15,4.3527485 15,7.2812502 L 15,8.0000002 L 13.15625,8.0000002 C 9.7507553,8.0000002 7,10.750759 7,14.15625 L 7,39.84375 C 7,43.24924 9.7507558,46 13.15625,46 L 33.84375,46 C 37.249245,46 39.999999,43.24924 40,39.84375 L 40,14.15625 C 40,10.75076 37.249243,8.0000002 33.84375,8.0000002 L 32,8.0000002 L 32,7.2812502 C 32,4.3527485 29.647252,2.0000002 26.71875,2.0000002 L 20.28125,2.0000002 z" + android:rotation="0" + android:stroke="#ff8833" + android:strokeWidth="1" /> + <path + android:name="spark" + android:fill="#FFFF0000" + android:pathData="M 30,18.031528 L 25.579581,23.421071 L 29.370621,26.765348 L 20.096792,37 L 21.156922,28.014053 L 17,24.902844 L 20.880632,18 L 30,18.031528 z" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml index 681eb4faf099..0a0407ddb63d 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml @@ -23,22 +23,20 @@ android:viewportHeight="600" android:viewportWidth="600" /> - <group> - <path - android:name="pie1" - android:pathData="M300,70 a230,230 0 1,0 1,0 z" - android:stroke="#FF00FF00" - android:strokeWidth="70" - android:trimPathEnd=".75" - android:trimPathOffset="0" - android:trimPathStart="0" /> - <path - android:name="v" - android:fill="#FF00FF00" - android:pathData="M300,70 l 0,-70 70,70 -70,70z" - android:pivotX="300" - android:pivotY="300" - android:rotation="0" /> - </group> + <path + android:name="pie1" + android:pathData="M300,70 a230,230 0 1,0 1,0 z" + android:stroke="#FF00FF00" + android:strokeWidth="70" + android:trimPathEnd=".75" + android:trimPathOffset="0" + android:trimPathStart="0" /> + <path + android:name="v" + android:fill="#FF00FF00" + android:pathData="M300,70 l 0,-70 70,70 -70,70z" + android:pivotX="300" + android:pivotY="300" + android:rotation="0" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml index ef1b8e4cba33..385b1e9d4236 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml @@ -23,22 +23,20 @@ android:viewportHeight="400" android:viewportWidth="600" /> - <group> - <path - android:name="pie1" - android:fill="#ffffffff" - android:pathData="M300,200 h-150 a150,150 0 1,0 150,-150 z" - android:stroke="#FF00FF00" - android:strokeWidth="1" /> - <path - android:name="half" - android:fill="#FFFF0000" - android:pathData="M275,175 v-150 a150,150 0 0,0 -150,150 z" - android:pivotX="300" - android:pivotY="200" - android:rotation="0" - android:stroke="#FF0000FF" - android:strokeWidth="5" /> - </group> + <path + android:name="pie1" + android:fill="#ffffffff" + android:pathData="M300,200 h-150 a150,150 0 1,0 150,-150 z" + android:stroke="#FF00FF00" + android:strokeWidth="1" /> + <path + android:name="half" + android:fill="#FFFF0000" + android:pathData="M275,175 v-150 a150,150 0 0,0 -150,150 z" + android:pivotX="300" + android:pivotY="200" + android:rotation="0" + android:stroke="#FF0000FF" + android:strokeWidth="5" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml index 77bf7232c9af..b701b358a0f7 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml @@ -23,19 +23,17 @@ android:viewportHeight="500" android:viewportWidth="800" /> - <group> - <path - android:name="pie2" - android:pathData="M200,350 l 50,-25 + <path + android:name="pie2" + android:pathData="M200,350 l 50,-25 a25,12 -30 0,1 100,-50 l 50,-25 a25,25 -30 0,1 100,-50 l 50,-25 a25,37 -30 0,1 100,-50 l 50,-25 a25,50 -30 0,1 100,-50 l 50,-25" - android:pivotX="90" - android:pivotY="100" - android:rotation="20" - android:stroke="#FF00FF00" - android:strokeWidth="10" /> - </group> + android:pivotX="90" + android:pivotY="100" + android:rotation="20" + android:stroke="#FF00FF00" + android:strokeWidth="10" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml index df5838c53e01..8d773e1ce699 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml @@ -23,16 +23,14 @@ android:viewportHeight="400" android:viewportWidth="500" /> - <group> - <path - android:name="house" - android:fill="#ff440000" - android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200" - android:pivotX="250" - android:pivotY="200" - android:rotation="180" - android:stroke="#FFFF0000" - android:strokeWidth="10" /> - </group> + <path + android:name="house" + android:fill="#ff440000" + android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200" + android:pivotX="250" + android:pivotY="200" + android:rotation="180" + android:stroke="#FFFF0000" + android:strokeWidth="10" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml index 0bdcda564603..3b7926cadce2 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml @@ -23,15 +23,13 @@ android:viewportHeight="200" android:viewportWidth="200" /> - <group> - <path - android:name="house" - android:pathData="M 100,10 v 90 M 10,100 h 90" - android:pivotX="100" - android:pivotY="100" - android:rotation="360" - android:stroke="#FF00FF00" - android:strokeWidth="10" /> - </group> + <path + android:name="house" + android:pathData="M 100,10 v 90 M 10,100 h 90" + android:pivotX="100" + android:pivotY="100" + android:rotation="360" + android:stroke="#FF00FF00" + android:strokeWidth="10" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml index 4453ae4b3569..1ec72be1229a 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml @@ -1,4 +1,5 @@ -<!-- Copyright (C) 2014 The Android Open Source Project +<!-- + 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. @@ -15,21 +16,20 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" > <size - android:width="64dp" - android:height="64dp"/> + android:height="64dp" + android:width="64dp" /> - <viewport android:viewportWidth="1200" - android:viewportHeight="600"/> + <viewport + android:viewportHeight="600" + android:viewportWidth="1200" /> - <group> - <path - android:name="house" - android:pathData="M200,300 Q400,50 600,300 T1000,300" - android:stroke="#FFFF0000" - android:strokeWidth="10" - android:rotation="360" - android:pivotX="600" - android:pivotY="300"/> - </group> + <path + android:name="house" + android:pathData="M200,300 Q400,50 600,300 T1000,300" + android:pivotX="600" + android:pivotY="300" + android:rotation="360" + android:stroke="#FFFF0000" + android:strokeWidth="10" /> -</vector> +</vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml index dfae9acb122d..12d0e93f3bb2 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml @@ -23,15 +23,13 @@ android:viewportHeight="400" android:viewportWidth="500" /> - <group> - <path - android:name="house" - android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200" - android:pivotX="250" - android:pivotY="200" - android:rotation="360" - android:stroke="#FFFFFF00" - android:strokeWidth="10" /> - </group> + <path + android:name="house" + android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200" + android:pivotX="250" + android:pivotY="200" + android:rotation="360" + android:stroke="#FFFFFF00" + android:strokeWidth="10" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml index a890fd6a8874..017e04c0f540 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml @@ -23,14 +23,12 @@ android:viewportHeight="800" android:viewportWidth="1000" /> - <group> - <path - android:name="house" - android:pathData="M10,300 Q400,550 600,300 T1000,300" - android:pivotX="90" - android:pivotY="100" - android:stroke="#FFFF0000" - android:strokeWidth="60" /> - </group> + <path + android:name="house" + android:pathData="M10,300 Q400,550 600,300 T1000,300" + android:pivotX="90" + android:pivotY="100" + android:stroke="#FFFF0000" + android:strokeWidth="60" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml index b8af7e2d076c..b7002a392310 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml @@ -23,16 +23,14 @@ android:viewportHeight="480" android:viewportWidth="480" /> - <group> - <path - android:name="edit" - android:fill="#FF00FFFF" - android:pathData="M406.667,180c0,0 -100 -100 -113.334 -113.333 + <path + android:name="edit" + android:fill="#FF00FFFF" + android:pathData="M406.667,180c0,0 -100 -100 -113.334 -113.333 c-13.333 -13.334 -33.333,0 -33.333,0l-160,160c0,0 -40,153.333 -40,173.333c0,13.333,13.333,13.333,13.333,13.333l173.334 -40 c0,0,146.666 -146.666,160 -160C420,200,406.667,180,406.667,180z M226.399,356.823L131.95,378.62l-38.516 -38.522 c7.848 -34.675,20.152 -82.52,23.538 -95.593l3.027,2.162l106.667,106.666L226.399,356.823z" - android:stroke="#FF000000" - android:strokeWidth="10" /> - </group> + android:stroke="#FF000000" + android:strokeWidth="10" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml index 22ce795564fc..cda213d94a33 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml @@ -23,10 +23,8 @@ limitations under the License. android:viewportHeight="24" android:viewportWidth="24" /> - <group> - <path - android:fill="#FF000000" - android:pathData="M3.0,17.25L3.0,21.0l3.75,0.0L17.813995,9.936001l-3.75,-3.75L3.0,17.25zM20.707,7.0429993c0.391,-0.391 0.391,-1.023 0.0,-1.414l-2.336,-2.336c-0.391,-0.391 -1.023,-0.391 -1.414,0.0l-1.832,1.832l3.75,3.75L20.707,7.0429993z" /> - </group> + <path + android:fill="#FF000000" + android:pathData="M3.0,17.25L3.0,21.0l3.75,0.0L17.813995,9.936001l-3.75,-3.75L3.0,17.25zM20.707,7.0429993c0.391,-0.391 0.391,-1.023 0.0,-1.414l-2.336,-2.336c-0.391,-0.391 -1.023,-0.391 -1.414,0.0l-1.832,1.832l3.75,3.75L20.707,7.0429993z" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml index 042173ca9f9c..2cb638165a76 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml @@ -23,10 +23,8 @@ limitations under the License. android:viewportHeight="24" android:viewportWidth="24" /> - <group> - <path - android:fill="#FF000000" - android:pathData="M6.0,19.0c0.0,1.104 0.896,2.0 2.0,2.0l8.0,0.0c1.104,0.0 2.0,-0.896 2.0,-2.0l0.0,-12.0L6.0,7.0L6.0,19.0zM18.0,4.0l-2.5,0.0l-1.0,-1.0l-5.0,0.0l-1.0,1.0L6.0,4.0C5.4469986,4.0 5.0,4.4469986 5.0,5.0l0.0,1.0l14.0,0.0l0.0,-1.0C19.0,4.4469986 18.552002,4.0 18.0,4.0z" /> - </group> + <path + android:fill="#FF000000" + android:pathData="M6.0,19.0c0.0,1.104 0.896,2.0 2.0,2.0l8.0,0.0c1.104,0.0 2.0,-0.896 2.0,-2.0l0.0,-12.0L6.0,7.0L6.0,19.0zM18.0,4.0l-2.5,0.0l-1.0,-1.0l-5.0,0.0l-1.0,1.0L6.0,4.0C5.4469986,4.0 5.0,4.4469986 5.0,5.0l0.0,1.0l14.0,0.0l0.0,-1.0C19.0,4.4469986 18.552002,4.0 18.0,4.0z" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml index 6b6f43de122b..d58942e8b9e7 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml @@ -23,10 +23,8 @@ limitations under the License. android:viewportHeight="24" android:viewportWidth="24" /> - <group> - <path - android:fill="#FF000000" - android:pathData="M16.0,5.0c-1.955,0.0 -3.83,1.268 -4.5,3.0c-0.67,-1.732 -2.547,-3.0 -4.5,-3.0C4.4570007,5.0 2.5,6.931999 2.5,9.5c0.0,3.529 3.793,6.258 9.0,11.5c5.207,-5.242 9.0,-7.971 9.0,-11.5C20.5,6.931999 18.543,5.0 16.0,5.0z" /> - </group> + <path + android:fill="#FF000000" + android:pathData="M16.0,5.0c-1.955,0.0 -3.83,1.268 -4.5,3.0c-0.67,-1.732 -2.547,-3.0 -4.5,-3.0C4.4570007,5.0 2.5,6.931999 2.5,9.5c0.0,3.529 3.793,6.258 9.0,11.5c5.207,-5.242 9.0,-7.971 9.0,-11.5C20.5,6.931999 18.543,5.0 16.0,5.0z" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml index ba8ebcad170c..4717be481814 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml @@ -23,13 +23,11 @@ limitations under the License. android:viewportHeight="24" android:viewportWidth="24" /> - <group> - <path - android:fillOpacity="0.9" - android:pathData="M11.994999,2.0C6.4679985,2.0 2.0,6.4780006 2.0,12.0s4.468,10.0 9.995,10.0S22.0,17.522 22.0,12.0S17.521,2.0 11.994999,2.0zM12.0,20.0c-4.42,0.0 -8.0,-3.582 -8.0,-8.0s3.58,-8.0 8.0,-8.0s8.0,3.582 8.0,8.0S16.419998,20.0 12.0,20.0z" /> - <path - android:fillOpacity="0.9" - android:pathData="M12.5,6.0l-1.5,0.0 0.0,7.0 5.3029995,3.1819992 0.75,-1.249999 -4.5529995,-2.7320004z" /> - </group> + <path + android:fillOpacity="0.9" + android:pathData="M11.994999,2.0C6.4679985,2.0 2.0,6.4780006 2.0,12.0s4.468,10.0 9.995,10.0S22.0,17.522 22.0,12.0S17.521,2.0 11.994999,2.0zM12.0,20.0c-4.42,0.0 -8.0,-3.582 -8.0,-8.0s3.58,-8.0 8.0,-8.0s8.0,3.582 8.0,8.0S16.419998,20.0 12.0,20.0z" /> + <path + android:fillOpacity="0.9" + android:pathData="M12.5,6.0l-1.5,0.0 0.0,7.0 5.3029995,3.1819992 0.75,-1.249999 -4.5529995,-2.7320004z" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml index 896a9387afe6..c626325145e1 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml @@ -23,10 +23,8 @@ limitations under the License. android:viewportHeight="24" android:viewportWidth="24" /> - <group> - <path - android:fill="#FF000000" - android:pathData="M19.429,12.975998c0.042,-0.32 0.07,-0.645 0.07,-0.976s-0.029,-0.655 -0.07,-0.976l2.113,-1.654c0.188,-0.151 0.243,-0.422 0.118,-0.639l-2.0,-3.463c-0.125,-0.217 -0.386,-0.304 -0.612,-0.218l-2.49,1.004c-0.516,-0.396 -1.081,-0.731 -1.69,-0.984l-0.375,-2.648C14.456,2.1829987 14.25,2.0 14.0,2.0l-4.0,0.0C9.75,2.0 9.544,2.1829987 9.506,2.422001L9.131,5.0699997C8.521,5.322998 7.957,5.6570015 7.44,6.054001L4.952,5.0509987C4.726,4.965 4.464,5.052002 4.34,5.269001l-2.0,3.463C2.2150002,8.947998 2.27,9.219002 2.4580002,9.369999l2.112,1.653C4.528,11.344002 4.5,11.668999 4.5,12.0s0.029,0.656 0.071,0.977L2.4580002,14.630001c-0.188,0.151 -0.243,0.422 -0.118,0.639l2.0,3.463c0.125,0.217 0.386,0.304 0.612,0.218l2.489,-1.004c0.516,0.396 1.081,0.731 1.69,0.984l0.375,2.648C9.544,21.817001 9.75,22.0 10.0,22.0l4.0,0.0c0.25,0.0 0.456,-0.183 0.494,-0.422l0.375,-2.648c0.609,-0.253 1.174,-0.588 1.689,-0.984l2.49,1.004c0.226,0.086 0.487,-0.001 0.612,-0.218l2.0,-3.463c0.125,-0.217 0.07,-0.487 -0.118,-0.639L19.429,12.975998zM12.0,16.0c-2.21,0.0 -4.0,-1.791 -4.0,-4.0c0.0,-2.21 1.79,-4.0 4.0,-4.0c2.208,0.0 4.0,1.79 4.0,4.0C16.0,14.209 14.208,16.0 12.0,16.0z" /> - </group> + <path + android:fill="#FF000000" + android:pathData="M19.429,12.975998c0.042,-0.32 0.07,-0.645 0.07,-0.976s-0.029,-0.655 -0.07,-0.976l2.113,-1.654c0.188,-0.151 0.243,-0.422 0.118,-0.639l-2.0,-3.463c-0.125,-0.217 -0.386,-0.304 -0.612,-0.218l-2.49,1.004c-0.516,-0.396 -1.081,-0.731 -1.69,-0.984l-0.375,-2.648C14.456,2.1829987 14.25,2.0 14.0,2.0l-4.0,0.0C9.75,2.0 9.544,2.1829987 9.506,2.422001L9.131,5.0699997C8.521,5.322998 7.957,5.6570015 7.44,6.054001L4.952,5.0509987C4.726,4.965 4.464,5.052002 4.34,5.269001l-2.0,3.463C2.2150002,8.947998 2.27,9.219002 2.4580002,9.369999l2.112,1.653C4.528,11.344002 4.5,11.668999 4.5,12.0s0.029,0.656 0.071,0.977L2.4580002,14.630001c-0.188,0.151 -0.243,0.422 -0.118,0.639l2.0,3.463c0.125,0.217 0.386,0.304 0.612,0.218l2.489,-1.004c0.516,0.396 1.081,0.731 1.69,0.984l0.375,2.648C9.544,21.817001 9.75,22.0 10.0,22.0l4.0,0.0c0.25,0.0 0.456,-0.183 0.494,-0.422l0.375,-2.648c0.609,-0.253 1.174,-0.588 1.689,-0.984l2.49,1.004c0.226,0.086 0.487,-0.001 0.612,-0.218l2.0,-3.463c0.125,-0.217 0.07,-0.487 -0.118,-0.639L19.429,12.975998zM12.0,16.0c-2.21,0.0 -4.0,-1.791 -4.0,-4.0c0.0,-2.21 1.79,-4.0 4.0,-4.0c2.208,0.0 4.0,1.79 4.0,4.0C16.0,14.209 14.208,16.0 12.0,16.0z" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_test01.xml b/tests/VectorDrawableTest/res/drawable/vector_test01.xml index a9091aba3827..bad5a46b5678 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_test01.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_test01.xml @@ -23,12 +23,10 @@ limitations under the License. android:viewportHeight="512" android:viewportWidth="512" /> - <group> - <path - android:name="002b" - android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0t-200,299" - android:stroke="#FF0000FF" - android:strokeWidth="4" /> - </group> + <path + android:name="002b" + android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0t-200,299" + android:stroke="#FF0000FF" + android:strokeWidth="4" /> </vector>
\ No newline at end of file diff --git a/tests/VectorDrawableTest/res/drawable/vector_test02.xml b/tests/VectorDrawableTest/res/drawable/vector_test02.xml index ab58c0674eab..c92b6f40f841 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_test02.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_test02.xml @@ -23,12 +23,10 @@ limitations under the License. android:viewportHeight="512" android:viewportWidth="512" /> - <group> - <path - android:name="002b" - android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0T-200,299" - android:stroke="#FF0000FF" - android:strokeWidth="4" /> - </group> + <path + android:name="002b" + android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0T-200,299" + android:stroke="#FF0000FF" + android:strokeWidth="4" /> </vector>
\ No newline at end of file diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp index e0dab785bc47..12d53898a2d0 100644 --- a/tools/aapt/AaptAssets.cpp +++ b/tools/aapt/AaptAssets.cpp @@ -3,8 +3,10 @@ // #include "AaptAssets.h" -#include "ResourceFilter.h" +#include "AaptConfig.h" +#include "AaptUtil.h" #include "Main.h" +#include "ResourceFilter.h" #include <utils/misc.h> #include <utils/SortedVector.h> @@ -14,7 +16,6 @@ #include <errno.h> static const char* kDefaultLocale = "default"; -static const char* kWildcardName = "any"; static const char* kAssetDir = "assets"; static const char* kResourceDir = "res"; static const char* kValuesDir = "values"; @@ -149,24 +150,6 @@ static bool isHidden(const char *root, const char *path) // ========================================================================= // ========================================================================= -/* static */ void AaptLocaleValue::splitAndLowerCase(const char* const chars, - Vector<String8>* parts, const char separator) { - const char *p = chars; - const char *q; - while (NULL != (q = strchr(p, separator))) { - String8 val(p, q - p); - val.toLower(); - parts->add(val); - p = q+1; - } - - if (p < chars + strlen(chars)) { - String8 val(p); - val.toLower(); - parts->add(val); - } -} - /* static */ inline bool isAlpha(const String8& string) { const size_t length = string.length(); @@ -230,8 +213,7 @@ void AaptLocaleValue::setVariant(const char* variantChars) { bool AaptLocaleValue::initFromFilterString(const String8& str) { // A locale (as specified in the filter) is an underscore separated name such // as "en_US", "en_Latn_US", or "en_US_POSIX". - Vector<String8> parts; - splitAndLowerCase(str.string(), &parts, '_'); + Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '_'); const int numTags = parts.size(); bool valid = false; @@ -301,8 +283,7 @@ int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int sta if (part[0] == 'b' && part[1] == '+') { // This is a "modified" BCP-47 language tag. Same semantics as BCP-47 tags, // except that the separator is "+" and not "-". - Vector<String8> subtags; - AaptLocaleValue::splitAndLowerCase(part.string(), &subtags, '+'); + Vector<String8> subtags = AaptUtil::splitAndLowerCase(part, '+'); subtags.removeItemsAt(0); if (subtags.size() == 1) { setLanguage(subtags[0]); @@ -438,1349 +419,46 @@ void AaptLocaleValue::writeTo(ResTable_config* out) const { } } - -/* static */ bool -AaptGroupEntry::parseFilterNamePart(const String8& part, int* axis, AxisValue* value) -{ - ResTable_config config; - memset(&config, 0, sizeof(ResTable_config)); - - // IMSI - MCC - if (getMccName(part.string(), &config)) { - *axis = AXIS_MCC; - value->intValue = config.mcc; - return true; - } - - // IMSI - MNC - if (getMncName(part.string(), &config)) { - *axis = AXIS_MNC; - value->intValue = config.mnc; - return true; - } - - // locale - language - if (value->localeValue.initFromFilterString(part)) { - *axis = AXIS_LOCALE; - return true; - } - - // layout direction - if (getLayoutDirectionName(part.string(), &config)) { - *axis = AXIS_LAYOUTDIR; - value->intValue = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR); - return true; - } - - // smallest screen dp width - if (getSmallestScreenWidthDpName(part.string(), &config)) { - *axis = AXIS_SMALLESTSCREENWIDTHDP; - value->intValue = config.smallestScreenWidthDp; - return true; - } - - // screen dp width - if (getScreenWidthDpName(part.string(), &config)) { - *axis = AXIS_SCREENWIDTHDP; - value->intValue = config.screenWidthDp; - return true; - } - - // screen dp height - if (getScreenHeightDpName(part.string(), &config)) { - *axis = AXIS_SCREENHEIGHTDP; - value->intValue = config.screenHeightDp; - return true; - } - - // screen layout size - if (getScreenLayoutSizeName(part.string(), &config)) { - *axis = AXIS_SCREENLAYOUTSIZE; - value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENSIZE); - return true; - } - - // screen layout long - if (getScreenLayoutLongName(part.string(), &config)) { - *axis = AXIS_SCREENLAYOUTLONG; - value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENLONG); - return true; - } - - // orientation - if (getOrientationName(part.string(), &config)) { - *axis = AXIS_ORIENTATION; - value->intValue = config.orientation; - return true; - } - - // ui mode type - if (getUiModeTypeName(part.string(), &config)) { - *axis = AXIS_UIMODETYPE; - value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE); - return true; - } - - // ui mode night - if (getUiModeNightName(part.string(), &config)) { - *axis = AXIS_UIMODENIGHT; - value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT); - return true; - } - - // density - if (getDensityName(part.string(), &config)) { - *axis = AXIS_DENSITY; - value->intValue = config.density; - return true; - } - - // touchscreen - if (getTouchscreenName(part.string(), &config)) { - *axis = AXIS_TOUCHSCREEN; - value->intValue = config.touchscreen; - return true; - } - - // keyboard hidden - if (getKeysHiddenName(part.string(), &config)) { - *axis = AXIS_KEYSHIDDEN; - value->intValue = config.inputFlags; - return true; - } - - // keyboard - if (getKeyboardName(part.string(), &config)) { - *axis = AXIS_KEYBOARD; - value->intValue = config.keyboard; - return true; - } - - // navigation hidden - if (getNavHiddenName(part.string(), &config)) { - *axis = AXIS_NAVHIDDEN; - value->intValue = config.inputFlags; - return 0; - } - - // navigation - if (getNavigationName(part.string(), &config)) { - *axis = AXIS_NAVIGATION; - value->intValue = config.navigation; - return true; - } - - // screen size - if (getScreenSizeName(part.string(), &config)) { - *axis = AXIS_SCREENSIZE; - value->intValue = config.screenSize; - return true; - } - - // version - if (getVersionName(part.string(), &config)) { - *axis = AXIS_VERSION; - value->intValue = config.version; - return true; - } - - return false; -} - -AxisValue -AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis) -{ - AxisValue value; - switch (axis) { - case AXIS_MCC: - value.intValue = config.mcc; - break; - case AXIS_MNC: - value.intValue = config.mnc; - break; - case AXIS_LOCALE: - value.localeValue.initFromResTable(config); - break; - case AXIS_LAYOUTDIR: - value.intValue = config.screenLayout&ResTable_config::MASK_LAYOUTDIR; - break; - case AXIS_SCREENLAYOUTSIZE: - value.intValue = config.screenLayout&ResTable_config::MASK_SCREENSIZE; - break; - case AXIS_ORIENTATION: - value.intValue = config.orientation; - break; - case AXIS_UIMODETYPE: - value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE); - break; - case AXIS_UIMODENIGHT: - value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT); - break; - case AXIS_DENSITY: - value.intValue = config.density; - break; - case AXIS_TOUCHSCREEN: - value.intValue = config.touchscreen; - break; - case AXIS_KEYSHIDDEN: - value.intValue = config.inputFlags; - break; - case AXIS_KEYBOARD: - value.intValue = config.keyboard; - break; - case AXIS_NAVIGATION: - value.intValue = config.navigation; - break; - case AXIS_SCREENSIZE: - value.intValue = config.screenSize; - break; - case AXIS_SMALLESTSCREENWIDTHDP: - value.intValue = config.smallestScreenWidthDp; - break; - case AXIS_SCREENWIDTHDP: - value.intValue = config.screenWidthDp; - break; - case AXIS_SCREENHEIGHTDP: - value.intValue = config.screenHeightDp; - break; - case AXIS_VERSION: - value.intValue = config.version; - break; - } - - return value; -} - -bool -AaptGroupEntry::configSameExcept(const ResTable_config& config, - const ResTable_config& otherConfig, int axis) -{ - for (int i=AXIS_START; i<=AXIS_END; i++) { - if (i == axis) { - continue; - } - if (getConfigValueForAxis(config, i) != getConfigValueForAxis(otherConfig, i)) { - return false; - } - } - return true; -} - bool AaptGroupEntry::initFromDirName(const char* dir, String8* resType) { - mParamsChanged = true; - - Vector<String8> parts; - AaptLocaleValue::splitAndLowerCase(dir, &parts, '-'); - - String8 mcc, mnc, layoutsize, layoutlong, orient, den; - String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers; - String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp; - - AaptLocaleValue locale; - int numLocaleComponents = 0; - - const int N = parts.size(); - int index = 0; - String8 part = parts[index]; - - // resource type - if (!isValidResourceType(part)) { - return false; - } - *resType = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - - // imsi - mcc - if (getMccName(part.string())) { - mcc = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not mcc: %s\n", part.string()); - } - - // imsi - mnc - if (getMncName(part.string())) { - mnc = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; + const char* q = strchr(dir, '-'); + size_t typeLen; + if (q != NULL) { + typeLen = q - dir; } else { - //printf("not mnc: %s\n", part.string()); + typeLen = strlen(dir); } - index = locale.initFromDirName(parts, index); - if (index == -1) { + String8 type(dir, typeLen); + if (!isValidResourceType(type)) { return false; } - if (index >= N){ - goto success; - } - - part = parts[index]; - if (getLayoutDirectionName(part.string())) { - layoutDir = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not layout direction: %s\n", part.string()); - } - - if (getSmallestScreenWidthDpName(part.string())) { - smallestwidthdp = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not smallest screen width dp: %s\n", part.string()); - } - - if (getScreenWidthDpName(part.string())) { - widthdp = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not screen width dp: %s\n", part.string()); - } - - if (getScreenHeightDpName(part.string())) { - heightdp = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not screen height dp: %s\n", part.string()); - } - - if (getScreenLayoutSizeName(part.string())) { - layoutsize = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not screen layout size: %s\n", part.string()); - } - - if (getScreenLayoutLongName(part.string())) { - layoutlong = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not screen layout long: %s\n", part.string()); - } - - // orientation - if (getOrientationName(part.string())) { - orient = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not orientation: %s\n", part.string()); - } - - // ui mode type - if (getUiModeTypeName(part.string())) { - uiModeType = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not ui mode type: %s\n", part.string()); - } - - // ui mode night - if (getUiModeNightName(part.string())) { - uiModeNight = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not ui mode night: %s\n", part.string()); - } - - // density - if (getDensityName(part.string())) { - den = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not density: %s\n", part.string()); - } - - // touchscreen - if (getTouchscreenName(part.string())) { - touch = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not touchscreen: %s\n", part.string()); - } - - // keyboard hidden - if (getKeysHiddenName(part.string())) { - keysHidden = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not keysHidden: %s\n", part.string()); - } - // keyboard - if (getKeyboardName(part.string())) { - key = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not keyboard: %s\n", part.string()); - } - - // navigation hidden - if (getNavHiddenName(part.string())) { - navHidden = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not navHidden: %s\n", part.string()); - } - - if (getNavigationName(part.string())) { - nav = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not navigation: %s\n", part.string()); - } - - if (getScreenSizeName(part.string())) { - size = part; - - index++; - if (index == N) { - goto success; - } - part = parts[index]; - } else { - //printf("not screen size: %s\n", part.string()); - } - - if (getVersionName(part.string())) { - vers = part; - - index++; - if (index == N) { - goto success; + if (q != NULL) { + if (!AaptConfig::parse(String8(q + 1), &mParams)) { + return false; } - part = parts[index]; - } else { - //printf("not version: %s\n", part.string()); } - // if there are extra parts, it doesn't match - return false; - -success: - this->mcc = mcc; - this->mnc = mnc; - this->locale = locale; - this->screenLayoutSize = layoutsize; - this->screenLayoutLong = layoutlong; - this->smallestScreenWidthDp = smallestwidthdp; - this->screenWidthDp = widthdp; - this->screenHeightDp = heightdp; - this->orientation = orient; - this->uiModeType = uiModeType; - this->uiModeNight = uiModeNight; - this->density = den; - this->touchscreen = touch; - this->keysHidden = keysHidden; - this->keyboard = key; - this->navHidden = navHidden; - this->navigation = nav; - this->screenSize = size; - this->layoutDirection = layoutDir; - this->version = vers; - - // what is this anyway? - this->vendor = ""; - + *resType = type; return true; } String8 -AaptGroupEntry::toString() const -{ - String8 s = this->mcc; - s += ","; - s += this->mnc; - s += ","; - s += locale.toDirName(); - s += ","; - s += layoutDirection; - s += ","; - s += smallestScreenWidthDp; - s += ","; - s += screenWidthDp; - s += ","; - s += screenHeightDp; - s += ","; - s += screenLayoutSize; - s += ","; - s += screenLayoutLong; - s += ","; - s += this->orientation; - s += ","; - s += uiModeType; - s += ","; - s += uiModeNight; - s += ","; - s += density; - s += ","; - s += touchscreen; - s += ","; - s += keysHidden; - s += ","; - s += keyboard; - s += ","; - s += navHidden; - s += ","; - s += navigation; - s += ","; - s += screenSize; - s += ","; - s += version; - return s; -} - -String8 AaptGroupEntry::toDirName(const String8& resType) const { String8 s = resType; - if (this->mcc != "") { - if (s.length() > 0) { - s += "-"; - } - s += mcc; - } - if (this->mnc != "") { - if (s.length() > 0) { - s += "-"; - } - s += mnc; - } - - const String8 localeComponent = locale.toDirName(); - if (localeComponent != "") { - if (s.length() > 0) { - s += "-"; - } - s += localeComponent; - } - - if (this->layoutDirection != "") { - if (s.length() > 0) { - s += "-"; - } - s += layoutDirection; - } - if (this->smallestScreenWidthDp != "") { - if (s.length() > 0) { - s += "-"; - } - s += smallestScreenWidthDp; - } - if (this->screenWidthDp != "") { - if (s.length() > 0) { - s += "-"; - } - s += screenWidthDp; - } - if (this->screenHeightDp != "") { - if (s.length() > 0) { - s += "-"; - } - s += screenHeightDp; - } - if (this->screenLayoutSize != "") { - if (s.length() > 0) { - s += "-"; - } - s += screenLayoutSize; - } - if (this->screenLayoutLong != "") { - if (s.length() > 0) { - s += "-"; - } - s += screenLayoutLong; - } - if (this->orientation != "") { - if (s.length() > 0) { - s += "-"; - } - s += orientation; - } - if (this->uiModeType != "") { - if (s.length() > 0) { - s += "-"; - } - s += uiModeType; - } - if (this->uiModeNight != "") { - if (s.length() > 0) { - s += "-"; - } - s += uiModeNight; - } - if (this->density != "") { - if (s.length() > 0) { - s += "-"; - } - s += density; - } - if (this->touchscreen != "") { - if (s.length() > 0) { - s += "-"; - } - s += touchscreen; - } - if (this->keysHidden != "") { - if (s.length() > 0) { - s += "-"; - } - s += keysHidden; - } - if (this->keyboard != "") { - if (s.length() > 0) { - s += "-"; - } - s += keyboard; - } - if (this->navHidden != "") { + String8 params = mParams.toString(); + if (params.length() > 0) { if (s.length() > 0) { s += "-"; } - s += navHidden; + s += params; } - if (this->navigation != "") { - if (s.length() > 0) { - s += "-"; - } - s += navigation; - } - if (this->screenSize != "") { - if (s.length() > 0) { - s += "-"; - } - s += screenSize; - } - if (this->version != "") { - if (s.length() > 0) { - s += "-"; - } - s += version; - } - return s; } -bool AaptGroupEntry::getMccName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->mcc = 0; - return true; - } - const char* c = name; - if (tolower(*c) != 'm') return false; - c++; - if (tolower(*c) != 'c') return false; - c++; - if (tolower(*c) != 'c') return false; - c++; - - const char* val = c; - - while (*c >= '0' && *c <= '9') { - c++; - } - if (*c != 0) return false; - if (c-val != 3) return false; - - int d = atoi(val); - if (d != 0) { - if (out) out->mcc = d; - return true; - } - - return false; -} - -bool AaptGroupEntry::getMncName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->mcc = 0; - return true; - } - const char* c = name; - if (tolower(*c) != 'm') return false; - c++; - if (tolower(*c) != 'n') return false; - c++; - if (tolower(*c) != 'c') return false; - c++; - - const char* val = c; - - while (*c >= '0' && *c <= '9') { - c++; - } - if (*c != 0) return false; - if (c-val == 0 || c-val > 3) return false; - - if (out) { - out->mnc = atoi(val); - if (out->mnc == 0) { - out->mnc = ACONFIGURATION_MNC_ZERO; - } - } - - return true; -} - -bool AaptGroupEntry::getLayoutDirectionName(const char* name, ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) - | ResTable_config::LAYOUTDIR_ANY; - return true; - } else if (strcmp(name, "ldltr") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) - | ResTable_config::LAYOUTDIR_LTR; - return true; - } else if (strcmp(name, "ldrtl") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) - | ResTable_config::LAYOUTDIR_RTL; - return true; - } - - return false; -} - -bool AaptGroupEntry::getScreenLayoutSizeName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) - | ResTable_config::SCREENSIZE_ANY; - return true; - } else if (strcmp(name, "small") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) - | ResTable_config::SCREENSIZE_SMALL; - return true; - } else if (strcmp(name, "normal") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) - | ResTable_config::SCREENSIZE_NORMAL; - return true; - } else if (strcmp(name, "large") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) - | ResTable_config::SCREENSIZE_LARGE; - return true; - } else if (strcmp(name, "xlarge") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) - | ResTable_config::SCREENSIZE_XLARGE; - return true; - } - - return false; -} - -bool AaptGroupEntry::getScreenLayoutLongName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENLONG) - | ResTable_config::SCREENLONG_ANY; - return true; - } else if (strcmp(name, "long") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENLONG) - | ResTable_config::SCREENLONG_YES; - return true; - } else if (strcmp(name, "notlong") == 0) { - if (out) out->screenLayout = - (out->screenLayout&~ResTable_config::MASK_SCREENLONG) - | ResTable_config::SCREENLONG_NO; - return true; - } - - return false; -} - -bool AaptGroupEntry::getOrientationName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->orientation = out->ORIENTATION_ANY; - return true; - } else if (strcmp(name, "port") == 0) { - if (out) out->orientation = out->ORIENTATION_PORT; - return true; - } else if (strcmp(name, "land") == 0) { - if (out) out->orientation = out->ORIENTATION_LAND; - return true; - } else if (strcmp(name, "square") == 0) { - if (out) out->orientation = out->ORIENTATION_SQUARE; - return true; - } - - return false; -} - -bool AaptGroupEntry::getUiModeTypeName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->uiMode = - (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) - | ResTable_config::UI_MODE_TYPE_ANY; - return true; - } else if (strcmp(name, "desk") == 0) { - if (out) out->uiMode = - (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) - | ResTable_config::UI_MODE_TYPE_DESK; - return true; - } else if (strcmp(name, "car") == 0) { - if (out) out->uiMode = - (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) - | ResTable_config::UI_MODE_TYPE_CAR; - return true; - } else if (strcmp(name, "television") == 0) { - if (out) out->uiMode = - (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) - | ResTable_config::UI_MODE_TYPE_TELEVISION; - return true; - } else if (strcmp(name, "appliance") == 0) { - if (out) out->uiMode = - (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) - | ResTable_config::UI_MODE_TYPE_APPLIANCE; - return true; - } else if (strcmp(name, "watch") == 0) { - if (out) out->uiMode = - (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) - | ResTable_config::UI_MODE_TYPE_WATCH; - return true; - } - - return false; -} - -bool AaptGroupEntry::getUiModeNightName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->uiMode = - (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) - | ResTable_config::UI_MODE_NIGHT_ANY; - return true; - } else if (strcmp(name, "night") == 0) { - if (out) out->uiMode = - (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) - | ResTable_config::UI_MODE_NIGHT_YES; - return true; - } else if (strcmp(name, "notnight") == 0) { - if (out) out->uiMode = - (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) - | ResTable_config::UI_MODE_NIGHT_NO; - return true; - } - - return false; -} - -bool AaptGroupEntry::getDensityName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->density = ResTable_config::DENSITY_DEFAULT; - return true; - } - - if (strcmp(name, "nodpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_NONE; - return true; - } - - if (strcmp(name, "ldpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_LOW; - return true; - } - - if (strcmp(name, "mdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_MEDIUM; - return true; - } - - if (strcmp(name, "tvdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_TV; - return true; - } - - if (strcmp(name, "hdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_HIGH; - return true; - } - - if (strcmp(name, "xhdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_XHIGH; - return true; - } - - if (strcmp(name, "xxhdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_XXHIGH; - return true; - } - - if (strcmp(name, "xxxhdpi") == 0) { - if (out) out->density = ResTable_config::DENSITY_XXXHIGH; - return true; - } - - char* c = (char*)name; - while (*c >= '0' && *c <= '9') { - c++; - } - - // check that we have 'dpi' after the last digit. - if (toupper(c[0]) != 'D' || - toupper(c[1]) != 'P' || - toupper(c[2]) != 'I' || - c[3] != 0) { - return false; - } - - // temporarily replace the first letter with \0 to - // use atoi. - char tmp = c[0]; - c[0] = '\0'; - - int d = atoi(name); - c[0] = tmp; - - if (d != 0) { - if (out) out->density = d; - return true; - } - - return false; -} - -bool AaptGroupEntry::getTouchscreenName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->touchscreen = out->TOUCHSCREEN_ANY; - return true; - } else if (strcmp(name, "notouch") == 0) { - if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH; - return true; - } else if (strcmp(name, "stylus") == 0) { - if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS; - return true; - } else if (strcmp(name, "finger") == 0) { - if (out) out->touchscreen = out->TOUCHSCREEN_FINGER; - return true; - } - - return false; -} - -bool AaptGroupEntry::getKeysHiddenName(const char* name, - ResTable_config* out) -{ - uint8_t mask = 0; - uint8_t value = 0; - if (strcmp(name, kWildcardName) == 0) { - mask = ResTable_config::MASK_KEYSHIDDEN; - value = ResTable_config::KEYSHIDDEN_ANY; - } else if (strcmp(name, "keysexposed") == 0) { - mask = ResTable_config::MASK_KEYSHIDDEN; - value = ResTable_config::KEYSHIDDEN_NO; - } else if (strcmp(name, "keyshidden") == 0) { - mask = ResTable_config::MASK_KEYSHIDDEN; - value = ResTable_config::KEYSHIDDEN_YES; - } else if (strcmp(name, "keyssoft") == 0) { - mask = ResTable_config::MASK_KEYSHIDDEN; - value = ResTable_config::KEYSHIDDEN_SOFT; - } - - if (mask != 0) { - if (out) out->inputFlags = (out->inputFlags&~mask) | value; - return true; - } - - return false; -} - -bool AaptGroupEntry::getKeyboardName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->keyboard = out->KEYBOARD_ANY; - return true; - } else if (strcmp(name, "nokeys") == 0) { - if (out) out->keyboard = out->KEYBOARD_NOKEYS; - return true; - } else if (strcmp(name, "qwerty") == 0) { - if (out) out->keyboard = out->KEYBOARD_QWERTY; - return true; - } else if (strcmp(name, "12key") == 0) { - if (out) out->keyboard = out->KEYBOARD_12KEY; - return true; - } - - return false; -} - -bool AaptGroupEntry::getNavHiddenName(const char* name, - ResTable_config* out) -{ - uint8_t mask = 0; - uint8_t value = 0; - if (strcmp(name, kWildcardName) == 0) { - mask = ResTable_config::MASK_NAVHIDDEN; - value = ResTable_config::NAVHIDDEN_ANY; - } else if (strcmp(name, "navexposed") == 0) { - mask = ResTable_config::MASK_NAVHIDDEN; - value = ResTable_config::NAVHIDDEN_NO; - } else if (strcmp(name, "navhidden") == 0) { - mask = ResTable_config::MASK_NAVHIDDEN; - value = ResTable_config::NAVHIDDEN_YES; - } - - if (mask != 0) { - if (out) out->inputFlags = (out->inputFlags&~mask) | value; - return true; - } - - return false; -} - -bool AaptGroupEntry::getNavigationName(const char* name, - ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) out->navigation = out->NAVIGATION_ANY; - return true; - } else if (strcmp(name, "nonav") == 0) { - if (out) out->navigation = out->NAVIGATION_NONAV; - return true; - } else if (strcmp(name, "dpad") == 0) { - if (out) out->navigation = out->NAVIGATION_DPAD; - return true; - } else if (strcmp(name, "trackball") == 0) { - if (out) out->navigation = out->NAVIGATION_TRACKBALL; - return true; - } else if (strcmp(name, "wheel") == 0) { - if (out) out->navigation = out->NAVIGATION_WHEEL; - return true; - } - - return false; -} - -bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) { - out->screenWidth = out->SCREENWIDTH_ANY; - out->screenHeight = out->SCREENHEIGHT_ANY; - } - return true; - } - - const char* x = name; - while (*x >= '0' && *x <= '9') x++; - if (x == name || *x != 'x') return false; - String8 xName(name, x-name); - x++; - - const char* y = x; - while (*y >= '0' && *y <= '9') y++; - if (y == name || *y != 0) return false; - String8 yName(x, y-x); - - uint16_t w = (uint16_t)atoi(xName.string()); - uint16_t h = (uint16_t)atoi(yName.string()); - if (w < h) { - return false; - } - - if (out) { - out->screenWidth = w; - out->screenHeight = h; - } - - return true; -} - -bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) { - out->smallestScreenWidthDp = out->SCREENWIDTH_ANY; - } - return true; - } - - if (*name != 's') return false; - name++; - if (*name != 'w') return false; - name++; - const char* x = name; - while (*x >= '0' && *x <= '9') x++; - if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; - String8 xName(name, x-name); - - if (out) { - out->smallestScreenWidthDp = (uint16_t)atoi(xName.string()); - } - - return true; -} - -bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) { - out->screenWidthDp = out->SCREENWIDTH_ANY; - } - return true; - } - - if (*name != 'w') return false; - name++; - const char* x = name; - while (*x >= '0' && *x <= '9') x++; - if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; - String8 xName(name, x-name); - - if (out) { - out->screenWidthDp = (uint16_t)atoi(xName.string()); - } - - return true; -} - -bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) { - out->screenHeightDp = out->SCREENWIDTH_ANY; - } - return true; - } - - if (*name != 'h') return false; - name++; - const char* x = name; - while (*x >= '0' && *x <= '9') x++; - if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; - String8 xName(name, x-name); - - if (out) { - out->screenHeightDp = (uint16_t)atoi(xName.string()); - } - - return true; -} - -bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out) -{ - if (strcmp(name, kWildcardName) == 0) { - if (out) { - out->sdkVersion = out->SDKVERSION_ANY; - out->minorVersion = out->MINORVERSION_ANY; - } - return true; - } - - if (*name != 'v') { - return false; - } - - name++; - const char* s = name; - while (*s >= '0' && *s <= '9') s++; - if (s == name || *s != 0) return false; - String8 sdkName(name, s-name); - - if (out) { - out->sdkVersion = (uint16_t)atoi(sdkName.string()); - out->minorVersion = 0; - } - - return true; -} - -int AaptGroupEntry::compare(const AaptGroupEntry& o) const -{ - int v = mcc.compare(o.mcc); - if (v == 0) v = mnc.compare(o.mnc); - if (v == 0) v = locale.compare(o.locale); - if (v == 0) v = layoutDirection.compare(o.layoutDirection); - if (v == 0) v = vendor.compare(o.vendor); - if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp); - if (v == 0) v = screenWidthDp.compare(o.screenWidthDp); - if (v == 0) v = screenHeightDp.compare(o.screenHeightDp); - if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize); - if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong); - if (v == 0) v = orientation.compare(o.orientation); - if (v == 0) v = uiModeType.compare(o.uiModeType); - if (v == 0) v = uiModeNight.compare(o.uiModeNight); - if (v == 0) v = density.compare(o.density); - if (v == 0) v = touchscreen.compare(o.touchscreen); - if (v == 0) v = keysHidden.compare(o.keysHidden); - if (v == 0) v = keyboard.compare(o.keyboard); - if (v == 0) v = navHidden.compare(o.navHidden); - if (v == 0) v = navigation.compare(o.navigation); - if (v == 0) v = screenSize.compare(o.screenSize); - if (v == 0) v = version.compare(o.version); - return v; -} - -const ResTable_config AaptGroupEntry::toParams() const -{ - if (!mParamsChanged) { - return mParams; - } - - mParamsChanged = false; - ResTable_config& params = mParams; - memset(¶ms, 0, sizeof(ResTable_config)); - getMccName(mcc.string(), ¶ms); - getMncName(mnc.string(), ¶ms); - locale.writeTo(¶ms); - getLayoutDirectionName(layoutDirection.string(), ¶ms); - getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), ¶ms); - getScreenWidthDpName(screenWidthDp.string(), ¶ms); - getScreenHeightDpName(screenHeightDp.string(), ¶ms); - getScreenLayoutSizeName(screenLayoutSize.string(), ¶ms); - getScreenLayoutLongName(screenLayoutLong.string(), ¶ms); - getOrientationName(orientation.string(), ¶ms); - getUiModeTypeName(uiModeType.string(), ¶ms); - getUiModeNightName(uiModeNight.string(), ¶ms); - getDensityName(density.string(), ¶ms); - getTouchscreenName(touchscreen.string(), ¶ms); - getKeysHiddenName(keysHidden.string(), ¶ms); - getKeyboardName(keyboard.string(), ¶ms); - getNavHiddenName(navHidden.string(), ¶ms); - getNavigationName(navigation.string(), ¶ms); - getScreenSizeName(screenSize.string(), ¶ms); - getVersionName(version.string(), ¶ms); - - // Fix up version number based on specified parameters. - int minSdk = 0; - if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY - || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY - || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) { - minSdk = SDK_HONEYCOMB_MR2; - } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE) - != ResTable_config::UI_MODE_TYPE_ANY - || (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT) - != ResTable_config::UI_MODE_NIGHT_ANY) { - minSdk = SDK_FROYO; - } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE) - != ResTable_config::SCREENSIZE_ANY - || (params.screenLayout&ResTable_config::MASK_SCREENLONG) - != ResTable_config::SCREENLONG_ANY - || params.density != ResTable_config::DENSITY_DEFAULT) { - minSdk = SDK_DONUT; - } - - if (minSdk > params.sdkVersion) { - params.sdkVersion = minSdk; - } - - return params; -} // ========================================================================= // ========================================================================= @@ -2229,9 +907,7 @@ AaptAssets::AaptAssets() : AaptDir(String8(), String8()), mHavePrivateSymbols(false), mChanged(false), mHaveIncludedAssets(false), - mRes(NULL) -{ -} + mRes(NULL) {} const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const { if (mChanged) { @@ -2506,7 +1182,7 @@ ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir) String8 resType; bool b = group.initFromDirName(entry->d_name, &resType); if (!b) { - fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(), + fprintf(stderr, "invalid resource directory name: %s %s\n", srcDir.string(), entry->d_name); err = -1; continue; @@ -2654,30 +1330,35 @@ bail: status_t AaptAssets::filter(Bundle* bundle) { - ResourceFilter reqFilter; + WeakResourceFilter reqFilter; status_t err = reqFilter.parse(bundle->getConfigurations()); if (err != NO_ERROR) { return err; } - ResourceFilter prefFilter; - err = prefFilter.parse(bundle->getPreferredConfigurations()); - if (err != NO_ERROR) { - return err; + uint32_t preferredDensity = 0; + if (bundle->getPreferredDensity().size() > 0) { + ResTable_config preferredConfig; + if (!AaptConfig::parseDensity(bundle->getPreferredDensity().string(), &preferredConfig)) { + fprintf(stderr, "Error parsing preferred density: %s\n", + bundle->getPreferredDensity().string()); + return UNKNOWN_ERROR; + } + preferredDensity = preferredConfig.density; } - if (reqFilter.isEmpty() && prefFilter.isEmpty()) { + if (reqFilter.isEmpty() && preferredDensity == 0) { return NO_ERROR; } if (bundle->getVerbose()) { if (!reqFilter.isEmpty()) { printf("Applying required filter: %s\n", - bundle->getConfigurations()); + bundle->getConfigurations().string()); } - if (!prefFilter.isEmpty()) { - printf("Applying preferred filter: %s\n", - bundle->getPreferredConfigurations()); + if (preferredDensity > 0) { + printf("Applying preferred density filter: %s\n", + bundle->getPreferredDensity().string()); } } @@ -2734,88 +1415,70 @@ status_t AaptAssets::filter(Bundle* bundle) } // Quick check: no preferred filters, nothing more to do. - if (prefFilter.isEmpty()) { + if (preferredDensity == 0) { continue; } // Get the preferred density if there is one. We do not match exactly for density. // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we // pick xhdpi. - uint32_t preferredDensity = 0; - const SortedVector<AxisValue>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY); - if (preferredConfigs != NULL && preferredConfigs->size() > 0) { - preferredDensity = (*preferredConfigs)[0].intValue; - } + for (size_t k=0; k<grp->getFiles().size(); k++) { + sp<AaptFile> file = grp->getFiles().valueAt(k); + if (k == 0 && grp->getFiles().size() == 1) { + // If this is the only file left, we need to keep it. + // Otherwise the resource IDs we are using will be inconsistent + // with what we get when not stripping. Sucky, but at least + // for now we can rely on the back-end doing another filtering + // pass to take this out and leave us with this resource name + // containing no entries. + continue; + } + if (file->getPath().getPathExtension() == ".xml") { + // We can't remove .xml files at this point, because when + // we parse them they may add identifier resources, so + // removing them can cause our resource identifiers to + // become inconsistent. + continue; + } + const ResTable_config& config(file->getGroupEntry().toParams()); + if (config.density != 0 && config.density != preferredDensity) { + // This is a resource we would prefer not to have. Check + // to see if have a similar variation that we would like + // to have and, if so, we can drop it. + uint32_t bestDensity = config.density; + + for (size_t m=0; m<grp->getFiles().size(); m++) { + if (m == k) { + continue; + } - // Now deal with preferred configurations. - for (int axis=AXIS_START; axis<=AXIS_END; axis++) { - for (size_t k=0; k<grp->getFiles().size(); k++) { - sp<AaptFile> file = grp->getFiles().valueAt(k); - if (k == 0 && grp->getFiles().size() == 1) { - // If this is the only file left, we need to keep it. - // Otherwise the resource IDs we are using will be inconsistent - // with what we get when not stripping. Sucky, but at least - // for now we can rely on the back-end doing another filtering - // pass to take this out and leave us with this resource name - // containing no entries. - continue; - } - if (file->getPath().getPathExtension() == ".xml") { - // We can't remove .xml files at this point, because when - // we parse them they may add identifier resources, so - // removing them can cause our resource identifiers to - // become inconsistent. - continue; - } - const ResTable_config& config(file->getGroupEntry().toParams()); - if (!prefFilter.match(axis, config)) { - // This is a resource we would prefer not to have. Check - // to see if have a similar variation that we would like - // to have and, if so, we can drop it. - - uint32_t bestDensity = config.density; - - for (size_t m=0; m<grp->getFiles().size(); m++) { - if (m == k) continue; - sp<AaptFile> mfile = grp->getFiles().valueAt(m); - const ResTable_config& mconfig(mfile->getGroupEntry().toParams()); - if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) { - if (axis == AXIS_DENSITY && preferredDensity > 0) { - // See if there is a better density resource - if (mconfig.density < bestDensity && - mconfig.density > preferredDensity && - bestDensity > preferredDensity) { - // This density is between our best density and - // the preferred density, therefore it is better. - bestDensity = mconfig.density; - } else if (mconfig.density > bestDensity && - bestDensity < preferredDensity) { - // This density is better than our best density and - // our best density was smaller than our preferred - // density, so it is better. - bestDensity = mconfig.density; - } - } else if (prefFilter.match(axis, mconfig)) { - if (bundle->getVerbose()) { - printf("Pruning unneeded resource: %s\n", - file->getPrintableSource().string()); - } - grp->removeFile(k); - k--; - break; - } + sp<AaptFile> mfile = grp->getFiles().valueAt(m); + const ResTable_config& mconfig(mfile->getGroupEntry().toParams()); + if (AaptConfig::isSameExcept(config, mconfig, ResTable_config::CONFIG_DENSITY)) { + // See if there is a better density resource + if (mconfig.density < bestDensity && + mconfig.density > preferredDensity && + bestDensity > preferredDensity) { + // This density is between our best density and + // the preferred density, therefore it is better. + bestDensity = mconfig.density; + } else if (mconfig.density > bestDensity && + bestDensity < preferredDensity) { + // This density is better than our best density and + // our best density was smaller than our preferred + // density, so it is better. + bestDensity = mconfig.density; } } + } - if (axis == AXIS_DENSITY && preferredDensity > 0 && - bestDensity != config.density) { - if (bundle->getVerbose()) { - printf("Pruning unneeded resource: %s\n", - file->getPrintableSource().string()); - } - grp->removeFile(k); - k--; + if (bestDensity != config.density) { + if (bundle->getVerbose()) { + printf("Pruning unneeded resource: %s\n", + file->getPrintableSource().string()); } + grp->removeFile(k); + k--; } } } diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h index 82dda5ff04d3..0c2576a954b1 100644 --- a/tools/aapt/AaptAssets.h +++ b/tools/aapt/AaptAssets.h @@ -6,22 +6,24 @@ #ifndef __AAPT_ASSETS_H #define __AAPT_ASSETS_H -#include <stdlib.h> #include <androidfw/AssetManager.h> #include <androidfw/ResourceTypes.h> +#include <stdlib.h> +#include <set> #include <utils/KeyedVector.h> #include <utils/RefBase.h> #include <utils/SortedVector.h> #include <utils/String8.h> #include <utils/Vector.h> -#include "ZipFile.h" +#include "AaptConfig.h" #include "Bundle.h" +#include "ConfigDescription.h" #include "SourcePos.h" +#include "ZipFile.h" using namespace android; - extern const char * const gDefaultIgnoreAssets; extern const char * gUserIgnoreAssets; @@ -82,9 +84,6 @@ struct AaptLocaleValue { return memcmp(this, &other, sizeof(AaptLocaleValue)); } - static void splitAndLowerCase(const char* const chars, Vector<String8>* parts, - const char separator); - inline bool operator<(const AaptLocaleValue& o) const { return compare(o) < 0; } inline bool operator<=(const AaptLocaleValue& o) const { return compare(o) <= 0; } inline bool operator==(const AaptLocaleValue& o) const { return compare(o) == 0; } @@ -98,31 +97,6 @@ private: void setVariant(const char* variant); }; -struct AxisValue { - // Used for all axes except AXIS_LOCALE, which is represented - // as a AaptLocaleValue value. - int intValue; - AaptLocaleValue localeValue; - - AxisValue() : intValue(0) { - } - - inline int compare(const AxisValue &other) const { - if (intValue != other.intValue) { - return intValue - other.intValue; - } - - return localeValue.compare(other.localeValue); - } - - inline bool operator<(const AxisValue& o) const { return compare(o) < 0; } - inline bool operator<=(const AxisValue& o) const { return compare(o) <= 0; } - inline bool operator==(const AxisValue& o) const { return compare(o) == 0; } - inline bool operator!=(const AxisValue& o) const { return compare(o) != 0; } - inline bool operator>=(const AxisValue& o) const { return compare(o) >= 0; } - inline bool operator>(const AxisValue& o) const { return compare(o) > 0; } -}; - /** * This structure contains a specific variation of a single file out * of all the variations it can have that we can have. @@ -130,23 +104,11 @@ struct AxisValue { struct AaptGroupEntry { public: - AaptGroupEntry() : mParamsChanged(true) { - memset(&mParams, 0, sizeof(ResTable_config)); - } - bool initFromDirName(const char* dir, String8* resType); - static bool parseFilterNamePart(const String8& part, int* axis, AxisValue* value); - - static AxisValue getConfigValueForAxis(const ResTable_config& config, int axis); - - static bool configSameExcept(const ResTable_config& config, - const ResTable_config& otherConfig, int axis); - - int compare(const AaptGroupEntry& o) const; - - const ResTable_config toParams() const; + inline const ConfigDescription& toParams() const { return mParams; } + inline int compare(const AaptGroupEntry& o) const { return mParams.compareLogical(o.mParams); } inline bool operator<(const AaptGroupEntry& o) const { return compare(o) < 0; } inline bool operator<=(const AaptGroupEntry& o) const { return compare(o) <= 0; } inline bool operator==(const AaptGroupEntry& o) const { return compare(o) == 0; } @@ -154,56 +116,13 @@ public: inline bool operator>=(const AaptGroupEntry& o) const { return compare(o) >= 0; } inline bool operator>(const AaptGroupEntry& o) const { return compare(o) > 0; } - String8 toString() const; + String8 toString() const { return mParams.toString(); } String8 toDirName(const String8& resType) const; - const String8& getVersionString() const { return version; } + const String8 getVersionString() const { return AaptConfig::getVersion(mParams); } private: - static bool getMccName(const char* name, ResTable_config* out = NULL); - static bool getMncName(const char* name, ResTable_config* out = NULL); - static bool getScreenLayoutSizeName(const char* name, ResTable_config* out = NULL); - static bool getScreenLayoutLongName(const char* name, ResTable_config* out = NULL); - static bool getOrientationName(const char* name, ResTable_config* out = NULL); - static bool getUiModeTypeName(const char* name, ResTable_config* out = NULL); - static bool getUiModeNightName(const char* name, ResTable_config* out = NULL); - static bool getDensityName(const char* name, ResTable_config* out = NULL); - static bool getTouchscreenName(const char* name, ResTable_config* out = NULL); - static bool getKeysHiddenName(const char* name, ResTable_config* out = NULL); - static bool getKeyboardName(const char* name, ResTable_config* out = NULL); - static bool getNavigationName(const char* name, ResTable_config* out = NULL); - static bool getNavHiddenName(const char* name, ResTable_config* out = NULL); - static bool getScreenSizeName(const char* name, ResTable_config* out = NULL); - static bool getSmallestScreenWidthDpName(const char* name, ResTable_config* out = NULL); - static bool getScreenWidthDpName(const char* name, ResTable_config* out = NULL); - static bool getScreenHeightDpName(const char* name, ResTable_config* out = NULL); - static bool getLayoutDirectionName(const char* name, ResTable_config* out = NULL); - static bool getVersionName(const char* name, ResTable_config* out = NULL); - - String8 mcc; - String8 mnc; - AaptLocaleValue locale; - String8 vendor; - String8 smallestScreenWidthDp; - String8 screenWidthDp; - String8 screenHeightDp; - String8 screenLayoutSize; - String8 screenLayoutLong; - String8 orientation; - String8 uiModeType; - String8 uiModeNight; - String8 density; - String8 touchscreen; - String8 keysHidden; - String8 keyboard; - String8 navHidden; - String8 navigation; - String8 screenSize; - String8 layoutDirection; - String8 version; - - mutable bool mParamsChanged; - mutable ResTable_config mParams; + ConfigDescription mParams; }; inline int compare_type(const AaptGroupEntry& lhs, const AaptGroupEntry& rhs) diff --git a/tools/aapt/AaptConfig.cpp b/tools/aapt/AaptConfig.cpp new file mode 100644 index 000000000000..69a9c7feca99 --- /dev/null +++ b/tools/aapt/AaptConfig.cpp @@ -0,0 +1,790 @@ +/* + * 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. + */ + +#include <androidfw/ResourceTypes.h> +#include <ctype.h> + +#include "AaptConfig.h" +#include "AaptAssets.h" +#include "AaptUtil.h" +#include "ResourceFilter.h" + +using android::String8; +using android::Vector; +using android::ResTable_config; + +namespace AaptConfig { + +static const char* kWildcardName = "any"; + +bool parse(const String8& str, ConfigDescription* out) { + Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '-'); + + ConfigDescription config; + AaptLocaleValue locale; + ssize_t index = 0; + ssize_t localeIndex = 0; + const ssize_t N = parts.size(); + const char* part = parts[index].string(); + + if (str.length() == 0) { + goto success; + } + + if (parseMcc(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseMnc(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + // Locale spans a few '-' separators, so we let it + // control the index. + localeIndex = locale.initFromDirName(parts, index); + if (localeIndex < 0) { + return false; + } else if (localeIndex > index) { + locale.writeTo(&config); + index = localeIndex; + if (index >= N) { + goto success; + } + part = parts[index].string(); + } + + if (parseLayoutDirection(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseSmallestScreenWidthDp(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseScreenWidthDp(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseScreenHeightDp(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseScreenLayoutSize(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseScreenLayoutLong(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseOrientation(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseUiModeType(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseUiModeNight(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseDensity(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseTouchscreen(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseKeysHidden(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseKeyboard(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseNavHidden(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseNavigation(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseScreenSize(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + if (parseVersion(part, &config)) { + index++; + if (index == N) { + goto success; + } + part = parts[index].string(); + } + + // Unrecognized. + return false; + +success: + if (out != NULL) { + applyVersionForCompatibility(&config); + *out = config; + } + return true; +} + +bool parseCommaSeparatedList(const String8& str, std::set<ConfigDescription>* outSet) { + Vector<String8> parts = AaptUtil::splitAndLowerCase(str, ','); + const size_t N = parts.size(); + for (size_t i = 0; i < N; i++) { + ConfigDescription config; + if (!parse(parts[i], &config)) { + return false; + } + outSet->insert(config); + } + return true; +} + +void applyVersionForCompatibility(ConfigDescription* config) { + if (config == NULL) { + return; + } + + uint16_t minSdk = 0; + if (config->smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY + || config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY + || config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) { + minSdk = SDK_HONEYCOMB_MR2; + } else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE) + != ResTable_config::UI_MODE_TYPE_ANY + || (config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT) + != ResTable_config::UI_MODE_NIGHT_ANY) { + minSdk = SDK_FROYO; + } else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE) + != ResTable_config::SCREENSIZE_ANY + || (config->screenLayout & ResTable_config::MASK_SCREENLONG) + != ResTable_config::SCREENLONG_ANY + || config->density != ResTable_config::DENSITY_DEFAULT) { + minSdk = SDK_DONUT; + } + + if (minSdk > config->sdkVersion) { + config->sdkVersion = minSdk; + } +} + +bool parseMcc(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->mcc = 0; + return true; + } + const char* c = name; + if (tolower(*c) != 'm') return false; + c++; + if (tolower(*c) != 'c') return false; + c++; + if (tolower(*c) != 'c') return false; + c++; + + const char* val = c; + + while (*c >= '0' && *c <= '9') { + c++; + } + if (*c != 0) return false; + if (c-val != 3) return false; + + int d = atoi(val); + if (d != 0) { + if (out) out->mcc = d; + return true; + } + + return false; +} + +bool parseMnc(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->mcc = 0; + return true; + } + const char* c = name; + if (tolower(*c) != 'm') return false; + c++; + if (tolower(*c) != 'n') return false; + c++; + if (tolower(*c) != 'c') return false; + c++; + + const char* val = c; + + while (*c >= '0' && *c <= '9') { + c++; + } + if (*c != 0) return false; + if (c-val == 0 || c-val > 3) return false; + + if (out) { + out->mnc = atoi(val); + if (out->mnc == 0) { + out->mnc = ACONFIGURATION_MNC_ZERO; + } + } + + return true; +} + +bool parseLayoutDirection(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) + | ResTable_config::LAYOUTDIR_ANY; + return true; + } else if (strcmp(name, "ldltr") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) + | ResTable_config::LAYOUTDIR_LTR; + return true; + } else if (strcmp(name, "ldrtl") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR) + | ResTable_config::LAYOUTDIR_RTL; + return true; + } + + return false; +} + +bool parseScreenLayoutSize(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) + | ResTable_config::SCREENSIZE_ANY; + return true; + } else if (strcmp(name, "small") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) + | ResTable_config::SCREENSIZE_SMALL; + return true; + } else if (strcmp(name, "normal") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) + | ResTable_config::SCREENSIZE_NORMAL; + return true; + } else if (strcmp(name, "large") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) + | ResTable_config::SCREENSIZE_LARGE; + return true; + } else if (strcmp(name, "xlarge") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENSIZE) + | ResTable_config::SCREENSIZE_XLARGE; + return true; + } + + return false; +} + +bool parseScreenLayoutLong(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENLONG) + | ResTable_config::SCREENLONG_ANY; + return true; + } else if (strcmp(name, "long") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENLONG) + | ResTable_config::SCREENLONG_YES; + return true; + } else if (strcmp(name, "notlong") == 0) { + if (out) out->screenLayout = + (out->screenLayout&~ResTable_config::MASK_SCREENLONG) + | ResTable_config::SCREENLONG_NO; + return true; + } + + return false; +} + +bool parseOrientation(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->orientation = out->ORIENTATION_ANY; + return true; + } else if (strcmp(name, "port") == 0) { + if (out) out->orientation = out->ORIENTATION_PORT; + return true; + } else if (strcmp(name, "land") == 0) { + if (out) out->orientation = out->ORIENTATION_LAND; + return true; + } else if (strcmp(name, "square") == 0) { + if (out) out->orientation = out->ORIENTATION_SQUARE; + return true; + } + + return false; +} + +bool parseUiModeType(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) + | ResTable_config::UI_MODE_TYPE_ANY; + return true; + } else if (strcmp(name, "desk") == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) + | ResTable_config::UI_MODE_TYPE_DESK; + return true; + } else if (strcmp(name, "car") == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) + | ResTable_config::UI_MODE_TYPE_CAR; + return true; + } else if (strcmp(name, "television") == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) + | ResTable_config::UI_MODE_TYPE_TELEVISION; + return true; + } else if (strcmp(name, "appliance") == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) + | ResTable_config::UI_MODE_TYPE_APPLIANCE; + return true; + } else if (strcmp(name, "watch") == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE) + | ResTable_config::UI_MODE_TYPE_WATCH; + return true; + } + + return false; +} + +bool parseUiModeNight(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) + | ResTable_config::UI_MODE_NIGHT_ANY; + return true; + } else if (strcmp(name, "night") == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) + | ResTable_config::UI_MODE_NIGHT_YES; + return true; + } else if (strcmp(name, "notnight") == 0) { + if (out) out->uiMode = + (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT) + | ResTable_config::UI_MODE_NIGHT_NO; + return true; + } + + return false; +} + +bool parseDensity(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->density = ResTable_config::DENSITY_DEFAULT; + return true; + } + + if (strcmp(name, "nodpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_NONE; + return true; + } + + if (strcmp(name, "ldpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_LOW; + return true; + } + + if (strcmp(name, "mdpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_MEDIUM; + return true; + } + + if (strcmp(name, "tvdpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_TV; + return true; + } + + if (strcmp(name, "hdpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_HIGH; + return true; + } + + if (strcmp(name, "xhdpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_XHIGH; + return true; + } + + if (strcmp(name, "xxhdpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_XXHIGH; + return true; + } + + if (strcmp(name, "xxxhdpi") == 0) { + if (out) out->density = ResTable_config::DENSITY_XXXHIGH; + return true; + } + + char* c = (char*)name; + while (*c >= '0' && *c <= '9') { + c++; + } + + // check that we have 'dpi' after the last digit. + if (toupper(c[0]) != 'D' || + toupper(c[1]) != 'P' || + toupper(c[2]) != 'I' || + c[3] != 0) { + return false; + } + + // temporarily replace the first letter with \0 to + // use atoi. + char tmp = c[0]; + c[0] = '\0'; + + int d = atoi(name); + c[0] = tmp; + + if (d != 0) { + if (out) out->density = d; + return true; + } + + return false; +} + +bool parseTouchscreen(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->touchscreen = out->TOUCHSCREEN_ANY; + return true; + } else if (strcmp(name, "notouch") == 0) { + if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH; + return true; + } else if (strcmp(name, "stylus") == 0) { + if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS; + return true; + } else if (strcmp(name, "finger") == 0) { + if (out) out->touchscreen = out->TOUCHSCREEN_FINGER; + return true; + } + + return false; +} + +bool parseKeysHidden(const char* name, ResTable_config* out) { + uint8_t mask = 0; + uint8_t value = 0; + if (strcmp(name, kWildcardName) == 0) { + mask = ResTable_config::MASK_KEYSHIDDEN; + value = ResTable_config::KEYSHIDDEN_ANY; + } else if (strcmp(name, "keysexposed") == 0) { + mask = ResTable_config::MASK_KEYSHIDDEN; + value = ResTable_config::KEYSHIDDEN_NO; + } else if (strcmp(name, "keyshidden") == 0) { + mask = ResTable_config::MASK_KEYSHIDDEN; + value = ResTable_config::KEYSHIDDEN_YES; + } else if (strcmp(name, "keyssoft") == 0) { + mask = ResTable_config::MASK_KEYSHIDDEN; + value = ResTable_config::KEYSHIDDEN_SOFT; + } + + if (mask != 0) { + if (out) out->inputFlags = (out->inputFlags&~mask) | value; + return true; + } + + return false; +} + +bool parseKeyboard(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->keyboard = out->KEYBOARD_ANY; + return true; + } else if (strcmp(name, "nokeys") == 0) { + if (out) out->keyboard = out->KEYBOARD_NOKEYS; + return true; + } else if (strcmp(name, "qwerty") == 0) { + if (out) out->keyboard = out->KEYBOARD_QWERTY; + return true; + } else if (strcmp(name, "12key") == 0) { + if (out) out->keyboard = out->KEYBOARD_12KEY; + return true; + } + + return false; +} + +bool parseNavHidden(const char* name, ResTable_config* out) { + uint8_t mask = 0; + uint8_t value = 0; + if (strcmp(name, kWildcardName) == 0) { + mask = ResTable_config::MASK_NAVHIDDEN; + value = ResTable_config::NAVHIDDEN_ANY; + } else if (strcmp(name, "navexposed") == 0) { + mask = ResTable_config::MASK_NAVHIDDEN; + value = ResTable_config::NAVHIDDEN_NO; + } else if (strcmp(name, "navhidden") == 0) { + mask = ResTable_config::MASK_NAVHIDDEN; + value = ResTable_config::NAVHIDDEN_YES; + } + + if (mask != 0) { + if (out) out->inputFlags = (out->inputFlags&~mask) | value; + return true; + } + + return false; +} + +bool parseNavigation(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) out->navigation = out->NAVIGATION_ANY; + return true; + } else if (strcmp(name, "nonav") == 0) { + if (out) out->navigation = out->NAVIGATION_NONAV; + return true; + } else if (strcmp(name, "dpad") == 0) { + if (out) out->navigation = out->NAVIGATION_DPAD; + return true; + } else if (strcmp(name, "trackball") == 0) { + if (out) out->navigation = out->NAVIGATION_TRACKBALL; + return true; + } else if (strcmp(name, "wheel") == 0) { + if (out) out->navigation = out->NAVIGATION_WHEEL; + return true; + } + + return false; +} + +bool parseScreenSize(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) { + out->screenWidth = out->SCREENWIDTH_ANY; + out->screenHeight = out->SCREENHEIGHT_ANY; + } + return true; + } + + const char* x = name; + while (*x >= '0' && *x <= '9') x++; + if (x == name || *x != 'x') return false; + String8 xName(name, x-name); + x++; + + const char* y = x; + while (*y >= '0' && *y <= '9') y++; + if (y == name || *y != 0) return false; + String8 yName(x, y-x); + + uint16_t w = (uint16_t)atoi(xName.string()); + uint16_t h = (uint16_t)atoi(yName.string()); + if (w < h) { + return false; + } + + if (out) { + out->screenWidth = w; + out->screenHeight = h; + } + + return true; +} + +bool parseSmallestScreenWidthDp(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) { + out->smallestScreenWidthDp = out->SCREENWIDTH_ANY; + } + return true; + } + + if (*name != 's') return false; + name++; + if (*name != 'w') return false; + name++; + const char* x = name; + while (*x >= '0' && *x <= '9') x++; + if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; + String8 xName(name, x-name); + + if (out) { + out->smallestScreenWidthDp = (uint16_t)atoi(xName.string()); + } + + return true; +} + +bool parseScreenWidthDp(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) { + out->screenWidthDp = out->SCREENWIDTH_ANY; + } + return true; + } + + if (*name != 'w') return false; + name++; + const char* x = name; + while (*x >= '0' && *x <= '9') x++; + if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; + String8 xName(name, x-name); + + if (out) { + out->screenWidthDp = (uint16_t)atoi(xName.string()); + } + + return true; +} + +bool parseScreenHeightDp(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) { + out->screenHeightDp = out->SCREENWIDTH_ANY; + } + return true; + } + + if (*name != 'h') return false; + name++; + const char* x = name; + while (*x >= '0' && *x <= '9') x++; + if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false; + String8 xName(name, x-name); + + if (out) { + out->screenHeightDp = (uint16_t)atoi(xName.string()); + } + + return true; +} + +bool parseVersion(const char* name, ResTable_config* out) { + if (strcmp(name, kWildcardName) == 0) { + if (out) { + out->sdkVersion = out->SDKVERSION_ANY; + out->minorVersion = out->MINORVERSION_ANY; + } + return true; + } + + if (*name != 'v') { + return false; + } + + name++; + const char* s = name; + while (*s >= '0' && *s <= '9') s++; + if (s == name || *s != 0) return false; + String8 sdkName(name, s-name); + + if (out) { + out->sdkVersion = (uint16_t)atoi(sdkName.string()); + out->minorVersion = 0; + } + + return true; +} + +String8 getVersion(const ResTable_config& config) { + return String8::format("v%u", config.sdkVersion); +} + +bool isSameExcept(const ResTable_config& a, const ResTable_config& b, int axisMask) { + return a.diff(b) == axisMask; +} + +} // namespace AaptConfig diff --git a/tools/aapt/AaptConfig.h b/tools/aapt/AaptConfig.h new file mode 100644 index 000000000000..2963539afc8d --- /dev/null +++ b/tools/aapt/AaptConfig.h @@ -0,0 +1,85 @@ +/* + * 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_CONFIG_H +#define __AAPT_CONFIG_H + +#include <set> +#include <utils/String8.h> + +#include "ConfigDescription.h" + +/** + * Utility methods for dealing with configurations. + */ +namespace AaptConfig { + +/** + * Parse a string of the form 'fr-sw600dp-land' and fill in the + * given ResTable_config with resulting configuration parameters. + * + * The resulting configuration has the appropriate sdkVersion defined + * for backwards compatibility. + */ +bool parse(const android::String8& str, ConfigDescription* out = NULL); + +/** + * Parse a comma separated list of configuration strings. Duplicate configurations + * will be removed. + * + * Example input: "fr,de-land,fr-sw600dp-land" + */ +bool parseCommaSeparatedList(const android::String8& str, std::set<ConfigDescription>* outSet); + +/** + * If the configuration uses an axis that was added after + * the original Android release, make sure the SDK version + * is set accordingly. + */ +void applyVersionForCompatibility(ConfigDescription* config); + +// Individual axis +bool parseMcc(const char* str, android::ResTable_config* out = NULL); +bool parseMnc(const char* str, android::ResTable_config* out = NULL); +bool parseLayoutDirection(const char* str, android::ResTable_config* out = NULL); +bool parseSmallestScreenWidthDp(const char* str, android::ResTable_config* out = NULL); +bool parseScreenWidthDp(const char* str, android::ResTable_config* out = NULL); +bool parseScreenHeightDp(const char* str, android::ResTable_config* out = NULL); +bool parseScreenLayoutSize(const char* str, android::ResTable_config* out = NULL); +bool parseScreenLayoutLong(const char* str, android::ResTable_config* out = NULL); +bool parseOrientation(const char* str, android::ResTable_config* out = NULL); +bool parseUiModeType(const char* str, android::ResTable_config* out = NULL); +bool parseUiModeNight(const char* str, android::ResTable_config* out = NULL); +bool parseDensity(const char* str, android::ResTable_config* out = NULL); +bool parseTouchscreen(const char* str, android::ResTable_config* out = NULL); +bool parseKeysHidden(const char* str, android::ResTable_config* out = NULL); +bool parseKeyboard(const char* str, android::ResTable_config* out = NULL); +bool parseNavHidden(const char* str, android::ResTable_config* out = NULL); +bool parseNavigation(const char* str, android::ResTable_config* out = NULL); +bool parseScreenSize(const char* str, android::ResTable_config* out = NULL); +bool parseVersion(const char* str, android::ResTable_config* out = NULL); + +android::String8 getVersion(const android::ResTable_config& config); + +/** + * Returns true if the two configurations only differ by the specified axis. + * The axis mask is a bitmask of CONFIG_* constants. + */ +bool isSameExcept(const android::ResTable_config& a, const android::ResTable_config& b, int configMask); + +} // namespace AaptConfig + +#endif // __AAPT_CONFIG_H diff --git a/tools/aapt/AaptUtil.cpp b/tools/aapt/AaptUtil.cpp new file mode 100644 index 000000000000..293e144b71fe --- /dev/null +++ b/tools/aapt/AaptUtil.cpp @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#include "AaptUtil.h" + +using android::Vector; +using android::String8; + +namespace AaptUtil { + +Vector<String8> split(const String8& str, const char sep) { + Vector<String8> parts; + const char* p = str.string(); + const char* q; + + while (true) { + q = strchr(p, sep); + if (q == NULL) { + parts.add(String8(p, strlen(p))); + return parts; + } + + parts.add(String8(p, q-p)); + p = q + 1; + } + return parts; +} + +Vector<String8> splitAndLowerCase(const String8& str, const char sep) { + Vector<String8> parts; + const char* p = str.string(); + const char* q; + + while (true) { + q = strchr(p, sep); + if (q == NULL) { + String8 val(p, strlen(p)); + val.toLower(); + parts.add(val); + return parts; + } + + String8 val(p, q-p); + val.toLower(); + parts.add(val); + p = q + 1; + } + return parts; +} + +} // namespace AaptUtil diff --git a/tools/aapt/AaptUtil.h b/tools/aapt/AaptUtil.h new file mode 100644 index 000000000000..47a704a4567d --- /dev/null +++ b/tools/aapt/AaptUtil.h @@ -0,0 +1,30 @@ +/* + * 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_UTIL_H +#define __AAPT_UTIL_H + +#include <utils/String8.h> +#include <utils/Vector.h> + +namespace AaptUtil { + +android::Vector<android::String8> split(const android::String8& str, const char sep); +android::Vector<android::String8> splitAndLowerCase(const android::String8& str, const char sep); + +} // namespace AaptUtil + +#endif // __AAPT_UTIL_H diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk index 806f8ff7a0d8..700afa1d570d 100644 --- a/tools/aapt/Android.mk +++ b/tools/aapt/Android.mk @@ -1,104 +1,168 @@ -# -# Copyright 2006 The Android Open Source Project # -# Android Asset Packaging Tool +# 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. # # This tool is prebuilt if we're doing an app-only build. ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),) +# ========================================================== +# Setup some common variables for the different build +# targets here. +# ========================================================== +LOCAL_PATH:= $(call my-dir) -aapt_src_files := \ - AaptAssets.cpp \ - Command.cpp \ - CrunchCache.cpp \ - FileFinder.cpp \ - Main.cpp \ - Package.cpp \ - StringPool.cpp \ - XMLNode.cpp \ - ResourceFilter.cpp \ - ResourceIdCache.cpp \ - ResourceTable.cpp \ - Images.cpp \ - Resource.cpp \ +aaptMain := Main.cpp +aaptSources := \ + AaptAssets.cpp \ + AaptConfig.cpp \ + AaptUtil.cpp \ + ApkBuilder.cpp \ + Command.cpp \ + CrunchCache.cpp \ + FileFinder.cpp \ + Package.cpp \ + StringPool.cpp \ + XMLNode.cpp \ + ResourceFilter.cpp \ + ResourceIdCache.cpp \ + ResourceTable.cpp \ + Images.cpp \ + Resource.cpp \ pseudolocalize.cpp \ SourcePos.cpp \ - WorkQueue.cpp \ + WorkQueue.cpp \ ZipEntry.cpp \ ZipFile.cpp \ - qsort_r_compat.c + qsort_r_compat.c + +aaptTests := \ + tests/AaptConfig_test.cpp \ + tests/AaptGroupEntry_test.cpp \ + tests/ResourceFilter_test.cpp + +aaptCIncludes := \ + external/libpng \ + external/zlib + +aaptHostLdLibs := +aaptHostStaticLibs := \ + libandroidfw \ + libpng \ + liblog \ + libutils \ + libcutils \ + libexpat \ + libziparchive-host -LOCAL_PATH:= $(call my-dir) +ifeq ($(HOST_OS),linux) + aaptHostLdLibs += -lrt -ldl -lpthread +endif + +# Statically link libz for MinGW (Win SDK under Linux), +# and dynamically link for all others. +ifneq ($(strip $(USE_MINGW)),) + aaptHostStaticLibs += libz +else + aaptHostLdLibs += -lz +endif + + +# ========================================================== +# Build the host static library: libaapt +# ========================================================== include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(aapt_src_files) +LOCAL_MODULE := libaapt + +LOCAL_SRC_FILES := $(aaptSources) +LOCAL_C_INCLUDES += $(aaptCIncludes) LOCAL_CFLAGS += -Wno-format-y2k +LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS ifeq (darwin,$(HOST_OS)) LOCAL_CFLAGS += -D_DARWIN_UNLIMITED_STREAMS endif -LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS +include $(BUILD_HOST_STATIC_LIBRARY) -LOCAL_C_INCLUDES += external/libpng -LOCAL_C_INCLUDES += external/zlib -LOCAL_STATIC_LIBRARIES := \ - libandroidfw \ - libutils \ - libcutils \ - libexpat \ - libpng \ - liblog \ - libziparchive-host +# ========================================================== +# Build the host executable: aapt +# ========================================================== +include $(CLEAR_VARS) -ifeq ($(HOST_OS),linux) -LOCAL_LDLIBS += -lrt -ldl -lpthread -endif +LOCAL_MODULE := aapt -# Statically link libz for MinGW (Win SDK under Linux), -# and dynamically link for all others. -ifneq ($(strip $(USE_MINGW)),) - LOCAL_STATIC_LIBRARIES += libz -else - LOCAL_LDLIBS += -lz -endif +LOCAL_SRC_FILES := $(aaptMain) -LOCAL_MODULE := aapt +LOCAL_STATIC_LIBRARIES += \ + libaapt \ + $(aaptHostStaticLibs) +LOCAL_LDLIBS += $(aaptHostLdLibs) include $(BUILD_HOST_EXECUTABLE) -# aapt for running on the device -# ========================================================= -ifneq ($(SDK_ONLY),true) + +# ========================================================== +# Build the host tests: libaapt_tests +# ========================================================== include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(aapt_src_files) +LOCAL_MODULE := libaapt_tests -LOCAL_MODULE := aapt +LOCAL_SRC_FILES += $(aaptTests) +LOCAL_C_INCLUDES += $(LOCAL_PATH) -LOCAL_C_INCLUDES += bionic -LOCAL_C_INCLUDES += bionic/libstdc++/include -LOCAL_C_INCLUDES += external/stlport/stlport -LOCAL_C_INCLUDES += external/libpng -LOCAL_C_INCLUDES += external/zlib +LOCAL_STATIC_LIBRARIES += \ + libaapt \ + $(aaptHostStaticLibs) +LOCAL_LDLIBS += $(aaptHostLdLibs) -LOCAL_CFLAGS += -Wno-non-virtual-dtor +include $(BUILD_HOST_NATIVE_TEST) + + +# ========================================================== +# Build the device executable: aapt +# ========================================================== +ifneq ($(SDK_ONLY),true) +include $(CLEAR_VARS) + +LOCAL_MODULE := aapt + +LOCAL_SRC_FILES := $(aaptSources) $(aaptMain) +LOCAL_C_INCLUDES += \ + $(aaptCIncludes) \ + bionic \ + external/stlport/stlport LOCAL_SHARED_LIBRARIES := \ - libandroidfw \ - libutils \ - libcutils \ - libpng \ - liblog \ - libz + libandroidfw \ + libutils \ + libcutils \ + libpng \ + liblog \ + libz LOCAL_STATIC_LIBRARIES := \ - libstlport_static \ - libexpat_static + libstlport_static \ + libexpat_static + +LOCAL_CPPFLAGS += -Wno-non-virtual-dtor include $(BUILD_EXECUTABLE) -endif + +endif # Not SDK_ONLY endif # No TARGET_BUILD_APPS or TARGET_BUILD_PDK diff --git a/tools/aapt/ApkBuilder.cpp b/tools/aapt/ApkBuilder.cpp new file mode 100644 index 000000000000..12f60400e99d --- /dev/null +++ b/tools/aapt/ApkBuilder.cpp @@ -0,0 +1,111 @@ +/* + * 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. + */ + +#include "AaptAssets.h" +#include "ApkBuilder.h" + +using namespace android; + +ApkBuilder::ApkBuilder(const sp<WeakResourceFilter>& configFilter) + : mConfigFilter(configFilter) + , mDefaultFilter(new AndResourceFilter()) { + // Add the default split, which is present for all APKs. + mDefaultFilter->addFilter(mConfigFilter); + mSplits.add(new ApkSplit(std::set<ConfigDescription>(), mDefaultFilter, true)); +} + +status_t ApkBuilder::createSplitForConfigs(const std::set<ConfigDescription>& configs) { + const size_t N = mSplits.size(); + for (size_t i = 0; i < N; i++) { + const std::set<ConfigDescription>& splitConfigs = mSplits[i]->getConfigs(); + std::set<ConfigDescription>::const_iterator iter = configs.begin(); + for (; iter != configs.end(); iter++) { + if (splitConfigs.count(*iter) > 0) { + // Can't have overlapping configurations. + fprintf(stderr, "ERROR: Split configuration '%s' is already defined " + "in another split.\n", iter->toString().string()); + return ALREADY_EXISTS; + } + } + } + + sp<StrongResourceFilter> splitFilter = new StrongResourceFilter(configs); + + // Add the inverse filter of this split filter to the base apk filter so it will + // omit resources that belong in this split. + mDefaultFilter->addFilter(new InverseResourceFilter(splitFilter)); + + // Now add the apk-wide config filter to our split filter. + sp<AndResourceFilter> filter = new AndResourceFilter(); + filter->addFilter(splitFilter); + filter->addFilter(mConfigFilter); + mSplits.add(new ApkSplit(configs, filter)); + return NO_ERROR; +} + +status_t ApkBuilder::addEntry(const String8& path, const sp<AaptFile>& file) { + const size_t N = mSplits.size(); + for (size_t i = 0; i < N; i++) { + if (mSplits[i]->matches(file)) { + return mSplits.editItemAt(i)->addEntry(path, file); + } + } + // Entry can be dropped if it doesn't match any split. This will only happen + // if the enry doesn't mConfigFilter. + return NO_ERROR; +} + +void ApkBuilder::print() const { + fprintf(stderr, "APK Builder\n"); + fprintf(stderr, "-----------\n"); + const size_t N = mSplits.size(); + for (size_t i = 0; i < N; i++) { + mSplits[i]->print(); + fprintf(stderr, "\n"); + } +} + +ApkSplit::ApkSplit(const std::set<ConfigDescription>& configs, const sp<ResourceFilter>& filter, bool isBase) + : mConfigs(configs), mFilter(filter), mIsBase(isBase) { + std::set<ConfigDescription>::const_iterator iter = configs.begin(); + for (; iter != configs.end(); iter++) { + if (mName.size() > 0) { + mName.append(","); + mDirName.append("_"); + } + + String8 configStr = iter->toString(); + mName.append(configStr); + mDirName.append(configStr); + } +} + +status_t ApkSplit::addEntry(const String8& path, const sp<AaptFile>& file) { + if (!mFiles.insert(OutputEntry(path, file)).second) { + // Duplicate file. + return ALREADY_EXISTS; + } + return NO_ERROR; +} + +void ApkSplit::print() const { + fprintf(stderr, "APK Split '%s'\n", mName.string()); + + std::set<OutputEntry>::const_iterator iter = mFiles.begin(); + for (; iter != mFiles.end(); iter++) { + fprintf(stderr, " %s (%s)\n", iter->getPath().string(), iter->getFile()->getSourceFile().string()); + } +} diff --git a/tools/aapt/ApkBuilder.h b/tools/aapt/ApkBuilder.h new file mode 100644 index 000000000000..a4b7d4a0ca23 --- /dev/null +++ b/tools/aapt/ApkBuilder.h @@ -0,0 +1,113 @@ +/* + * 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 __APK_BUILDER_H +#define __APK_BUILDER_H + +#include <set> +#include <utils/Errors.h> +#include <utils/String8.h> +#include <utils/StrongPointer.h> +#include <utils/Vector.h> + +#include "ConfigDescription.h" +#include "OutputSet.h" +#include "ResourceFilter.h" + +class ApkSplit; +class AaptFile; + +class ApkBuilder : public android::RefBase { +public: + ApkBuilder(const sp<WeakResourceFilter>& configFilter); + + /** + * Tells the builder to generate a separate APK for resources that + * match the configurations specified. Split APKs can not have + * overlapping resources. + * + * NOTE: All splits should be set up before any files are added. + */ + android::status_t createSplitForConfigs(const std::set<ConfigDescription>& configs); + + /** + * Adds a file to be written to the final APK. It's name must not collide + * with that of any files previously added. When a Split APK is being + * generated, duplicates can exist as long as they are in different splits + * (resources.arsc, AndroidManifest.xml). + */ + android::status_t addEntry(const String8& path, const android::sp<AaptFile>& file); + + android::Vector<sp<ApkSplit> >& getSplits() { + return mSplits; + } + + void print() const; + +private: + android::sp<ResourceFilter> mConfigFilter; + android::sp<AndResourceFilter> mDefaultFilter; + android::Vector<sp<ApkSplit> > mSplits; +}; + +class ApkSplit : public OutputSet { +public: + android::status_t addEntry(const String8& path, const android::sp<AaptFile>& file); + + const std::set<OutputEntry>& getEntries() const { + return mFiles; + } + + const std::set<ConfigDescription>& getConfigs() const { + return mConfigs; + } + + bool matches(const sp<AaptFile>& file) const { + return mFilter->match(file->getGroupEntry().toParams()); + } + + sp<ResourceFilter> getResourceFilter() const { + return mFilter; + } + + const android::String8& getPrintableName() const { + return mName; + } + + const android::String8& getDirectorySafeName() const { + return mDirName; + } + + bool isBase() const { + return mIsBase; + } + + void print() const; + +private: + friend class ApkBuilder; + + ApkSplit(const std::set<ConfigDescription>& configs, const android::sp<ResourceFilter>& filter, bool isBase=false); + + std::set<ConfigDescription> mConfigs; + const sp<ResourceFilter> mFilter; + const bool mIsBase; + String8 mName; + String8 mDirName; + std::set<OutputEntry> mFiles; +}; + +#endif // __APK_BUILDER_H diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h index ebe1bed40848..ceb52a07103b 100644 --- a/tools/aapt/Bundle.h +++ b/tools/aapt/Bundle.h @@ -151,10 +151,12 @@ public: void setPublicOutputFile(const char* file) { mPublicOutputFile = file; } const char* getRClassDir() const { return mRClassDir; } void setRClassDir(const char* dir) { mRClassDir = dir; } - const char* getConfigurations() const { return mConfigurations.size() > 0 ? mConfigurations.string() : NULL; } + const android::String8& getConfigurations() const { return mConfigurations; } void addConfigurations(const char* val) { if (mConfigurations.size() > 0) { mConfigurations.append(","); mConfigurations.append(val); } else { mConfigurations = val; } } - const char* getPreferredConfigurations() const { return mPreferredConfigurations.size() > 0 ? mPreferredConfigurations.string() : NULL; } - void addPreferredConfigurations(const char* val) { if (mPreferredConfigurations.size() > 0) { mPreferredConfigurations.append(","); mPreferredConfigurations.append(val); } else { mPreferredConfigurations = val; } } + const android::String8& getPreferredDensity() const { return mPreferredDensity; } + void setPreferredDensity(const char* val) { mPreferredDensity = val; } + void addSplitConfigurations(const char* val) { mPartialConfigurations.add(android::String8(val)); } + const android::Vector<android::String8>& getSplitConfigurations() const { return mPartialConfigurations; } const char* getResourceIntermediatesDir() const { return mResourceIntermediatesDir; } void setResourceIntermediatesDir(const char* dir) { mResourceIntermediatesDir = dir; } const android::Vector<const char*>& getPackageIncludes() const { return mPackageIncludes; } @@ -286,7 +288,8 @@ private: const char* mRClassDir; const char* mResourceIntermediatesDir; android::String8 mConfigurations; - android::String8 mPreferredConfigurations; + android::String8 mPreferredDensity; + android::Vector<android::String8> mPartialConfigurations; android::Vector<const char*> mPackageIncludes; android::Vector<const char*> mJarFiles; android::Vector<const char*> mNoCompressExtensions; diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 0af1ce1c49fc..036020064c0a 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -3,6 +3,7 @@ // // Android Asset Packaging Tool main entry point. // +#include "ApkBuilder.h" #include "Main.h" #include "Bundle.h" #include "ResourceFilter.h" @@ -2034,6 +2035,47 @@ bail: return (result != NO_ERROR); } +static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder) { + const size_t numDirs = dir->getDirs().size(); + for (size_t i = 0; i < numDirs; i++) { + status_t err = addResourcesToBuilder(dir->getDirs().valueAt(i), builder); + if (err != NO_ERROR) { + return err; + } + } + + const size_t numFiles = dir->getFiles().size(); + for (size_t i = 0; i < numFiles; i++) { + sp<AaptGroup> gp = dir->getFiles().valueAt(i); + const size_t numConfigs = gp->getFiles().size(); + for (size_t j = 0; j < numConfigs; j++) { + status_t err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j)); + if (err != NO_ERROR) { + fprintf(stderr, "Failed to add %s (%s) to builder.\n", + gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string()); + return err; + } + } + } + return NO_ERROR; +} + +static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) { + if (split->isBase()) { + return original; + } + + String8 ext(original.getPathExtension()); + if (ext == String8(".apk")) { + return String8::format("%s_%s%s", + original.getBasePath().string(), + split->getDirectorySafeName().string(), + ext.string()); + } + + return String8::format("%s_%s", original.string(), + split->getDirectorySafeName().string()); +} /* * Package up an asset directory and associated application files. @@ -2047,17 +2089,18 @@ int doPackage(Bundle* bundle) int N; FILE* fp; String8 dependencyFile; + sp<ApkBuilder> builder; // -c en_XA or/and ar_XB means do pseudolocalization - ResourceFilter filter; - err = filter.parse(bundle->getConfigurations()); + sp<WeakResourceFilter> configFilter = new WeakResourceFilter(); + err = configFilter->parse(bundle->getConfigurations()); if (err != NO_ERROR) { goto bail; } - if (filter.containsPseudo()) { + if (configFilter->containsPseudo()) { bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED); } - if (filter.containsPseudoBidi()) { + if (configFilter->containsPseudoBidi()) { bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI); } @@ -2105,9 +2148,32 @@ int doPackage(Bundle* bundle) assets->print(String8()); } + // Create the ApkBuilder, which will collect the compiled files + // to write to the final APK (or sets of APKs if we are building + // a Split APK. + builder = new ApkBuilder(configFilter); + + // If we are generating a Split APK, find out which configurations to split on. + if (bundle->getSplitConfigurations().size() > 0) { + const Vector<String8>& splitStrs = bundle->getSplitConfigurations(); + const size_t numSplits = splitStrs.size(); + for (size_t i = 0; i < numSplits; i++) { + std::set<ConfigDescription> configs; + if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) { + fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string()); + goto bail; + } + + err = builder->createSplitForConfigs(configs); + if (err != NO_ERROR) { + goto bail; + } + } + } + // If they asked for any fileAs that need to be compiled, do so. if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) { - err = buildResources(bundle, assets); + err = buildResources(bundle, assets, builder); if (err != 0) { goto bail; } @@ -2194,11 +2260,24 @@ int doPackage(Bundle* bundle) // Write the apk if (outputAPKFile) { - err = writeAPK(bundle, assets, String8(outputAPKFile)); + // Gather all resources and add them to the APK Builder. The builder will then + // figure out which Split they belong in. + err = addResourcesToBuilder(assets, builder); if (err != NO_ERROR) { - fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile); goto bail; } + + const Vector<sp<ApkSplit> >& splits = builder->getSplits(); + const size_t numSplits = splits.size(); + for (size_t i = 0; i < numSplits; i++) { + const sp<ApkSplit>& split = splits[i]; + String8 outputPath = buildApkName(String8(outputAPKFile), split); + err = writeAPK(bundle, outputPath, split); + if (err != NO_ERROR) { + fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string()); + goto bail; + } + } } // If we've been asked to generate a dependency file, we need to finish up here. diff --git a/tools/aapt/ConfigDescription.h b/tools/aapt/ConfigDescription.h new file mode 100644 index 000000000000..779c423fce1a --- /dev/null +++ b/tools/aapt/ConfigDescription.h @@ -0,0 +1,57 @@ +/* + * 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 __CONFIG_DESCRIPTION_H +#define __CONFIG_DESCRIPTION_H + +#include <androidfw/ResourceTypes.h> + +/** + * Subclass of ResTable_config that adds convenient + * initialization and comparison methods. + */ +struct ConfigDescription : public android::ResTable_config { + ConfigDescription() { + memset(this, 0, sizeof(*this)); + size = sizeof(android::ResTable_config); + } + ConfigDescription(const android::ResTable_config&o) { + *static_cast<android::ResTable_config*>(this) = o; + size = sizeof(android::ResTable_config); + } + ConfigDescription(const ConfigDescription&o) { + *static_cast<android::ResTable_config*>(this) = o; + } + + ConfigDescription& operator=(const android::ResTable_config& o) { + *static_cast<android::ResTable_config*>(this) = o; + size = sizeof(android::ResTable_config); + return *this; + } + ConfigDescription& operator=(const ConfigDescription& o) { + *static_cast<android::ResTable_config*>(this) = o; + return *this; + } + + inline bool operator<(const ConfigDescription& o) const { return compare(o) < 0; } + inline bool operator<=(const ConfigDescription& o) const { return compare(o) <= 0; } + inline bool operator==(const ConfigDescription& o) const { return compare(o) == 0; } + inline bool operator!=(const ConfigDescription& o) const { return compare(o) != 0; } + inline bool operator>=(const ConfigDescription& o) const { return compare(o) >= 0; } + inline bool operator>(const ConfigDescription& o) const { return compare(o) > 0; } +}; + +#endif // __CONFIG_DESCRIPTION_H diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp index 1cf4783a8de9..5a60014cad53 100644 --- a/tools/aapt/Main.cpp +++ b/tools/aapt/Main.cpp @@ -70,6 +70,7 @@ void usage(void) " [-F apk-file] [-J R-file-dir] \\\n" " [--product product1,product2,...] \\\n" " [-c CONFIGS] [--preferred-configurations CONFIGS] \\\n" + " [--split CONFIGS [--split CONFIGS]] \\\n" " [raw-files-dir [raw-files-dir] ...] \\\n" " [--output-text-symbols DIR]\n" "\n" @@ -166,10 +167,12 @@ void usage(void) " generate dependency files in the same directories for R.java and resource package\n" " --auto-add-overlay\n" " Automatically add resources that are only in overlays.\n" - " --preferred-configurations\n" - " Like the -c option for filtering out unneeded configurations, but\n" - " only expresses a preference. If there is no resource available with\n" - " the preferred configuration then it will not be stripped.\n" + " --preferred-density\n" + " Specifies a preference for a particular density. Resources that do not\n" + " match this density and have variants that are a closer match are removed.\n" + " --split\n" + " Builds a separate split APK for the configurations listed. This can\n" + " be loaded alongside the base APK at runtime.\n" " --rename-manifest-package\n" " Rewrite the manifest so that its package name is the package name\n" " given here. Relative class names (for example .Foo) will be\n" @@ -568,15 +571,24 @@ int main(int argc, char* const argv[]) bundle.setGenDependencies(true); } else if (strcmp(cp, "-utf16") == 0) { bundle.setWantUTF16(true); - } else if (strcmp(cp, "-preferred-configurations") == 0) { + } else if (strcmp(cp, "-preferred-density") == 0) { argc--; argv++; if (!argc) { - fprintf(stderr, "ERROR: No argument supplied for '--preferred-configurations' option\n"); + fprintf(stderr, "ERROR: No argument supplied for '--preferred-density' option\n"); wantUsage = true; goto bail; } - bundle.addPreferredConfigurations(argv[0]); + bundle.setPreferredDensity(argv[0]); + } else if (strcmp(cp, "-split") == 0) { + argc--; + argv++; + if (!argc) { + fprintf(stderr, "ERROR: No argument supplied for '--split' option\n"); + wantUsage = true; + goto bail; + } + bundle.addSplitConfigurations(argv[0]); } else if (strcmp(cp, "-rename-manifest-package") == 0) { argc--; argv++; diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h index a6b39ac418dc..34c449687c56 100644 --- a/tools/aapt/Main.h +++ b/tools/aapt/Main.h @@ -10,8 +10,12 @@ #include <utils/threads.h> #include <utils/List.h> #include <utils/Errors.h> -#include "Bundle.h" +#include <utils/StrongPointer.h> + #include "AaptAssets.h" +#include "ApkBuilder.h" +#include "Bundle.h" +#include "ResourceFilter.h" #include "ZipFile.h" @@ -22,6 +26,8 @@ #include <time.h> #endif /* BENCHMARK */ +class OutputSet; + extern int doVersion(Bundle* bundle); extern int doList(Bundle* bundle); extern int doDump(Bundle* bundle); @@ -34,13 +40,13 @@ extern int doSingleCrunch(Bundle* bundle); extern int calcPercent(long uncompressedLen, long compressedLen); extern android::status_t writeAPK(Bundle* bundle, - const sp<AaptAssets>& assets, - const android::String8& outputFile); + const android::String8& outputFile, + const android::sp<OutputSet>& outputSet); extern android::status_t updatePreProcessedCache(Bundle* bundle); extern android::status_t buildResources(Bundle* bundle, - const sp<AaptAssets>& assets); + const sp<AaptAssets>& assets, sp<ApkBuilder>& builder); extern android::status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets, const String8& pkgName, bool includePrivate); @@ -49,8 +55,6 @@ extern android::status_t writeProguardFile(Bundle* bundle, const sp<AaptAssets>& extern bool isValidResourceType(const String8& type); -ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets); - extern status_t filterResources(Bundle* bundle, const sp<AaptAssets>& assets); int dumpResources(Bundle* bundle); diff --git a/tools/aapt/OutputSet.h b/tools/aapt/OutputSet.h new file mode 100644 index 000000000000..ea9ef709b56f --- /dev/null +++ b/tools/aapt/OutputSet.h @@ -0,0 +1,56 @@ +/* + * 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 __OUTPUT_SET_H +#define __OUTPUT_SET_H + +#include <set> +#include <utils/Errors.h> +#include <utils/String8.h> +#include <utils/StrongPointer.h> + +class AaptFile; + +class OutputEntry { +public: + OutputEntry() {} + OutputEntry(const android::String8& path, const android::sp<const AaptFile>& file) + : mPath(path), mFile(file) {} + + inline const android::sp<const AaptFile>& getFile() const { + return mFile; + } + + inline const android::String8& getPath() const { + return mPath; + } + + bool operator<(const OutputEntry& o) const { return getPath() < o.mPath; } + bool operator==(const OutputEntry& o) const { return getPath() == o.mPath; } + +private: + android::String8 mPath; + android::sp<const AaptFile> mFile; +}; + +class OutputSet : public virtual android::RefBase { +public: + virtual const std::set<OutputEntry>& getEntries() const = 0; + + virtual ~OutputSet() {} +}; + +#endif // __OUTPUT_SET_H diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp index 872d95c509f7..dc16e354b584 100644 --- a/tools/aapt/Package.cpp +++ b/tools/aapt/Package.cpp @@ -5,6 +5,7 @@ // #include "Main.h" #include "AaptAssets.h" +#include "OutputSet.h" #include "ResourceTable.h" #include "ResourceFilter.h" @@ -36,11 +37,8 @@ static const char* kNoCompressExt[] = { }; /* fwd decls, so I can write this downward */ -ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets); -ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir, - const AaptGroupEntry& ge, const ResourceFilter* filter); -bool processFile(Bundle* bundle, ZipFile* zip, - const sp<AaptGroup>& group, const sp<AaptFile>& file); +ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& outputSet); +bool processFile(Bundle* bundle, ZipFile* zip, String8 storageName, const sp<const AaptFile>& file); bool okayToCompress(Bundle* bundle, const String8& pathName); ssize_t processJarFiles(Bundle* bundle, ZipFile* zip); @@ -51,8 +49,7 @@ ssize_t processJarFiles(Bundle* bundle, ZipFile* zip); * On success, "bundle->numPackages" will be the number of Zip packages * we created. */ -status_t writeAPK(Bundle* bundle, const sp<AaptAssets>& assets, - const String8& outputFile) +status_t writeAPK(Bundle* bundle, const String8& outputFile, const sp<OutputSet>& outputSet) { #if BENCHMARK fprintf(stdout, "BENCHMARK: Starting APK Bundling \n"); @@ -112,7 +109,7 @@ status_t writeAPK(Bundle* bundle, const sp<AaptAssets>& assets, printf("Writing all files...\n"); } - count = processAssets(bundle, zip, assets); + count = processAssets(bundle, zip, outputSet); if (count < 0) { fprintf(stderr, "ERROR: unable to process assets while packaging '%s'\n", outputFile.string()); @@ -218,72 +215,24 @@ bail: return result; } -ssize_t processAssets(Bundle* bundle, ZipFile* zip, - const sp<AaptAssets>& assets) -{ - ResourceFilter filter; - status_t status = filter.parse(bundle->getConfigurations()); - if (status != NO_ERROR) { - return -1; - } - - ssize_t count = 0; - - const size_t N = assets->getGroupEntries().size(); - for (size_t i=0; i<N; i++) { - const AaptGroupEntry& ge = assets->getGroupEntries()[i]; - - ssize_t res = processAssets(bundle, zip, assets, ge, &filter); - if (res < 0) { - return res; - } - - count += res; - } - - return count; -} - -ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir, - const AaptGroupEntry& ge, const ResourceFilter* filter) +ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& outputSet) { ssize_t count = 0; - - const size_t ND = dir->getDirs().size(); - size_t i; - for (i=0; i<ND; i++) { - const sp<AaptDir>& subDir = dir->getDirs().valueAt(i); - - const bool filterable = filter != NULL && subDir->getLeaf().find("mipmap-") != 0; - - if (filterable && subDir->getLeaf() != subDir->getPath() && !filter->match(ge.toParams())) { - continue; - } - - ssize_t res = processAssets(bundle, zip, subDir, ge, filterable ? filter : NULL); - if (res < 0) { - return res; - } - count += res; - } - - if (filter != NULL && !filter->match(ge.toParams())) { - return count; - } - - const size_t NF = dir->getFiles().size(); - for (i=0; i<NF; i++) { - sp<AaptGroup> gp = dir->getFiles().valueAt(i); - ssize_t fi = gp->getFiles().indexOfKey(ge); - if (fi >= 0) { - sp<AaptFile> fl = gp->getFiles().valueAt(fi); - if (!processFile(bundle, zip, gp, fl)) { + const std::set<OutputEntry>& entries = outputSet->getEntries(); + std::set<OutputEntry>::const_iterator iter = entries.begin(); + for (; iter != entries.end(); iter++) { + const OutputEntry& entry = *iter; + if (entry.getFile() == NULL) { + fprintf(stderr, "warning: null file being processed.\n"); + } else { + String8 storagePath(entry.getPath()); + storagePath.convertToResPath(); + if (!processFile(bundle, zip, storagePath, entry.getFile())) { return UNKNOWN_ERROR; } count++; } } - return count; } @@ -294,12 +243,10 @@ ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir, * delete the existing entry before adding the new one. */ bool processFile(Bundle* bundle, ZipFile* zip, - const sp<AaptGroup>& group, const sp<AaptFile>& file) + String8 storageName, const sp<const AaptFile>& file) { const bool hasData = file->hasData(); - String8 storageName(group->getPath()); - storageName.convertToResPath(); ZipEntry* entry; bool fromGzip = false; status_t result; @@ -403,8 +350,8 @@ bool processFile(Bundle* bundle, ZipFile* zip, fprintf(stderr, " Unable to add '%s': file already in archive (try '-u'?)\n", file->getPrintableSource().string()); } else { - fprintf(stderr, " Unable to add '%s': Zip add failed\n", - file->getPrintableSource().string()); + fprintf(stderr, " Unable to add '%s': Zip add failed (%d)\n", + file->getPrintableSource().string(), result); } return false; } diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index 1348be356bf8..e599643bfdb4 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -179,24 +179,6 @@ bool isValidResourceType(const String8& type) || type == "color" || type == "menu" || type == "mipmap"; } -static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true) -{ - sp<AaptGroup> group = assets->getFiles().valueFor(String8("resources.arsc")); - sp<AaptFile> file; - if (group != NULL) { - file = group->getFiles().valueFor(AaptGroupEntry()); - if (file != NULL) { - return file; - } - } - - if (!makeIfNecessary) { - return NULL; - } - return assets->addFile(String8("resources.arsc"), AaptGroupEntry(), String8(), - NULL, String8()); -} - static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets, const sp<AaptGroup>& grp) { @@ -359,23 +341,6 @@ static status_t preProcessImages(const Bundle* bundle, const sp<AaptAssets>& ass return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR; } -status_t postProcessImages(const sp<AaptAssets>& assets, - ResourceTable* table, - const sp<ResourceTypeSet>& set) -{ - ResourceDirIterator it(set, String8("drawable")); - bool hasErrors = false; - ssize_t res; - while ((res=it.next()) == NO_ERROR) { - res = postProcessImage(assets, table, it.getFile()); - if (res < NO_ERROR) { - hasErrors = true; - } - } - - return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR; -} - static void collect_files(const sp<AaptDir>& dir, KeyedVector<String8, sp<ResourceTypeSet> >* resources) { @@ -906,7 +871,38 @@ status_t updatePreProcessedCache(Bundle* bundle) return 0; } -status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) +status_t generateAndroidManifestForSplit(const String16& package, const sp<ApkSplit>& split, + sp<AaptFile>& outFile) { + const String8 filename("AndroidManifest.xml"); + const String16 androidPrefix("android"); + const String16 androidNSUri("http://schemas.android.com/apk/res/android"); + sp<XMLNode> root = XMLNode::newNamespace(filename, androidPrefix, androidNSUri); + + // Build the <manifest> tag + sp<XMLNode> manifest = XMLNode::newElement(filename, String16(), String16("manifest")); + + // Add the 'package' attribute which is set to the original package name. + manifest->addAttribute(String16(), String16("package"), package); + + // Add the 'split' attribute which describes the configurations included. + String8 splitName("config_"); + splitName.append(split->getDirectorySafeName()); + manifest->addAttribute(String16(), String16("split"), String16(splitName)); + + // Build an empty <application> tag (required). + sp<XMLNode> app = XMLNode::newElement(filename, String16(), String16("application")); + manifest->addChild(app); + root->addChild(manifest); + + status_t err = root->flatten(outFile, true, true); + if (err != NO_ERROR) { + return err; + } + outFile->setCompressionMethod(ZipEntry::kCompressDeflated); + return NO_ERROR; +} + +status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuilder>& builder) { // First, look for a package file to parse. This is required to // be able to generate the resource information. @@ -1122,12 +1118,6 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) // -------------------------------------------------------------------- if (table.hasResources()) { - sp<AaptFile> resFile(getResourceFile(assets)); - if (resFile == NULL) { - fprintf(stderr, "Error: unable to generate entry for resource data\n"); - return UNKNOWN_ERROR; - } - err = table.assignResourceIds(); if (err < NO_ERROR) { return err; @@ -1235,16 +1225,24 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) } if (drawables != NULL) { - err = postProcessImages(assets, &table, drawables); - if (err != NO_ERROR) { + ResourceDirIterator it(drawables, String8("drawable")); + while ((err=it.next()) == NO_ERROR) { + err = postProcessImage(assets, &table, it.getFile()); + if (err != NO_ERROR) { + hasErrors = true; + } + } + + if (err < NO_ERROR) { hasErrors = true; } + err = NO_ERROR; } if (colors != NULL) { ResourceDirIterator it(colors, String8("color")); while ((err=it.next()) == NO_ERROR) { - err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); + err = compileXmlFile(assets, it.getFile(), &table, xmlFlags); if (err != NO_ERROR) { hasErrors = true; } @@ -1320,15 +1318,35 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) return err; } - resFile = getResourceFile(assets); - if (resFile == NULL) { - fprintf(stderr, "Error: unable to generate entry for resource data\n"); - return UNKNOWN_ERROR; - } + Vector<sp<ApkSplit> >& splits = builder->getSplits(); + const size_t numSplits = splits.size(); + for (size_t i = 0; i < numSplits; i++) { + sp<ApkSplit>& split = splits.editItemAt(i); + sp<AaptFile> flattenedTable = new AaptFile(String8("resources.arsc"), + AaptGroupEntry(), String8()); + err = table.flatten(bundle, split->getResourceFilter(), flattenedTable); + if (err != NO_ERROR) { + fprintf(stderr, "Failed to generate resource table for split '%s'\n", + split->getPrintableName().string()); + return err; + } + split->addEntry(String8("resources.arsc"), flattenedTable); - err = table.flatten(bundle, resFile); - if (err < NO_ERROR) { - return err; + if (split->isBase()) { + resFile = flattenedTable; + finalResTable.add(flattenedTable->getData(), flattenedTable->getSize()); + } else { + sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"), + AaptGroupEntry(), String8()); + err = generateAndroidManifestForSplit(String16(assets->getPackage()), split, + generatedManifest); + if (err != NO_ERROR) { + fprintf(stderr, "Failed to generate AndroidManifest.xml for split '%s'\n", + split->getPrintableName().string()); + return err; + } + split->addEntry(String8("AndroidManifest.xml"), generatedManifest); + } } if (bundle->getPublicOutputFile()) { @@ -1344,18 +1362,13 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) table.writePublicDefinitions(String16(assets->getPackage()), fp); fclose(fp); } - - // Read resources back in, - finalResTable.add(resFile->getData(), resFile->getSize()); - -#if 0 - NOISY( - printf("Generated resources:\n"); - finalResTable.print(); - ) -#endif + + if (finalResTable.getTableCount() == 0 || resFile == NULL) { + fprintf(stderr, "No resource table was generated.\n"); + return UNKNOWN_ERROR; + } } - + // Perform a basic validation of the manifest file. This time we // parse it with the comments intact, so that we can use them to // generate java docs... so we are not going to write this one diff --git a/tools/aapt/ResourceFilter.cpp b/tools/aapt/ResourceFilter.cpp index 8ca852e0813f..de8b4fccb369 100644 --- a/tools/aapt/ResourceFilter.cpp +++ b/tools/aapt/ResourceFilter.cpp @@ -1,119 +1,92 @@ // -// Copyright 2011 The Android Open Source Project +// Copyright 2014 The Android Open Source Project // // Build resource files from raw assets. // #include "ResourceFilter.h" +#include "AaptUtil.h" +#include "AaptConfig.h" status_t -ResourceFilter::parse(const char* arg) +WeakResourceFilter::parse(const String8& str) { - if (arg == NULL) { - return 0; - } - - const char* p = arg; - const char* q; - - while (true) { - q = strchr(p, ','); - if (q == NULL) { - q = p + strlen(p); - } - - String8 part(p, q-p); - + Vector<String8> configStrs = AaptUtil::split(str, ','); + const size_t N = configStrs.size(); + mConfigs.clear(); + mConfigMask = 0; + mConfigs.resize(N); + for (size_t i = 0; i < N; i++) { + const String8& part = configStrs[i]; if (part == "en_XA") { mContainsPseudoAccented = true; } else if (part == "ar_XB") { mContainsPseudoBidi = true; } - int axis; - AxisValue value; - if (!AaptGroupEntry::parseFilterNamePart(part, &axis, &value)) { - fprintf(stderr, "Invalid configuration: %s\n", arg); - fprintf(stderr, " "); - for (int i=0; i<p-arg; i++) { - fprintf(stderr, " "); - } - for (int i=0; i<q-p; i++) { - fprintf(stderr, "^"); - } - fprintf(stderr, "\n"); - return 1; - } - ssize_t index = mData.indexOfKey(axis); - if (index < 0) { - mData.add(axis, SortedVector<AxisValue>()); - } - SortedVector<AxisValue>& sv = mData.editValueFor(axis); - sv.add(value); + std::pair<ConfigDescription, uint32_t>& entry = mConfigs.editItemAt(i); - // If it's a locale with a region, script or variant, we should also match an - // unmodified locale of the same language - if (axis == AXIS_LOCALE) { - if (value.localeValue.region[0] || value.localeValue.script[0] || - value.localeValue.variant[0]) { - AxisValue copy; - memcpy(copy.localeValue.language, value.localeValue.language, - sizeof(value.localeValue.language)); - sv.add(copy); - } + AaptLocaleValue val; + if (val.initFromFilterString(part)) { + // For backwards compatibility, we accept configurations that + // only specify locale in the standard 'en_US' format. + val.writeTo(&entry.first); + } else if (!AaptConfig::parse(part, &entry.first)) { + fprintf(stderr, "Invalid configuration: %s\n", part.string()); + return UNKNOWN_ERROR; } - p = q; - if (!*p) break; - p++; + + entry.second = mDefault.diff(entry.first); + + // Ignore the version + entry.second &= ~ResTable_config::CONFIG_VERSION; + + mConfigMask |= entry.second; } return NO_ERROR; } bool -ResourceFilter::isEmpty() const -{ - return mData.size() == 0; -} - -bool -ResourceFilter::match(int axis, const AxisValue& value) const +WeakResourceFilter::match(const ResTable_config& config) const { - if (value.intValue == 0 && (value.localeValue.language[0] == 0)) { - // they didn't specify anything so take everything - return true; - } - ssize_t index = mData.indexOfKey(axis); - if (index < 0) { - // we didn't request anything on this axis so take everything + uint32_t mask = mDefault.diff(config); + if ((mConfigMask & mask) == 0) { + // The two configurations don't have any common axis. return true; } - const SortedVector<AxisValue>& sv = mData.valueAt(index); - return sv.indexOf(value) >= 0; -} - -bool -ResourceFilter::match(int axis, const ResTable_config& config) const -{ - return match(axis, AaptGroupEntry::getConfigValueForAxis(config, axis)); -} -bool -ResourceFilter::match(const ResTable_config& config) const -{ - for (int i=AXIS_START; i<=AXIS_END; i++) { - if (!match(i, AaptGroupEntry::getConfigValueForAxis(config, i))) { - return false; + const size_t N = mConfigs.size(); + for (size_t i = 0; i < N; i++) { + const std::pair<ConfigDescription, uint32_t>& entry = mConfigs[i]; + uint32_t diff = entry.first.diff(config); + if ((diff & entry.second) == 0) { + return true; + } else if ((diff & entry.second) == ResTable_config::CONFIG_LOCALE) { + // If the locales differ, but the languages are the same and + // the locale we are matching only has a language specified, + // we match. + if (config.language[0] && memcmp(config.language, entry.first.language, sizeof(config.language)) == 0) { + if (config.country[0] == 0) { + return true; + } + } } } - return true; + return false; } -const SortedVector<AxisValue>* ResourceFilter::configsForAxis(int axis) const -{ - ssize_t index = mData.indexOfKey(axis); - if (index < 0) { - return NULL; +status_t +StrongResourceFilter::parse(const String8& str) { + Vector<String8> configStrs = AaptUtil::split(str, ','); + ConfigDescription config; + mConfigs.clear(); + for (size_t i = 0; i < configStrs.size(); i++) { + if (!AaptConfig::parse(configStrs[i], &config)) { + fprintf(stderr, "Invalid configuration: %s\n", configStrs[i].string()); + return UNKNOWN_ERROR; + } + mConfigs.insert(config); } - return &mData.valueAt(index); + return NO_ERROR; } diff --git a/tools/aapt/ResourceFilter.h b/tools/aapt/ResourceFilter.h index c57770e3f187..f459584a1af9 100644 --- a/tools/aapt/ResourceFilter.h +++ b/tools/aapt/ResourceFilter.h @@ -7,31 +7,137 @@ #ifndef RESOURCE_FILTER_H #define RESOURCE_FILTER_H +#include <androidfw/ResourceTypes.h> +#include <set> +#include <utility> +#include <utils/Errors.h> +#include <utils/String8.h> +#include <utils/StrongPointer.h> +#include <utils/Vector.h> + #include "AaptAssets.h" +#include "ConfigDescription.h" + +class ResourceFilter : public virtual android::RefBase { +public: + virtual bool match(const android::ResTable_config& config) const = 0; +}; /** * Implements logic for parsing and handling "-c" and "--preferred-configurations" * options. */ -class ResourceFilter -{ +class WeakResourceFilter : public ResourceFilter { public: - ResourceFilter() : mData(), mContainsPseudoAccented(false), - mContainsPseudoBidi(false) {} - status_t parse(const char* arg); - bool isEmpty() const; - bool match(int axis, const ResTable_config& config) const; - bool match(const ResTable_config& config) const; - const SortedVector<AxisValue>* configsForAxis(int axis) const; - inline bool containsPseudo() const { return mContainsPseudoAccented; } - inline bool containsPseudoBidi() const { return mContainsPseudoBidi; } + WeakResourceFilter() + : mContainsPseudoAccented(false) + , mContainsPseudoBidi(false) {} + + android::status_t parse(const android::String8& str); + + bool match(const android::ResTable_config& config) const; + + inline bool isEmpty() const { + return mConfigMask == 0; + } + + inline bool containsPseudo() const { + return mContainsPseudoAccented; + } + + inline bool containsPseudoBidi() const { + return mContainsPseudoBidi; + } private: - bool match(int axis, const AxisValue& value) const; + ConfigDescription mDefault; + uint32_t mConfigMask; + android::Vector<std::pair<ConfigDescription, uint32_t> > mConfigs; - KeyedVector<int,SortedVector<AxisValue> > mData; bool mContainsPseudoAccented; bool mContainsPseudoBidi; }; +/** + * Matches resources that have at least one of the configurations + * that this filter is looking for. In order to match a configuration, + * the resource must have the exact same configuration. + * + * This filter acts as a logical OR when matching resources. + * + * For example, if the filter is looking for resources with + * fr-land, de-land, or sw600dp: + * + * (PASS) fr-land + * (FAIL) fr + * (PASS) de-land + * (FAIL) de + * (FAIL) de-sw600dp + * (PASS) sw600dp + * (FAIL) sw600dp-land + */ +class StrongResourceFilter : public ResourceFilter { +public: + StrongResourceFilter() {} + StrongResourceFilter(const std::set<ConfigDescription>& configs) + : mConfigs(configs) {} + + android::status_t parse(const android::String8& str); + + bool match(const android::ResTable_config& config) const { + std::set<ConfigDescription>::const_iterator iter = mConfigs.begin(); + for (; iter != mConfigs.end(); iter++) { + if (iter->compare(config) == 0) { + return true; + } + } + return false; + } + + inline const std::set<ConfigDescription>& getConfigs() const { + return mConfigs; + } + +private: + std::set<ConfigDescription> mConfigs; +}; + +/** + * Negates the response of the target filter. + */ +class InverseResourceFilter : public ResourceFilter { +public: + InverseResourceFilter(const android::sp<ResourceFilter>& filter) + : mFilter(filter) {} + + bool match(const android::ResTable_config& config) const { + return !mFilter->match(config); + } + +private: + const android::sp<ResourceFilter> mFilter; +}; + +/** + * A logical AND of all the added filters. + */ +class AndResourceFilter : public ResourceFilter { +public: + void addFilter(const android::sp<ResourceFilter>& filter) { + mFilters.add(filter); + } + + bool match(const android::ResTable_config& config) const { + const size_t N = mFilters.size(); + for (size_t i = 0; i < N; i++) { + if (!mFilters[i]->match(config)) { + return false; + } + } + return true; + } + +private: + android::Vector<android::sp<ResourceFilter> > mFilters; +}; #endif diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 6eab65b86118..efbba40f950d 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -2095,10 +2095,10 @@ bool ResourceTable::hasResources() const { return mNumLocal > 0; } -sp<AaptFile> ResourceTable::flatten(Bundle* bundle) +sp<AaptFile> ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter) { sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8()); - status_t err = flatten(bundle, data); + status_t err = flatten(bundle, filter, data); return err == NO_ERROR ? data : NULL; } @@ -2658,8 +2658,8 @@ ResourceTable::validateLocalizations(void) } // Check that all requested localizations are present for this string - if (mBundle->getConfigurations() != NULL && mBundle->getRequireLocalization()) { - const char* allConfigs = mBundle->getConfigurations(); + if (mBundle->getConfigurations().size() > 0 && mBundle->getRequireLocalization()) { + const char* allConfigs = mBundle->getConfigurations().string(); const char* start = allConfigs; const char* comma; @@ -2713,14 +2713,8 @@ ResourceTable::validateLocalizations(void) return err; } -status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) +status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter, const sp<AaptFile>& dest) { - ResourceFilter filter; - status_t err = filter.parse(bundle->getConfigurations()); - if (err != NO_ERROR) { - return err; - } - const ConfigDescription nullConfig; const size_t N = mOrderedPackages.size(); @@ -2795,7 +2789,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) const size_t N = c->getEntries().size(); for (size_t ei=0; ei<N; ei++) { ConfigDescription config = c->getEntries().keyAt(ei); - if (filterable && !filter.match(config)) { + if (filterable && !filter->match(config)) { continue; } sp<Entry> e = c->getEntries().valueAt(ei); @@ -2887,7 +2881,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) return amt; } - err = flattenLibraryTable(data, libraryPackages); + status_t err = flattenLibraryTable(data, libraryPackages); if (err != NO_ERROR) { fprintf(stderr, "ERROR: failed to write library table\n"); return err; @@ -2943,11 +2937,11 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) } const size_t CN = cl->getEntries().size(); for (size_t ci=0; ci<CN; ci++) { - if (filterable && !filter.match(cl->getEntries().keyAt(ci))) { + if (filterable && !filter->match(cl->getEntries().keyAt(ci))) { continue; } for (size_t cj=ci+1; cj<CN; cj++) { - if (filterable && !filter.match(cl->getEntries().keyAt(cj))) { + if (filterable && !filter->match(cl->getEntries().keyAt(cj))) { continue; } typeSpecFlags[ei] |= htodl( @@ -2989,7 +2983,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) config.screenHeightDp, config.layoutDirection)); - if (filterable && !filter.match(config)) { + if (filterable && !filter->match(config)) { continue; } @@ -3108,7 +3102,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest) } ssize_t strStart = dest->getSize(); - err = valueStrings.writeStringBlock(dest); + status_t err = valueStrings.writeStringBlock(dest); if (err != NO_ERROR) { return err; } diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index ec8fd175e161..a73993c9022f 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -7,8 +7,10 @@ #ifndef RESOURCE_TABLE_H #define RESOURCE_TABLE_H +#include "ConfigDescription.h" #include "StringPool.h" #include "SourcePos.h" +#include "ResourceFilter.h" #include <set> #include <map> @@ -76,37 +78,6 @@ public: class Type; class Entry; - struct ConfigDescription : public ResTable_config { - ConfigDescription() { - memset(this, 0, sizeof(*this)); - size = sizeof(ResTable_config); - } - ConfigDescription(const ResTable_config&o) { - *static_cast<ResTable_config*>(this) = o; - size = sizeof(ResTable_config); - } - ConfigDescription(const ConfigDescription&o) { - *static_cast<ResTable_config*>(this) = o; - } - - ConfigDescription& operator=(const ResTable_config& o) { - *static_cast<ResTable_config*>(this) = o; - size = sizeof(ResTable_config); - return *this; - } - ConfigDescription& operator=(const ConfigDescription& o) { - *static_cast<ResTable_config*>(this) = o; - return *this; - } - - inline bool operator<(const ConfigDescription& o) const { return compare(o) < 0; } - inline bool operator<=(const ConfigDescription& o) const { return compare(o) <= 0; } - inline bool operator==(const ConfigDescription& o) const { return compare(o) == 0; } - inline bool operator!=(const ConfigDescription& o) const { return compare(o) != 0; } - inline bool operator>=(const ConfigDescription& o) const { return compare(o) >= 0; } - inline bool operator>(const ConfigDescription& o) const { return compare(o) > 0; } - }; - ResourceTable(Bundle* bundle, const String16& assetsPackage); status_t addIncludedResources(Bundle* bundle, const sp<AaptAssets>& assets); @@ -182,7 +153,7 @@ public: size_t numLocalResources() const; bool hasResources() const; - sp<AaptFile> flatten(Bundle*); + sp<AaptFile> flatten(Bundle* bundle, const sp<const ResourceFilter>& filter); static inline uint32_t makeResId(uint32_t packageId, uint32_t typeId, @@ -223,7 +194,7 @@ public: void addLocalization(const String16& name, const String8& locale, const SourcePos& src); status_t validateLocalizations(void); - status_t flatten(Bundle*, const sp<AaptFile>& dest); + status_t flatten(Bundle* bundle, const sp<const ResourceFilter>& filter, const sp<AaptFile>& dest); status_t flattenLibraryTable(const sp<AaptFile>& dest, const Vector<sp<Package> >& libs); void writePublicDefinitions(const String16& package, FILE* fp); diff --git a/tools/aapt/tests/AaptConfig_test.cpp b/tools/aapt/tests/AaptConfig_test.cpp new file mode 100644 index 000000000000..e795d81836bd --- /dev/null +++ b/tools/aapt/tests/AaptConfig_test.cpp @@ -0,0 +1,78 @@ +/* + * 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. + */ + +#include <utils/String8.h> +#include <gtest/gtest.h> + +#include "AaptConfig.h" +#include "ConfigDescription.h" +#include "TestHelper.h" + +using android::String8; + +static ::testing::AssertionResult TestParse(const String8& input, ConfigDescription* config=NULL) { + if (AaptConfig::parse(String8(input), config)) { + return ::testing::AssertionSuccess() << input << " was successfully parsed"; + } + return ::testing::AssertionFailure() << input << " could not be parsed"; +} + +static ::testing::AssertionResult TestParse(const char* input, ConfigDescription* config=NULL) { + return TestParse(String8(input), config); +} + +TEST(AaptConfigTest, ParseFailWhenQualifiersAreOutOfOrder) { + EXPECT_FALSE(TestParse("en-sw600dp-ldrtl")); + EXPECT_FALSE(TestParse("land-en")); + EXPECT_FALSE(TestParse("hdpi-320dpi")); +} + +TEST(AaptConfigTest, ParseFailWhenQualifiersAreNotMatched) { + EXPECT_FALSE(TestParse("en-sw600dp-ILLEGAL")); +} + +TEST(AaptConfigTest, ParseFailWhenQualifiersHaveTrailingDash) { + EXPECT_FALSE(TestParse("en-sw600dp-land-")); +} + +TEST(AaptConfigTest, ParseBasicQualifiers) { + ConfigDescription config; + EXPECT_TRUE(TestParse("", &config)); + EXPECT_EQ(String8(""), config.toString()); + + EXPECT_TRUE(TestParse("fr-land", &config)); + EXPECT_EQ(String8("fr-land"), config.toString()); + + EXPECT_TRUE(TestParse("mcc310-pl-sw720dp-normal-long-port-night-" + "xhdpi-keyssoft-qwerty-navexposed-nonav", &config)); + EXPECT_EQ(String8("mcc310-pl-sw720dp-normal-long-port-night-" + "xhdpi-keyssoft-qwerty-navexposed-nonav-v13"), config.toString()); +} + +TEST(AaptConfigTest, ParseLocales) { + ConfigDescription config; + EXPECT_TRUE(TestParse("en-rUS", &config)); + EXPECT_EQ(String8("en-US"), config.toString()); +} + +TEST(AaptConfigTest, ParseQualifierAddedInApi13) { + ConfigDescription config; + EXPECT_TRUE(TestParse("sw600dp", &config)); + EXPECT_EQ(String8("sw600dp-v13"), config.toString()); + + EXPECT_TRUE(TestParse("sw600dp-v8", &config)); + EXPECT_EQ(String8("sw600dp-v13"), config.toString()); +} diff --git a/tools/aapt/tests/AaptGroupEntry_test.cpp b/tools/aapt/tests/AaptGroupEntry_test.cpp new file mode 100644 index 000000000000..7348a08a022f --- /dev/null +++ b/tools/aapt/tests/AaptGroupEntry_test.cpp @@ -0,0 +1,54 @@ +/* + * 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. + */ + +#include <utils/String8.h> +#include <gtest/gtest.h> + +#include "AaptAssets.h" +#include "ResourceFilter.h" +#include "TestHelper.h" + +using android::String8; + +static ::testing::AssertionResult TestParse(AaptGroupEntry& entry, const String8& dirName, + String8* outType) { + if (entry.initFromDirName(dirName, outType)) { + return ::testing::AssertionSuccess() << dirName << " was successfully parsed"; + } + return ::testing::AssertionFailure() << dirName << " could not be parsed"; +} + +static ::testing::AssertionResult TestParse(AaptGroupEntry& entry, const char* input, + String8* outType) { + return TestParse(entry, String8(input), outType); +} + +TEST(AaptGroupEntryTest, ParseNoQualifier) { + AaptGroupEntry entry; + String8 type; + EXPECT_TRUE(TestParse(entry, "menu", &type)); + EXPECT_EQ(String8("menu"), type); +} + +TEST(AaptGroupEntryTest, ParseCorrectType) { + AaptGroupEntry entry; + String8 type; + EXPECT_TRUE(TestParse(entry, "anim", &type)); + EXPECT_EQ(String8("anim"), type); + + EXPECT_TRUE(TestParse(entry, "animator", &type)); + EXPECT_EQ(String8("animator"), type); +} diff --git a/tools/aapt/tests/ResourceFilter_test.cpp b/tools/aapt/tests/ResourceFilter_test.cpp new file mode 100644 index 000000000000..30697bb52125 --- /dev/null +++ b/tools/aapt/tests/ResourceFilter_test.cpp @@ -0,0 +1,128 @@ +/* + * 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. + */ + +#include <androidfw/ResourceTypes.h> +#include <utils/String8.h> +#include <gtest/gtest.h> + +#include "AaptConfig.h" +#include "ResourceFilter.h" +#include "ConfigDescription.h" + +using android::String8; + +// In this context, 'Axis' represents a particular field in the configuration, +// such as language or density. + +TEST(WeakResourceFilterTest, EmptyFilterMatchesAnything) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8(""))); + + ConfigDescription config; + config.density = 320; + + EXPECT_TRUE(filter.match(config)); + + config.language[0] = 'f'; + config.language[1] = 'r'; + + EXPECT_TRUE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, MatchesConfigWithUnrelatedAxis) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("fr"))); + + ConfigDescription config; + config.density = 320; + + EXPECT_TRUE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, MatchesConfigWithSameValueAxis) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("fr"))); + + ConfigDescription config; + config.language[0] = 'f'; + config.language[1] = 'r'; + + EXPECT_TRUE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, MatchesConfigWithSameValueAxisAndOtherUnrelatedAxis) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("fr"))); + + ConfigDescription config; + config.language[0] = 'f'; + config.language[1] = 'r'; + config.density = 320; + + EXPECT_TRUE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, DoesNotMatchConfigWithDifferentValueAxis) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("fr"))); + + ConfigDescription config; + config.language[0] = 'd'; + config.language[1] = 'e'; + + EXPECT_FALSE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, MatchesConfigWithSameLanguageButNoRegionSpecified) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("de-rDE"))); + + ConfigDescription config; + config.language[0] = 'd'; + config.language[1] = 'e'; + + EXPECT_TRUE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, ParsesStandardLocaleOnlyString) { + WeakResourceFilter filter; + EXPECT_EQ(NO_ERROR, filter.parse(String8("de_DE"))); +} + +TEST(WeakResourceFilterTest, IgnoresVersion) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("normal-v4"))); + + ConfigDescription config; + config.smallestScreenWidthDp = 600; + config.version = 13; + + // The configs don't match on any axis besides version, which should be ignored. + EXPECT_TRUE(filter.match(config)); +} + +TEST(WeakResourceFilterTest, MatchesConfigWithRegion) { + WeakResourceFilter filter; + ASSERT_EQ(NO_ERROR, filter.parse(String8("kok,kok_IN,kok_419"))); + + ConfigDescription config; + AaptLocaleValue val; + ASSERT_TRUE(val.initFromFilterString(String8("kok_IN"))); + val.writeTo(&config); + + EXPECT_TRUE(filter.match(config)); +} + diff --git a/tools/aapt/tests/TestHelper.h b/tools/aapt/tests/TestHelper.h new file mode 100644 index 000000000000..79174832a54d --- /dev/null +++ b/tools/aapt/tests/TestHelper.h @@ -0,0 +1,33 @@ +/* + * 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 __TEST_HELPER_H +#define __TEST_HELPER_H + +#include <utils/String8.h> + +namespace android { + +/** + * Stream operator for nicely printing String8's in gtest output. + */ +inline std::ostream& operator<<(std::ostream& stream, const String8& str) { + return stream << str.string(); +} + +} + +#endif diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 5af1e4ea009e..15b65c10a033 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -402,12 +402,12 @@ public class WifiManager { public static final String EXTRA_LINK_PROPERTIES = "linkProperties"; /** - * The lookup key for a {@link android.net.LinkCapabilities} object associated with the + * The lookup key for a {@link android.net.NetworkCapabilities} object associated with the * Wi-Fi network. Retrieve with * {@link android.content.Intent#getParcelableExtra(String)}. * @hide */ - public static final String EXTRA_LINK_CAPABILITIES = "linkCapabilities"; + public static final String EXTRA_NETWORK_CAPABILITIES = "networkCapabilities"; /** * The network IDs of the configured networks could have changed. diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java index 7ded1715ad05..40e664903cf3 100644 --- a/wifi/java/android/net/wifi/WifiStateTracker.java +++ b/wifi/java/android/net/wifi/WifiStateTracker.java @@ -21,7 +21,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.BaseNetworkStateTracker; -import android.net.LinkCapabilities; +import android.net.NetworkCapabilities; import android.net.LinkQualityInfo; import android.net.LinkProperties; import android.net.NetworkInfo; @@ -65,7 +65,7 @@ public class WifiStateTracker extends BaseNetworkStateTracker { public WifiStateTracker(int netType, String networkName) { mNetworkInfo = new NetworkInfo(netType, 0, networkName, ""); mLinkProperties = new LinkProperties(); - mLinkCapabilities = new LinkCapabilities(); + mNetworkCapabilities = new NetworkCapabilities(); mNetworkInfo.setIsAvailable(false); setTeardownRequested(false); @@ -183,17 +183,6 @@ public class WifiStateTracker extends BaseNetworkStateTracker { } /** - * A capability is an Integer/String pair, the capabilities - * are defined in the class LinkSocket#Key. - * - * @return a copy of this connections capabilities, may be empty but never null. - */ - @Override - public LinkCapabilities getLinkCapabilities() { - return new LinkCapabilities(mLinkCapabilities); - } - - /** * Return link info * @return an object of type WifiLinkQualityInfo */ @@ -264,10 +253,10 @@ public class WifiStateTracker extends BaseNetworkStateTracker { if (mLinkProperties == null) { mLinkProperties = new LinkProperties(); } - mLinkCapabilities = intent.getParcelableExtra( - WifiManager.EXTRA_LINK_CAPABILITIES); - if (mLinkCapabilities == null) { - mLinkCapabilities = new LinkCapabilities(); + mNetworkCapabilities = intent.getParcelableExtra( + WifiManager.EXTRA_NETWORK_CAPABILITIES); + if (mNetworkCapabilities == null) { + mNetworkCapabilities = new NetworkCapabilities(); } mWifiInfo = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO); @@ -323,4 +312,3 @@ public class WifiStateTracker extends BaseNetworkStateTracker { mSamplingDataTracker.stopSampling(s); } } - |