diff options
Diffstat (limited to 'core/java')
33 files changed, 1036 insertions, 631 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 06f79e7..0eda6b4 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -864,7 +864,17 @@ public class ActivityManager { return null; } } - + + /** @hide */ + public Bitmap getTaskTopThumbnail(int id) throws SecurityException { + try { + return ActivityManagerNative.getDefault().getTaskTopThumbnail(id); + } catch (RemoteException e) { + // System dead, we will be dead too soon! + return null; + } + } + /** * Flag for {@link #moveTaskToFront(int, int)}: also move the "home" * activity along with the task, so it is positioned immediately behind diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 773f73c..b0df660 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -498,7 +498,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM reply.writeTypedList(list); return true; } - + case GET_TASK_THUMBNAILS_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); int id = data.readInt(); @@ -512,7 +512,21 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM } return true; } - + + case GET_TASK_TOP_THUMBNAIL_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + int id = data.readInt(); + Bitmap bm = getTaskTopThumbnail(id); + reply.writeNoException(); + if (bm != null) { + reply.writeInt(1); + bm.writeToParcel(reply, 0); + } else { + reply.writeInt(0); + } + return true; + } + case GET_SERVICES_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); int maxNum = data.readInt(); @@ -1597,6 +1611,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case GET_RUNNING_USER_IDS_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + int[] result = getRunningUserIds(); + reply.writeNoException(); + reply.writeIntArray(result); + return true; + } + case REMOVE_SUB_TASK_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); @@ -2307,6 +2329,21 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); return bm; } + public Bitmap getTaskTopThumbnail(int id) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeInt(id); + mRemote.transact(GET_TASK_TOP_THUMBNAIL_TRANSACTION, data, reply, 0); + reply.readException(); + Bitmap bm = null; + if (reply.readInt() != 0) { + bm = Bitmap.CREATOR.createFromParcel(reply); + } + data.recycle(); + reply.recycle(); + return bm; + } public List getServices(int maxNum, int flags) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); @@ -3817,6 +3854,18 @@ class ActivityManagerProxy implements IActivityManager return result; } + public int[] getRunningUserIds() throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(GET_RUNNING_USER_IDS_TRANSACTION, data, reply, 0); + reply.readException(); + int[] result = reply.createIntArray(); + reply.recycle(); + data.recycle(); + return result; + } + public boolean removeSubTask(int taskId, int subTaskIndex) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 9b82f2a..67ecf5b 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -52,6 +52,7 @@ import android.os.AsyncTask; import android.os.Binder; import android.os.Bundle; import android.os.Debug; +import android.os.DropBoxManager; import android.os.Environment; import android.os.Handler; import android.os.IBinder; @@ -108,6 +109,7 @@ import java.util.Map; import java.util.TimeZone; import java.util.regex.Pattern; +import libcore.io.DropBox; import libcore.io.EventLogger; import libcore.io.IoUtils; @@ -3636,39 +3638,45 @@ public final class ActivityThread { } } - ArrayList<ComponentCallbacks2> collectComponentCallbacksLocked( + ArrayList<ComponentCallbacks2> collectComponentCallbacks( boolean allActivities, Configuration newConfig) { ArrayList<ComponentCallbacks2> callbacks = new ArrayList<ComponentCallbacks2>(); - if (mActivities.size() > 0) { - for (ActivityClientRecord ar : mActivities.values()) { - Activity a = ar.activity; - if (a != null) { - Configuration thisConfig = applyConfigCompatMainThread(mCurDefaultDisplayDpi, - newConfig, ar.packageInfo.mCompatibilityInfo.getIfNeeded()); - if (!ar.activity.mFinished && (allActivities || !ar.paused)) { - // If the activity is currently resumed, its configuration - // needs to change right now. - callbacks.add(a); - } else if (thisConfig != null) { - // Otherwise, we will tell it about the change - // the next time it is resumed or shown. Note that - // the activity manager may, before then, decide the - // activity needs to be destroyed to handle its new - // configuration. - if (DEBUG_CONFIGURATION) { - Slog.v(TAG, "Setting activity " - + ar.activityInfo.name + " newConfig=" + thisConfig); + synchronized (mPackages) { + final int N = mAllApplications.size(); + for (int i=0; i<N; i++) { + callbacks.add(mAllApplications.get(i)); + } + if (mActivities.size() > 0) { + for (ActivityClientRecord ar : mActivities.values()) { + Activity a = ar.activity; + if (a != null) { + Configuration thisConfig = applyConfigCompatMainThread(mCurDefaultDisplayDpi, + newConfig, ar.packageInfo.mCompatibilityInfo.getIfNeeded()); + if (!ar.activity.mFinished && (allActivities || !ar.paused)) { + // If the activity is currently resumed, its configuration + // needs to change right now. + callbacks.add(a); + } else if (thisConfig != null) { + // Otherwise, we will tell it about the change + // the next time it is resumed or shown. Note that + // the activity manager may, before then, decide the + // activity needs to be destroyed to handle its new + // configuration. + if (DEBUG_CONFIGURATION) { + Slog.v(TAG, "Setting activity " + + ar.activityInfo.name + " newConfig=" + thisConfig); + } + ar.newConfig = thisConfig; } - ar.newConfig = thisConfig; } } } - } - if (mServices.size() > 0) { - for (Service service : mServices.values()) { - callbacks.add(service); + if (mServices.size() > 0) { + for (Service service : mServices.values()) { + callbacks.add(service); + } } } synchronized (mProviderMap) { @@ -3678,10 +3686,6 @@ public final class ActivityThread { } } } - final int N = mAllApplications.size(); - for (int i=0; i<N; i++) { - callbacks.add(mAllApplications.get(i)); - } return callbacks; } @@ -3842,7 +3846,6 @@ public final class ActivityThread { final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) { - ArrayList<ComponentCallbacks2> callbacks = null; int configDiff = 0; synchronized (mPackages) { @@ -3873,9 +3876,10 @@ public final class ActivityThread { configDiff = mConfiguration.diff(config); mConfiguration.updateFrom(config); config = applyCompatConfiguration(mCurDefaultDisplayDpi); - callbacks = collectComponentCallbacksLocked(false, config); } - + + ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config); + // Cleanup hardware accelerated stuff WindowManagerGlobal.getInstance().trimLocalMemory(); @@ -3988,11 +3992,7 @@ public final class ActivityThread { } final void handleLowMemory() { - ArrayList<ComponentCallbacks2> callbacks; - - synchronized (mPackages) { - callbacks = collectComponentCallbacksLocked(true, null); - } + ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(true, null); final int N = callbacks.size(); for (int i=0; i<N; i++) { @@ -4020,10 +4020,7 @@ public final class ActivityThread { final WindowManagerGlobal windowManager = WindowManagerGlobal.getInstance(); windowManager.startTrimMemory(level); - ArrayList<ComponentCallbacks2> callbacks; - synchronized (mPackages) { - callbacks = collectComponentCallbacksLocked(true, null); - } + ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(true, null); final int N = callbacks.size(); for (int i = 0; i < N; i++) { @@ -4831,7 +4828,10 @@ public final class ActivityThread { "Unable to instantiate Application():" + e.toString(), e); } } - + + // add dropbox logging to libcore + DropBox.setReporter(new DropBoxReporter()); + ViewRootImpl.addConfigCallback(new ComponentCallbacks2() { public void onConfigurationChanged(Configuration newConfig) { synchronized (mPackages) { @@ -4887,6 +4887,25 @@ public final class ActivityThread { } } + private class DropBoxReporter implements DropBox.Reporter { + + private DropBoxManager dropBox; + + public DropBoxReporter() { + dropBox = (DropBoxManager) getSystemContext().getSystemService(Context.DROPBOX_SERVICE); + } + + @Override + public void addData(String tag, byte[] data, int flags) { + dropBox.addData(tag, data, flags); + } + + @Override + public void addText(String tag, String data) { + dropBox.addText(tag, data); + } + } + public static void main(String[] args) { SamplingProfilerIntegration.start(); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 2fb17b6..ed17d0e 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -104,6 +104,7 @@ public interface IActivityManager extends IInterface { public List<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) throws RemoteException; public ActivityManager.TaskThumbnails getTaskThumbnails(int taskId) throws RemoteException; + public Bitmap getTaskTopThumbnail(int taskId) throws RemoteException; public List getServices(int maxNum, int flags) throws RemoteException; public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() throws RemoteException; @@ -326,6 +327,7 @@ public interface IActivityManager extends IInterface { public int stopUser(int userid, IStopUserCallback callback) throws RemoteException; public UserInfo getCurrentUser() throws RemoteException; public boolean isUserRunning(int userid) throws RemoteException; + public int[] getRunningUserIds() throws RemoteException; public boolean removeSubTask(int taskId, int subTaskIndex) throws RemoteException; @@ -548,7 +550,7 @@ public interface IActivityManager extends IInterface { int UNBIND_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+91; int GET_UID_FOR_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+92; int HANDLE_INCOMING_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+93; - + int GET_TASK_TOP_THUMBNAIL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+94; int KILL_APPLICATION_WITH_APPID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+95; int CLOSE_SYSTEM_DIALOGS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+96; int GET_PROCESS_MEMORY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+97; @@ -610,4 +612,5 @@ public interface IActivityManager extends IInterface { int STOP_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+153; int REGISTER_USER_SWITCH_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+154; int UNREGISTER_USER_SWITCH_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+155; + int GET_RUNNING_USER_IDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+156; } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index cb83dc2..7896450 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1923,6 +1923,7 @@ public class Notification implements Parcelable contentView.setViewVisibility(rowId, View.GONE); } + int i=0; while (i < mTexts.size() && i < rowIds.length) { CharSequence str = mTexts.get(i); @@ -1933,11 +1934,11 @@ public class Notification implements Parcelable i++; } - if (mTexts.size() > rowIds.length) { - contentView.setViewVisibility(R.id.inbox_more, View.VISIBLE); - } else { - contentView.setViewVisibility(R.id.inbox_more, View.GONE); - } + contentView.setViewVisibility(R.id.inbox_end_pad, + mTexts.size() > 0 ? View.VISIBLE : View.GONE); + + contentView.setViewVisibility(R.id.inbox_more, + mTexts.size() > rowIds.length ? View.VISIBLE : View.GONE); return contentView; } diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java index 0f6488a..5986dcd 100644 --- a/core/java/android/content/ContentService.java +++ b/core/java/android/content/ContentService.java @@ -154,11 +154,15 @@ public final class ContentService extends IContentService.Stub { throw new IllegalArgumentException("You must pass a valid uri and observer"); } + // STOPSHIP: disable the multi-user permission checks until a solid fix for the + // content provider / observer case is in place. + /* final int callingUser = UserHandle.getCallingUserId(); if (callingUser != userHandle) { mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, "no permission to observe other users' provider view"); } + */ if (userHandle < 0) { if (userHandle == UserHandle.USER_CURRENT) { diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index e8507bc..b9518b8 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1254,7 +1254,9 @@ public class Intent implements Parcelable, Cloneable { * Activity Action: Launch application installer. * <p> * Input: The data must be a content: or file: URI at which the application - * can be retrieved. You can optionally supply + * can be retrieved. As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, + * you can also use "package:<package-name>" to install an application for the + * current user that is already installed for another user. You can optionally supply * {@link #EXTRA_INSTALLER_PACKAGE_NAME}, {@link #EXTRA_NOT_UNKNOWN_SOURCE}, * {@link #EXTRA_ALLOW_REPLACE}, and {@link #EXTRA_RETURN_RESULT}. * <p> @@ -2795,6 +2797,15 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED"; /** + * @hide + * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED} + * intents to indicate that at this point the package has been removed for + * all users on the device. + */ + public static final String EXTRA_REMOVED_FOR_ALL_USERS + = "android.intent.extra.REMOVED_FOR_ALL_USERS"; + + /** * Used as a boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED} * intents to indicate that this is a replacement of the package, so this * broadcast will immediately be followed by an add broadcast for a diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index b316f23..492e5e9 100755 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1554,6 +1554,8 @@ public class Resources { public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics, CompatibilityInfo compat) { if (mSystem != null) { + // TODO: Remove once b/7094175 is fixed + Slog.v(TAG, "updateSystemConfiguration: b/7094175 config=" + config); mSystem.updateConfiguration(config, metrics, compat); //Log.i(TAG, "Updated system resources " + mSystem // + ": " + mSystem.getConfiguration()); diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 4347e75..58a0f13 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -160,6 +160,10 @@ public final class DisplayManager { /** * Connects to a Wifi display. * The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast. + * <p> + * Automatically remembers the display after a successful connection, if not + * already remembered. + * </p> * * @param deviceAddress The MAC address of the device to which we should connect. * @hide @@ -178,6 +182,36 @@ public final class DisplayManager { } /** + * Renames a Wifi display. + * <p> + * The display must already be remembered for this call to succeed. In other words, + * we must already have successfully connected to the display at least once and then + * not forgotten it. + * </p> + * + * @param deviceAddress The MAC address of the device to rename. + * @param alias The alias name by which to remember the device, or null + * or empty if no alias should be used. + * @hide + */ + public void renameWifiDisplay(String deviceAddress, String alias) { + mGlobal.renameWifiDisplay(deviceAddress, alias); + } + + /** + * Forgets a previously remembered Wifi display. + * <p> + * Automatically disconnects from the display if currently connected to it. + * </p> + * + * @param deviceAddress The MAC address of the device to forget. + * @hide + */ + public void forgetWifiDisplay(String deviceAddress) { + mGlobal.forgetWifiDisplay(deviceAddress); + } + + /** * Gets the current Wifi display status. * Watch for changes in the status by registering a broadcast receiver for * {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED}. diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 14b5440..a858681 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -281,6 +281,31 @@ public final class DisplayManagerGlobal { } } + public void renameWifiDisplay(String deviceAddress, String alias) { + if (deviceAddress == null) { + throw new IllegalArgumentException("deviceAddress must not be null"); + } + + try { + mDm.renameWifiDisplay(deviceAddress, alias); + } catch (RemoteException ex) { + Log.e(TAG, "Failed to rename Wifi display " + deviceAddress + + " with alias " + alias + ".", ex); + } + } + + public void forgetWifiDisplay(String deviceAddress) { + if (deviceAddress == null) { + throw new IllegalArgumentException("deviceAddress must not be null"); + } + + try { + mDm.forgetWifiDisplay(deviceAddress); + } catch (RemoteException ex) { + Log.e(TAG, "Failed to forget Wifi display.", ex); + } + } + public WifiDisplayStatus getWifiDisplayStatus() { try { return mDm.getWifiDisplayStatus(); diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 36a9a7f..4b6fb53 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -38,5 +38,11 @@ interface IDisplayManager { void disconnectWifiDisplay(); // Requires CONFIGURE_WIFI_DISPLAY permission. + void renameWifiDisplay(String address, String alias); + + // Requires CONFIGURE_WIFI_DISPLAY permission. + void forgetWifiDisplay(String address); + + // Requires CONFIGURE_WIFI_DISPLAY permission. WifiDisplayStatus getWifiDisplayStatus(); } diff --git a/core/java/android/hardware/display/WifiDisplay.java b/core/java/android/hardware/display/WifiDisplay.java index e51e97e..0138b1c 100644 --- a/core/java/android/hardware/display/WifiDisplay.java +++ b/core/java/android/hardware/display/WifiDisplay.java @@ -19,6 +19,8 @@ package android.hardware.display; import android.os.Parcel; import android.os.Parcelable; +import libcore.util.Objects; + /** * Describes the properties of a Wifi display. * <p> @@ -30,6 +32,7 @@ import android.os.Parcelable; public final class WifiDisplay implements Parcelable { private final String mDeviceAddress; private final String mDeviceName; + private final String mDeviceAlias; public static final WifiDisplay[] EMPTY_ARRAY = new WifiDisplay[0]; @@ -37,7 +40,8 @@ public final class WifiDisplay implements Parcelable { public WifiDisplay createFromParcel(Parcel in) { String deviceAddress = in.readString(); String deviceName = in.readString(); - return new WifiDisplay(deviceAddress, deviceName); + String deviceAlias = in.readString(); + return new WifiDisplay(deviceAddress, deviceName, deviceAlias); } public WifiDisplay[] newArray(int size) { @@ -45,7 +49,7 @@ public final class WifiDisplay implements Parcelable { } }; - public WifiDisplay(String deviceAddress, String deviceName) { + public WifiDisplay(String deviceAddress, String deviceName, String deviceAlias) { if (deviceAddress == null) { throw new IllegalArgumentException("deviceAddress must not be null"); } @@ -55,6 +59,7 @@ public final class WifiDisplay implements Parcelable { mDeviceAddress = deviceAddress; mDeviceName = deviceName; + mDeviceAlias = deviceAlias; } /** @@ -71,6 +76,25 @@ public final class WifiDisplay implements Parcelable { return mDeviceName; } + /** + * Gets the user-specified alias of the Wifi display device, or null if none. + * <p> + * The alias should be used in the UI whenever available. It is the value + * provided by the user when renaming the device. + * </p> + */ + public String getDeviceAlias() { + return mDeviceAlias; + } + + /** + * Gets the name to show in the UI. + * Uses the device alias if available, otherwise uses the device name. + */ + public String getFriendlyDisplayName() { + return mDeviceAlias != null ? mDeviceAlias : mDeviceName; + } + @Override public boolean equals(Object o) { return o instanceof WifiDisplay && equals((WifiDisplay)o); @@ -79,7 +103,8 @@ public final class WifiDisplay implements Parcelable { public boolean equals(WifiDisplay other) { return other != null && mDeviceAddress.equals(other.mDeviceAddress) - && mDeviceName.equals(other.mDeviceName); + && mDeviceName.equals(other.mDeviceName) + && Objects.equal(mDeviceAlias, other.mDeviceAlias); } @Override @@ -92,6 +117,7 @@ public final class WifiDisplay implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeString(mDeviceAddress); dest.writeString(mDeviceName); + dest.writeString(mDeviceAlias); } @Override @@ -102,6 +128,10 @@ public final class WifiDisplay implements Parcelable { // For debugging purposes only. @Override public String toString() { - return mDeviceName + " (" + mDeviceAddress + ")"; + String result = mDeviceName + " (" + mDeviceAddress + ")"; + if (mDeviceAlias != null) { + result += ", alias " + mDeviceAlias; + } + return result; } } diff --git a/core/java/android/hardware/display/WifiDisplayStatus.java b/core/java/android/hardware/display/WifiDisplayStatus.java index d5fe45d..f7e72c4 100644 --- a/core/java/android/hardware/display/WifiDisplayStatus.java +++ b/core/java/android/hardware/display/WifiDisplayStatus.java @@ -23,7 +23,7 @@ import java.util.Arrays; /** * Describes the current global state of Wifi display connectivity, including the - * currently connected display and all known displays. + * currently connected display and all available or remembered displays. * <p> * This object is immutable. * </p> @@ -31,22 +31,37 @@ import java.util.Arrays; * @hide */ public final class WifiDisplayStatus implements Parcelable { - private final boolean mEnabled; + private final int mFeatureState; private final int mScanState; private final int mActiveDisplayState; private final WifiDisplay mActiveDisplay; - private final WifiDisplay[] mKnownDisplays; - + private final WifiDisplay[] mAvailableDisplays; + private final WifiDisplay[] mRememberedDisplays; + + /** Feature state: Wifi display is not available on this device. */ + public static final int FEATURE_STATE_UNAVAILABLE = 0; + /** Feature state: Wifi display is disabled, probably because Wifi is disabled. */ + public static final int FEATURE_STATE_DISABLED = 1; + /** Feature state: Wifi display is turned off in settings. */ + public static final int FEATURE_STATE_OFF = 2; + /** Feature state: Wifi display is turned on in settings. */ + public static final int FEATURE_STATE_ON = 3; + + /** Scan state: Not currently scanning. */ public static final int SCAN_STATE_NOT_SCANNING = 0; + /** Scan state: Currently scanning. */ public static final int SCAN_STATE_SCANNING = 1; + /** Display state: Not connected. */ public static final int DISPLAY_STATE_NOT_CONNECTED = 0; + /** Display state: Connecting to active display. */ public static final int DISPLAY_STATE_CONNECTING = 1; + /** Display state: Connected to active display. */ public static final int DISPLAY_STATE_CONNECTED = 2; public static final Creator<WifiDisplayStatus> CREATOR = new Creator<WifiDisplayStatus>() { public WifiDisplayStatus createFromParcel(Parcel in) { - boolean enabled = (in.readInt() != 0); + int featureState = in.readInt(); int scanState = in.readInt(); int activeDisplayState= in.readInt(); @@ -55,13 +70,18 @@ public final class WifiDisplayStatus implements Parcelable { activeDisplay = WifiDisplay.CREATOR.createFromParcel(in); } - WifiDisplay[] knownDisplays = WifiDisplay.CREATOR.newArray(in.readInt()); - for (int i = 0; i < knownDisplays.length; i++) { - knownDisplays[i] = WifiDisplay.CREATOR.createFromParcel(in); + WifiDisplay[] availableDisplays = WifiDisplay.CREATOR.newArray(in.readInt()); + for (int i = 0; i < availableDisplays.length; i++) { + availableDisplays[i] = WifiDisplay.CREATOR.createFromParcel(in); + } + + WifiDisplay[] rememberedDisplays = WifiDisplay.CREATOR.newArray(in.readInt()); + for (int i = 0; i < rememberedDisplays.length; i++) { + rememberedDisplays[i] = WifiDisplay.CREATOR.createFromParcel(in); } - return new WifiDisplayStatus(enabled, scanState, activeDisplayState, - activeDisplay, knownDisplays); + return new WifiDisplayStatus(featureState, scanState, activeDisplayState, + activeDisplay, availableDisplays, rememberedDisplays); } public WifiDisplayStatus[] newArray(int size) { @@ -70,33 +90,38 @@ public final class WifiDisplayStatus implements Parcelable { }; public WifiDisplayStatus() { - this(false, SCAN_STATE_NOT_SCANNING, DISPLAY_STATE_NOT_CONNECTED, - null, WifiDisplay.EMPTY_ARRAY); + this(FEATURE_STATE_UNAVAILABLE, SCAN_STATE_NOT_SCANNING, DISPLAY_STATE_NOT_CONNECTED, + null, WifiDisplay.EMPTY_ARRAY, WifiDisplay.EMPTY_ARRAY); } - public WifiDisplayStatus(boolean enabled, int scanState, int activeDisplayState, - WifiDisplay activeDisplay, WifiDisplay[] knownDisplays) { - if (knownDisplays == null) { - throw new IllegalArgumentException("knownDisplays must not be null"); + public WifiDisplayStatus(int featureState, int scanState, + int activeDisplayState, WifiDisplay activeDisplay, + WifiDisplay[] availableDisplays, WifiDisplay[] rememberedDisplays) { + if (availableDisplays == null) { + throw new IllegalArgumentException("availableDisplays must not be null"); + } + if (rememberedDisplays == null) { + throw new IllegalArgumentException("rememberedDisplays must not be null"); } - mEnabled = enabled; + mFeatureState = featureState; mScanState = scanState; mActiveDisplayState = activeDisplayState; mActiveDisplay = activeDisplay; - mKnownDisplays = knownDisplays; + mAvailableDisplays = availableDisplays; + mRememberedDisplays = rememberedDisplays; } /** - * Returns true if the Wifi display feature is enabled and available for use. + * Returns the state of the Wifi display feature on this device. * <p> - * The value of this property reflects whether Wifi and Wifi P2P functions - * are enabled. Enablement is not directly controllable by the user at this - * time, except indirectly such as by turning off Wifi altogether. + * The value of this property reflects whether the device supports the Wifi display, + * whether it has been enabled by the user and whether the prerequisites for + * connecting to displays have been met. * </p> */ - public boolean isEnabled() { - return mEnabled; + public int getFeatureState() { + return mFeatureState; } /** @@ -127,15 +152,29 @@ public final class WifiDisplayStatus implements Parcelable { } /** - * Gets the list of all known Wifi displays, never null. + * Gets the list of all available Wifi displays as reported by the most recent + * scan, never null. + * <p> + * Some of these displays may already be remembered, others may be unknown. + * </p> */ - public WifiDisplay[] getKnownDisplays() { - return mKnownDisplays; + public WifiDisplay[] getAvailableDisplays() { + return mAvailableDisplays; + } + + /** + * Gets the list of all remembered Wifi displays, never null. + * <p> + * Not all remembered displays will necessarily be available. + * </p> + */ + public WifiDisplay[] getRememberedDisplays() { + return mRememberedDisplays; } @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mEnabled ? 1 : 0); + dest.writeInt(mFeatureState); dest.writeInt(mScanState); dest.writeInt(mActiveDisplayState); @@ -146,8 +185,13 @@ public final class WifiDisplayStatus implements Parcelable { dest.writeInt(0); } - dest.writeInt(mKnownDisplays.length); - for (WifiDisplay display : mKnownDisplays) { + dest.writeInt(mAvailableDisplays.length); + for (WifiDisplay display : mAvailableDisplays) { + display.writeToParcel(dest, flags); + } + + dest.writeInt(mRememberedDisplays.length); + for (WifiDisplay display : mRememberedDisplays) { display.writeToParcel(dest, flags); } } @@ -160,11 +204,12 @@ public final class WifiDisplayStatus implements Parcelable { // For debugging purposes only. @Override public String toString() { - return "WifiDisplayStatus{enabled=" + mEnabled + return "WifiDisplayStatus{featureState=" + mFeatureState + ", scanState=" + mScanState + ", activeDisplayState=" + mActiveDisplayState + ", activeDisplay=" + mActiveDisplay - + ", knownDisplays=" + Arrays.toString(mKnownDisplays) + + ", availableDisplays=" + Arrays.toString(mAvailableDisplays) + + ", rememberedDisplays=" + Arrays.toString(mRememberedDisplays) + "}"; } } diff --git a/core/java/android/net/CaptivePortalTracker.java b/core/java/android/net/CaptivePortalTracker.java index 8218e37..24dc898 100644 --- a/core/java/android/net/CaptivePortalTracker.java +++ b/core/java/android/net/CaptivePortalTracker.java @@ -16,6 +16,7 @@ package android.net; +import android.app.Activity; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -24,33 +25,32 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; -import android.database.ContentObserver; import android.net.ConnectivityManager; import android.net.IConnectivityManager; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; +import android.os.UserHandle; import android.os.Message; import android.os.RemoteException; import android.provider.Settings; import android.util.Log; +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; + import java.io.IOException; import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.Inet4Address; import java.net.URL; import java.net.UnknownHostException; -import java.util.concurrent.atomic.AtomicBoolean; import com.android.internal.R; /** - * This class allows captive portal detection + * This class allows captive portal detection on a network. * @hide */ -public class CaptivePortalTracker { - private static final boolean DBG = true; +public class CaptivePortalTracker extends StateMachine { + private static final boolean DBG = false; private static final String TAG = "CaptivePortalTracker"; private static final String DEFAULT_SERVER = "clients3.google.com"; @@ -62,37 +62,31 @@ public class CaptivePortalTracker { private String mUrl; private boolean mNotificationShown = false; private boolean mIsCaptivePortalCheckEnabled = false; - private InternalHandler mHandler; private IConnectivityManager mConnService; private Context mContext; private NetworkInfo mNetworkInfo; - private boolean mIsCaptivePortal = false; - private static final int DETECT_PORTAL = 0; - private static final int HANDLE_CONNECT = 1; + private static final int CMD_DETECT_PORTAL = 0; + private static final int CMD_CONNECTIVITY_CHANGE = 1; + private static final int CMD_DELAYED_CAPTIVE_CHECK = 2; - /** - * Activity Action: Switch to the captive portal network - * <p>Input: Nothing. - * <p>Output: Nothing. - */ - public static final String ACTION_SWITCH_TO_CAPTIVE_PORTAL - = "android.net.SWITCH_TO_CAPTIVE_PORTAL"; + /* This delay happens every time before we do a captive check on a network */ + private static final int DELAYED_CHECK_INTERVAL_MS = 10000; + private int mDelayedCheckToken = 0; + + private State mDefaultState = new DefaultState(); + private State mNoActiveNetworkState = new NoActiveNetworkState(); + private State mActiveNetworkState = new ActiveNetworkState(); + private State mDelayedCaptiveCheckState = new DelayedCaptiveCheckState(); + + private CaptivePortalTracker(Context context, IConnectivityManager cs) { + super(TAG); - private CaptivePortalTracker(Context context, NetworkInfo info, IConnectivityManager cs) { mContext = context; - mNetworkInfo = info; mConnService = cs; - HandlerThread handlerThread = new HandlerThread("CaptivePortalThread"); - handlerThread.start(); - mHandler = new InternalHandler(handlerThread.getLooper()); - mHandler.obtainMessage(DETECT_PORTAL).sendToTarget(); - IntentFilter filter = new IntentFilter(); - filter.addAction(ACTION_SWITCH_TO_CAPTIVE_PORTAL); filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); - mContext.registerReceiver(mReceiver, filter); mServer = Settings.Secure.getString(mContext.getContentResolver(), @@ -101,100 +95,180 @@ public class CaptivePortalTracker { mIsCaptivePortalCheckEnabled = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1; + + addState(mDefaultState); + addState(mNoActiveNetworkState, mDefaultState); + addState(mActiveNetworkState, mDefaultState); + addState(mDelayedCaptiveCheckState, mActiveNetworkState); + setInitialState(mNoActiveNetworkState); } private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - if (action.equals(ACTION_SWITCH_TO_CAPTIVE_PORTAL)) { - notifyPortalCheckComplete(); - } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { + if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { NetworkInfo info = intent.getParcelableExtra( ConnectivityManager.EXTRA_NETWORK_INFO); - mHandler.obtainMessage(HANDLE_CONNECT, info).sendToTarget(); + sendMessage(obtainMessage(CMD_CONNECTIVITY_CHANGE, info)); } } }; - public static CaptivePortalTracker detect(Context context, NetworkInfo info, + public static CaptivePortalTracker makeCaptivePortalTracker(Context context, IConnectivityManager cs) { - CaptivePortalTracker captivePortal = new CaptivePortalTracker(context, info, cs); + CaptivePortalTracker captivePortal = new CaptivePortalTracker(context, cs); + captivePortal.start(); return captivePortal; } - private class InternalHandler extends Handler { - public InternalHandler(Looper looper) { - super(looper); + public void detectCaptivePortal(NetworkInfo info) { + sendMessage(obtainMessage(CMD_DETECT_PORTAL, info)); + } + + private class DefaultState extends State { + @Override + public void enter() { + if (DBG) log(getName() + "\n"); } @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case DETECT_PORTAL: - InetAddress server = lookupHost(mServer); - if (server != null) { - requestRouteToHost(server); - if (isCaptivePortal(server)) { - if (DBG) log("Captive portal " + mNetworkInfo); - setNotificationVisible(true); - mIsCaptivePortal = true; - break; - } - } - notifyPortalCheckComplete(); - quit(); + public boolean processMessage(Message message) { + if (DBG) log(getName() + message.toString() + "\n"); + switch (message.what) { + case CMD_DETECT_PORTAL: + NetworkInfo info = (NetworkInfo) message.obj; + // Checking on a secondary connection is not supported + // yet + notifyPortalCheckComplete(info); break; - case HANDLE_CONNECT: - NetworkInfo info = (NetworkInfo) msg.obj; - if (info.getType() != mNetworkInfo.getType()) break; + case CMD_CONNECTIVITY_CHANGE: + case CMD_DELAYED_CAPTIVE_CHECK: + break; + default: + loge("Ignoring " + message); + break; + } + return HANDLED; + } + } - if (info.getState() == NetworkInfo.State.CONNECTED || - info.getState() == NetworkInfo.State.DISCONNECTED) { - setNotificationVisible(false); - } + private class NoActiveNetworkState extends State { + @Override + public void enter() { + if (DBG) log(getName() + "\n"); + mNetworkInfo = null; + /* Clear any previous notification */ + setNotificationVisible(false); + } - /* Connected to a captive portal */ - if (info.getState() == NetworkInfo.State.CONNECTED && - mIsCaptivePortal) { - launchBrowser(); - quit(); + @Override + public boolean processMessage(Message message) { + if (DBG) log(getName() + message.toString() + "\n"); + InetAddress server; + NetworkInfo info; + switch (message.what) { + case CMD_CONNECTIVITY_CHANGE: + info = (NetworkInfo) message.obj; + if (info.isConnected() && isActiveNetwork(info)) { + mNetworkInfo = info; + transitionTo(mDelayedCaptiveCheckState); } break; default: - loge("Unhandled message " + msg); - break; + return NOT_HANDLED; } + return HANDLED; + } + } + + private class ActiveNetworkState extends State { + @Override + public void enter() { + if (DBG) log(getName() + "\n"); } - private void quit() { - mIsCaptivePortal = false; - getLooper().quit(); - mContext.unregisterReceiver(mReceiver); + @Override + public boolean processMessage(Message message) { + NetworkInfo info; + switch (message.what) { + case CMD_CONNECTIVITY_CHANGE: + info = (NetworkInfo) message.obj; + if (!info.isConnected() + && info.getType() == mNetworkInfo.getType()) { + if (DBG) log("Disconnected from active network " + info); + transitionTo(mNoActiveNetworkState); + } else if (info.getType() != mNetworkInfo.getType() && + info.isConnected() && + isActiveNetwork(info)) { + if (DBG) log("Active network switched " + info); + deferMessage(message); + transitionTo(mNoActiveNetworkState); + } + break; + default: + return NOT_HANDLED; + } + return HANDLED; } } - private void launchBrowser() { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(mUrl)); - intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK); - mContext.startActivity(intent); + + + private class DelayedCaptiveCheckState extends State { + @Override + public void enter() { + if (DBG) log(getName() + "\n"); + sendMessageDelayed(obtainMessage(CMD_DELAYED_CAPTIVE_CHECK, + ++mDelayedCheckToken, 0), DELAYED_CHECK_INTERVAL_MS); + } + + @Override + public boolean processMessage(Message message) { + if (DBG) log(getName() + message.toString() + "\n"); + switch (message.what) { + case CMD_DELAYED_CAPTIVE_CHECK: + if (message.arg1 == mDelayedCheckToken) { + InetAddress server = lookupHost(mServer); + if (server != null) { + if (isCaptivePortal(server)) { + if (DBG) log("Captive network " + mNetworkInfo); + setNotificationVisible(true); + } + } + if (DBG) log("Not captive network " + mNetworkInfo); + transitionTo(mActiveNetworkState); + } + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } } - private void notifyPortalCheckComplete() { + private void notifyPortalCheckComplete(NetworkInfo info) { + if (info == null) { + loge("notifyPortalCheckComplete on null"); + return; + } try { - mConnService.captivePortalCheckComplete(mNetworkInfo); + mConnService.captivePortalCheckComplete(info); } catch(RemoteException e) { e.printStackTrace(); } } - private void requestRouteToHost(InetAddress server) { + private boolean isActiveNetwork(NetworkInfo info) { try { - mConnService.requestRouteToHostAddress(mNetworkInfo.getType(), - server.getAddress()); + NetworkInfo active = mConnService.getActiveNetworkInfo(); + if (active != null && active.getType() == info.getType()) { + return true; + } } catch (RemoteException e) { e.printStackTrace(); } + return false; } /** @@ -205,6 +279,7 @@ public class CaptivePortalTracker { if (!mIsCaptivePortalCheckEnabled) return false; mUrl = "http://" + server.getHostAddress() + "/generate_204"; + if (DBG) log("Checking " + mUrl); try { URL url = new URL(mUrl); urlConnection = (HttpURLConnection) url.openConnection(); @@ -250,7 +325,12 @@ public class CaptivePortalTracker { .getSystemService(Context.NOTIFICATION_SERVICE); if (visible) { - CharSequence title = r.getString(R.string.wifi_available_sign_in, 0); + CharSequence title; + if (mNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI) { + title = r.getString(R.string.wifi_available_sign_in, 0); + } else { + title = r.getString(R.string.network_available_sign_in, 0); + } CharSequence details = r.getString(R.string.network_available_sign_in_detailed, mNetworkInfo.getExtraInfo()); @@ -258,9 +338,10 @@ public class CaptivePortalTracker { notification.when = 0; notification.icon = com.android.internal.R.drawable.stat_notify_wifi_in_range; notification.flags = Notification.FLAG_AUTO_CANCEL; - notification.contentIntent = PendingIntent.getBroadcast(mContext, 0, - new Intent(CaptivePortalTracker.ACTION_SWITCH_TO_CAPTIVE_PORTAL), 0); - + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(mUrl)); + intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | + Intent.FLAG_ACTIVITY_NEW_TASK); + notification.contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0); notification.tickerText = title; notification.setLatestEventInfo(mContext, title, details, notification.contentIntent); diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index fb7a4f8..446bbf0 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -21,6 +21,7 @@ import android.os.Parcelable; import android.os.SystemClock; import android.util.SparseBooleanArray; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.Objects; import java.io.CharArrayWriter; @@ -608,13 +609,13 @@ public class NetworkStats implements Parcelable { * Return all rows except those attributed to the requested UID; doesn't * mutate the original structure. */ - public NetworkStats withoutUid(int uid) { + public NetworkStats withoutUids(int[] uids) { final NetworkStats stats = new NetworkStats(elapsedRealtime, 10); Entry entry = new Entry(); for (int i = 0; i < size; i++) { entry = getValues(i, entry); - if (entry.uid != uid) { + if (!ArrayUtils.contains(uids, entry.uid)) { stats.addValues(entry); } } diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 1607b96..364004b 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -32,6 +32,7 @@ public class Environment { private static final String ENV_EXTERNAL_STORAGE = "EXTERNAL_STORAGE"; private static final String ENV_EMULATED_STORAGE_TARGET = "EMULATED_STORAGE_TARGET"; + private static final String ENV_MEDIA_STORAGE = "MEDIA_STORAGE"; /** {@hide} */ public static String DIRECTORY_ANDROID = "Android"; @@ -88,21 +89,30 @@ public class Environment { private final File mExternalStorageAndroidData; private final File mExternalStorageAndroidMedia; private final File mExternalStorageAndroidObb; + private final File mMediaStorage; public UserEnvironment(int userId) { // See storage config details at http://source.android.com/tech/storage/ String rawExternalStorage = System.getenv(ENV_EXTERNAL_STORAGE); String rawEmulatedStorageTarget = System.getenv(ENV_EMULATED_STORAGE_TARGET); + String rawMediaStorage = System.getenv(ENV_MEDIA_STORAGE); + if (TextUtils.isEmpty(rawMediaStorage)) { + rawMediaStorage = "/data/media"; + } if (!TextUtils.isEmpty(rawEmulatedStorageTarget)) { // Device has emulated storage; external storage paths should have // userId burned into them. + final String rawUserId = Integer.toString(userId); final File emulatedBase = new File(rawEmulatedStorageTarget); + final File mediaBase = new File(rawMediaStorage); // /storage/emulated/0 - mExternalStorage = buildPath(emulatedBase, Integer.toString(userId)); + mExternalStorage = buildPath(emulatedBase, rawUserId); // /storage/emulated/obb mExternalStorageAndroidObb = buildPath(emulatedBase, "obb"); + // /data/media/0 + mMediaStorage = buildPath(mediaBase, rawUserId); } else { // Device has physical external storage; use plain paths. @@ -115,6 +125,8 @@ public class Environment { mExternalStorage = new File(rawExternalStorage); // /storage/sdcard0/Android/obb mExternalStorageAndroidObb = buildPath(mExternalStorage, DIRECTORY_ANDROID, "obb"); + // /data/media + mMediaStorage = new File(rawMediaStorage); } mExternalStorageAndroidData = buildPath(mExternalStorage, DIRECTORY_ANDROID, "data"); @@ -152,6 +164,10 @@ public class Environment { public File getExternalStorageAppCacheDirectory(String packageName) { return new File(new File(mExternalStorageAndroidData, packageName), "cache"); } + + public File getMediaStorageDirectory() { + return mMediaStorage; + } } /** @@ -198,7 +214,8 @@ public class Environment { * @hide */ public static File getMediaStorageDirectory() { - return MEDIA_STORAGE_DIRECTORY; + throwIfSystem(); + return sCurrentUser.getMediaStorageDirectory(); } /** @@ -231,10 +248,6 @@ public class Environment { private static final File SECURE_DATA_DIRECTORY = getDirectory("ANDROID_SECURE_DATA", "/data/secure"); - /** @hide */ - private static final File MEDIA_STORAGE_DIRECTORY - = getDirectory("MEDIA_STORAGE", "/data/media"); - private static final File DOWNLOAD_CACHE_DIRECTORY = getDirectory("DOWNLOAD_CACHE", "/cache"); /** diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 44b0b62..b532966 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -44,7 +44,7 @@ public class UserManager { * @return true if multiple users can be created, false if it is a single user device. * @hide */ - public boolean supportsMultipleUsers() { + public static boolean supportsMultipleUsers() { return getMaxSupportedUsers() > 1; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 68ce72f..cc9abff 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -214,6 +214,21 @@ public final class Settings { "android.settings.BLUETOOTH_SETTINGS"; /** + * Activity Action: Show settings to allow configuration of Wifi Displays. + * <p> + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_WIFI_DISPLAY_SETTINGS = + "android.settings.WIFI_DISPLAY_SETTINGS"; + + /** * Activity Action: Show settings to allow configuration of date and time. * <p> * In some cases, a matching Activity may not exist, so ensure you @@ -919,6 +934,7 @@ public final class Settings { MOVED_TO_GLOBAL.add(Settings.Global.POWER_SOUNDS_ENABLED); MOVED_TO_GLOBAL.add(Settings.Global.STAY_ON_WHILE_PLUGGED_IN); MOVED_TO_GLOBAL.add(Settings.Global.WIFI_SLEEP_POLICY); + MOVED_TO_GLOBAL.add(Settings.Global.MODE_RINGER); } private static void lazyInitCache() { @@ -1338,7 +1354,7 @@ public final class Settings { /** @deprecated - Do not use */ @Deprecated public static void setShowGTalkServiceStatus(ContentResolver cr, boolean flag) { - /* intentionally empty */ + setShowGTalkServiceStatusForUser(cr, flag, UserHandle.myUserId()); } /** @@ -1482,6 +1498,12 @@ public final class Settings { @Deprecated public static final int WIFI_SLEEP_POLICY_NEVER = Global.WIFI_SLEEP_POLICY_NEVER; + /** + * @deprecated Use {@link android.provider.Settings.Global#MODE_RINGER} instead + */ + @Deprecated + public static final String MODE_RINGER = Global.MODE_RINGER; + //TODO: deprecate static IP constants /** * Whether to use static IP and other static network attributes. @@ -1651,13 +1673,6 @@ public final class Settings { public static final String ALWAYS_FINISH_ACTIVITIES = "always_finish_activities"; - - /** - * Ringer mode. This is used internally, changing this value will not - * change the ringer mode. See AudioManager. - */ - public static final String MODE_RINGER = "mode_ringer"; - /** * Determines which streams are affected by ringer mode changes. The * stream type's bit should be set to 1 if it should be muted when going @@ -5540,6 +5555,13 @@ public final class Settings { "web_autofill_query_url"; /** + * Whether Wifi display is enabled/disabled + * 0=disabled. 1=enabled. + * @hide + */ + public static final String WIFI_DISPLAY_ON = "wifi_display_on"; + + /** * Whether to notify the user of open networks. * <p> * If not connected and the scan results have an open network, we will @@ -5716,7 +5738,11 @@ public final class Settings { */ public static final String WTF_IS_FATAL = "wtf_is_fatal"; - + /** + * Ringer mode. This is used internally, changing this value will not + * change the ringer mode. See AudioManager. + */ + public static final String MODE_RINGER = "mode_ringer"; // Populated lazily, guarded by class object: diff --git a/core/java/android/service/dreams/Dream.java b/core/java/android/service/dreams/Dream.java index ba2ac67..4a23d39 100644 --- a/core/java/android/service/dreams/Dream.java +++ b/core/java/android/service/dreams/Dream.java @@ -22,7 +22,6 @@ import android.content.Intent; import android.graphics.drawable.ColorDrawable; import android.os.Handler; import android.os.IBinder; -import android.os.RemoteException; import android.os.ServiceManager; import android.util.Slog; import android.view.ActionMode; @@ -40,12 +39,16 @@ import android.view.accessibility.AccessibilityEvent; import com.android.internal.policy.PolicyManager; /** - * Extend this class to implement a custom screensaver. + * Extend this class to implement a custom Dream. + * + * <p>Dreams are interactive screensavers launched when a charging device is idle, or docked in a + * desk dock. Dreams provide another modality for apps to express themselves, tailored for + * an exhibition/lean-back experience.</p> */ public class Dream extends Service implements Window.Callback { private final static boolean DEBUG = true; - private final static String TAG = "Dream"; - + private final String TAG = Dream.class.getSimpleName() + "[" + getClass().getSimpleName() + "]"; + /** * The {@link Intent} that must be declared as handled by the service. * To be supported, the service must also require the @@ -60,28 +63,43 @@ public class Dream extends Service implements Window.Callback { public static final String METADATA_NAME_CONFIG_ACTIVITY = "android.service.dreams.config_activity"; - private Window mWindow; + /** + * Broadcast Action: Sent after the system starts dreaming. + * + * <p class="note">This is a protected intent that can only be sent by the system. + * It is only sent to registered receivers.</p> + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DREAMING_STARTED = "android.intent.action.DREAMING_STARTED"; + /** + * Broadcast Action: Sent after the system stops dreaming. + * + * <p class="note">This is a protected intent that can only be sent by the system. + * It is only sent to registered receivers.</p> + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_DREAMING_STOPPED = "android.intent.action.DREAMING_STOPPED"; + + private final Handler mHandler = new Handler(); + private IBinder mWindowToken; + private Window mWindow; private WindowManager mWindowManager; private IDreamManager mSandman; - private boolean mInteractive; - - final Handler mHandler = new Handler(); - - boolean mFinished = false; + private boolean mFinished; // begin Window.Callback methods @Override public boolean dispatchKeyEvent(KeyEvent event) { // TODO: create more flexible version of mInteractive that allows use of KEYCODE_BACK if (!mInteractive) { - if (DEBUG) Slog.v(TAG, "finishing on keyEvent"); - finish(); + if (DEBUG) Slog.v(TAG, "Finishing on keyEvent"); + safelyFinish(); return true; } else if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { - if (DEBUG) Slog.v(TAG, "finishing on back key"); - finish(); + if (DEBUG) Slog.v(TAG, "Finishing on back key"); + safelyFinish(); return true; } return mWindow.superDispatchKeyEvent(event); @@ -90,8 +108,8 @@ public class Dream extends Service implements Window.Callback { @Override public boolean dispatchKeyShortcutEvent(KeyEvent event) { if (!mInteractive) { - if (DEBUG) Slog.v(TAG, "finishing on keyShortcutEvent"); - finish(); + if (DEBUG) Slog.v(TAG, "Finishing on keyShortcutEvent"); + safelyFinish(); return true; } return mWindow.superDispatchKeyShortcutEvent(event); @@ -102,8 +120,8 @@ public class Dream extends Service implements Window.Callback { // TODO: create more flexible version of mInteractive that allows clicks // but finish()es on any other kind of activity if (!mInteractive) { - if (DEBUG) Slog.v(TAG, "finishing on touchEvent"); - finish(); + if (DEBUG) Slog.v(TAG, "Finishing on touchEvent"); + safelyFinish(); return true; } return mWindow.superDispatchTouchEvent(event); @@ -112,8 +130,8 @@ public class Dream extends Service implements Window.Callback { @Override public boolean dispatchTrackballEvent(MotionEvent event) { if (!mInteractive) { - if (DEBUG) Slog.v(TAG, "finishing on trackballEvent"); - finish(); + if (DEBUG) Slog.v(TAG, "Finishing on trackballEvent"); + safelyFinish(); return true; } return mWindow.superDispatchTrackballEvent(event); @@ -122,8 +140,8 @@ public class Dream extends Service implements Window.Callback { @Override public boolean dispatchGenericMotionEvent(MotionEvent event) { if (!mInteractive) { - if (DEBUG) Slog.v(TAG, "finishing on genericMotionEvent"); - finish(); + if (DEBUG) Slog.v(TAG, "Finishing on genericMotionEvent"); + safelyFinish(); return true; } return mWindow.superDispatchGenericMotionEvent(event); @@ -212,30 +230,9 @@ public class Dream extends Service implements Window.Callback { public Window getWindow() { return mWindow; } - - /** - * Called when this Dream is constructed. Place your initialization here. - * - * Subclasses must call through to the superclass implementation. - */ - @Override - public void onCreate() { - super.onCreate(); - - if (DEBUG) Slog.v(TAG, "Dream created on thread " + Thread.currentThread().getId()); - - mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService("dreams")); - } - - /** - * Called when this Dream is started. - */ - public void onStart() { - // hook for subclasses - } /** - * Inflate a layout resource and set it to be the content view for this Dream. + * Inflates a layout resource and set it to be the content view for this Dream. * Behaves similarly to {@link android.app.Activity#setContentView(int)}. * * @param layoutResID Resource ID to be inflated. @@ -248,7 +245,7 @@ public class Dream extends Service implements Window.Callback { } /** - * Set a view to be the content view for this Dream. + * Sets a view to be the content view for this Dream. * Behaves similarly to {@link android.app.Activity#setContentView(android.view.View)}, * including using {@link ViewGroup.LayoutParams#MATCH_PARENT} as the layout height and width of the view. * @@ -262,7 +259,7 @@ public class Dream extends Service implements Window.Callback { } /** - * Set a view to be the content view for this Dream. + * Sets a view to be the content view for this Dream. * Behaves similarly to * {@link android.app.Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}. * @@ -277,7 +274,7 @@ public class Dream extends Service implements Window.Callback { } /** - * Add a view to the Dream's window, leaving other content views in place. + * Adds a view to the Dream's window, leaving other content views in place. * * @param view The desired content to display. * @param params Layout parameters for the view. @@ -285,21 +282,27 @@ public class Dream extends Service implements Window.Callback { public void addContentView(View view, ViewGroup.LayoutParams params) { getWindow().addContentView(view, params); } - + /** - * @param mInteractive the mInteractive to set + * Marks this dream as interactive to receive input events. + * + * <p>Non-interactive dreams (default) will dismiss on the first input event.</p> + * + * <p>Interactive dreams should call {@link #finish()} to dismiss themselves.</p> + * + * @param interactive True if this dream will handle input events. */ - public void setInteractive(boolean mInteractive) { - this.mInteractive = mInteractive; + public void setInteractive(boolean interactive) { + mInteractive = interactive; } /** - * @return the mInteractive + * Returns whether or not this dream is interactive. */ public boolean isInteractive() { return mInteractive; } - + /** Convenience method for setting View.SYSTEM_UI_FLAG_LOW_PROFILE on the content view. */ protected void lightsOut() { // turn the lights down low @@ -319,14 +322,29 @@ public class Dream extends Service implements Window.Callback { public View findViewById(int id) { return getWindow().findViewById(id); } - + /** - * Called when this Dream is being removed from the screen and stopped. + * Called when this Dream is constructed. Place your initialization here. + * + * Subclasses must call through to the superclass implementation. */ @Override - public void onDestroy() { - super.onDestroy(); - mWindowManager.removeView(mWindow.getDecorView()); + public void onCreate() { + if (DEBUG) Slog.v(TAG, "onCreate() on thread " + Thread.currentThread().getId()); + super.onCreate(); + loadSandman(); + } + + /** + * Called when this Dream is started. + */ + public void onStart() { + // hook for subclasses + Slog.v(TAG, "called Dream.onStart()"); + } + + private void loadSandman() { + mSandman = IDreamManager.Stub.asInterface(ServiceManager.getService("dreams")); } /** @@ -335,16 +353,21 @@ public class Dream extends Service implements Window.Callback { * @param windowToken Binder to attach to the window to allow access to the correct window type. * @hide */ - final /*package*/ void attach(IBinder windowToken) { - if (DEBUG) Slog.v(TAG, "Dream attached on thread " + Thread.currentThread().getId()); - + private final void attach(IBinder windowToken) { + if (DEBUG) Slog.v(TAG, "Attached on thread " + Thread.currentThread().getId()); + + if (mSandman == null) { + Slog.w(TAG, "No dream manager found, super.onCreate may not have been called"); + loadSandman(); + } + mWindowToken = windowToken; mWindow = PolicyManager.makeNewWindow(this); mWindow.setCallback(this); mWindow.requestFeature(Window.FEATURE_NO_TITLE); mWindow.setBackgroundDrawable(new ColorDrawable(0xFF000000)); - if (DEBUG) Slog.v(TAG, "attaching window token: " + windowToken - + " to window of type " + WindowManager.LayoutParams.TYPE_DREAM); + if (DEBUG) Slog.v(TAG, String.format("Attaching window token: %s to window of type %s", + windowToken, WindowManager.LayoutParams.TYPE_DREAM)); WindowManager.LayoutParams lp = mWindow.getAttributes(); lp.type = WindowManager.LayoutParams.TYPE_DREAM; @@ -355,58 +378,105 @@ public class Dream extends Service implements Window.Callback { | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON ); mWindow.setAttributes(lp); - - //WindowManagerImpl.getDefault().addView(mWindow.getDecorView(), lp); - - if (DEBUG) Slog.v(TAG, "created and attached window: " + mWindow); + + if (DEBUG) Slog.v(TAG, "Created and attached window: " + mWindow); mWindow.setWindowManager(null, windowToken, "dream", true); mWindowManager = mWindow.getWindowManager(); - - // now make it visible + + // now make it visible (on the ui thread) mHandler.post(new Runnable(){ @Override public void run() { - if (DEBUG) Slog.v(TAG, "Dream window added on thread " + Thread.currentThread().getId()); + if (DEBUG) Slog.v(TAG, "Window added on thread " + Thread.currentThread().getId()); - getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); + try { + getWindowManager().addView(mWindow.getDecorView(), mWindow.getAttributes()); + } catch (Throwable t) { + Slog.w("Crashed adding window view", t); + safelyFinish(); + return; + } // start it up - onStart(); + try { + onStart(); + } catch (Throwable t) { + Slog.w("Crashed in onStart()", t); + safelyFinish(); + } }}); } - + + private void safelyFinish() { + if (DEBUG) Slog.v(TAG, "safelyFinish()"); + try { + finish(); + } catch (Throwable t) { + Slog.w(TAG, "Crashed in safelyFinish()", t); + finishInternal(); + return; + } + + if (!mFinished) { + Slog.w(TAG, "Bad dream, did not call super.finish()"); + finishInternal(); + } + } + /** - * Stop the dream and wake up. - * - * After this method is called, the service will be stopped. + * Stops the dream, detaches from the window, and wakes up. + * + * Subclasses must call through to the superclass implementation. + * + * <p>After this method is called, the service will be stopped.</p> */ public void finish() { + if (DEBUG) Slog.v(TAG, "finish()"); + finishInternal(); + } + + private void finishInternal() { + if (DEBUG) Slog.v(TAG, "finishInternal() mFinished = " + mFinished); if (mFinished) return; try { - mSandman.awaken(); // assuming we were started by the DreamManager - stopSelf(); // if launched via any other means mFinished = true; - } catch (RemoteException ex) { - // sigh + + if (mSandman != null) { + mSandman.awakenSelf(mWindowToken); + } else { + Slog.w(TAG, "No dream manager found"); + } + stopSelf(); // if launched via any other means + + } catch (Throwable t) { + Slog.w(TAG, "Crashed in finishInternal()", t); } } - class IDreamServiceWrapper extends IDreamService.Stub { - public IDreamServiceWrapper() { - } + @Override + public void onDestroy() { + if (DEBUG) Slog.v(TAG, "onDestroy()"); + super.onDestroy(); - public void attach(IBinder windowToken) { - Dream.this.attach(windowToken); + if (DEBUG) Slog.v(TAG, "Removing window"); + try { + mWindowManager.removeView(mWindow.getDecorView()); + } catch (Throwable t) { + Slog.w(TAG, "Crashed removing window view", t); } } - /** - * Implement to return the implementation of the internal accessibility - * service interface. Subclasses should not override. - */ @Override public final IBinder onBind(Intent intent) { - return new IDreamServiceWrapper(); + if (DEBUG) Slog.v(TAG, "onBind() intent = " + intent); + return new DreamServiceWrapper(); } + + private class DreamServiceWrapper extends IDreamService.Stub { + public void attach(IBinder windowToken) { + Dream.this.attach(windowToken); + } + } + } diff --git a/core/java/android/service/dreams/DreamManagerService.java b/core/java/android/service/dreams/DreamManagerService.java deleted file mode 100644 index 4aa1cbb..0000000 --- a/core/java/android/service/dreams/DreamManagerService.java +++ /dev/null @@ -1,247 +0,0 @@ -package android.service.dreams; - -import static android.provider.Settings.Secure.SCREENSAVER_COMPONENTS; -import static android.provider.Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT; -import java.io.FileDescriptor; -import java.io.PrintWriter; - -import android.app.ActivityManagerNative; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.ServiceConnection; -import android.content.pm.PackageManager; -import android.os.Binder; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.UserHandle; -import android.provider.Settings; -import android.util.Slog; -import android.view.IWindowManager; -import android.view.WindowManager; -import android.view.WindowManagerGlobal; - -/** - * - * @hide - * - */ - -public class DreamManagerService - extends IDreamManager.Stub - implements ServiceConnection -{ - private static final boolean DEBUG = true; - private static final String TAG = "DreamManagerService"; - - final Object mLock = new Object[0]; - - private Context mContext; - private IWindowManager mIWindowManager; - - private ComponentName mCurrentDreamComponent; - private IDreamService mCurrentDream; - private Binder mCurrentDreamToken; - private int mCurrentUserId; - - public DreamManagerService(Context context) { - if (DEBUG) Slog.v(TAG, "DreamManagerService startup"); - mContext = context; - mIWindowManager = WindowManagerGlobal.getWindowManagerService(); - } - - private void checkPermission(String permission) { - if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(permission)) { - throw new SecurityException("Access denied to process: " + Binder.getCallingPid() - + ", must have permission " + permission); - } - } - - // IDreamManager method - @Override - public void dream() { - ComponentName[] dreams = getDreamComponentsForUser(mCurrentUserId); - ComponentName name = dreams != null && dreams.length > 0 ? dreams[0] : null; - if (name != null) { - synchronized (mLock) { - final long ident = Binder.clearCallingIdentity(); - try { - bindDreamComponentL(name, false); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - } - - // IDreamManager method - @Override - public void setDreamComponents(ComponentName[] componentNames) { - Settings.Secure.putStringForUser(mContext.getContentResolver(), - SCREENSAVER_COMPONENTS, - componentsToString(componentNames), - UserHandle.getCallingUserId()); - } - - private static String componentsToString(ComponentName[] componentNames) { - StringBuilder names = new StringBuilder(); - if (componentNames != null) { - for (ComponentName componentName : componentNames) { - if (names.length() > 0) - names.append(','); - names.append(componentName.flattenToString()); - } - } - return names.toString(); - } - - private static ComponentName[] componentsFromString(String names) { - String[] namesArray = names.split(","); - ComponentName[] componentNames = new ComponentName[namesArray.length]; - for (int i = 0; i < namesArray.length; i++) - componentNames[i] = ComponentName.unflattenFromString(namesArray[i]); - return componentNames; - } - - // IDreamManager method - @Override - public ComponentName[] getDreamComponents() { - return getDreamComponentsForUser(UserHandle.getCallingUserId()); - } - - private ComponentName[] getDreamComponentsForUser(int userId) { - String names = Settings.Secure.getStringForUser(mContext.getContentResolver(), - SCREENSAVER_COMPONENTS, - userId); - return names == null ? null : componentsFromString(names); - } - - // IDreamManager method - @Override - public ComponentName getDefaultDreamComponent() { - String name = Settings.Secure.getStringForUser(mContext.getContentResolver(), - SCREENSAVER_DEFAULT_COMPONENT, - UserHandle.getCallingUserId()); - return name == null ? null : ComponentName.unflattenFromString(name); - } - - // IDreamManager method - @Override - public void testDream(ComponentName name) { - if (DEBUG) Slog.v(TAG, "startDream name=" + name - + " pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); -// checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT); - synchronized (mLock) { - final long ident = Binder.clearCallingIdentity(); - try { - bindDreamComponentL(name, true); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - } - - // IDreamManager method - @Override - public void awaken() { - if (DEBUG) Slog.v(TAG, "awaken()"); - synchronized (mLock) { - if (mCurrentDream != null) { - if (DEBUG) Slog.v(TAG, "disconnecting: " + mCurrentDreamComponent + " service: " + mCurrentDream); - mContext.unbindService(this); - mCurrentDream = null; - mCurrentDreamToken = null; - } - } - } - - // IDreamManager method - @Override - public boolean isDreaming() { - synchronized (mLock) { - return mCurrentDreamToken != null; - } - } - - public void bindDreamComponentL(ComponentName componentName, boolean test) { - if (DEBUG) Slog.v(TAG, "bindDreamComponent: componentName=" + componentName - + " pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); - - Intent intent = new Intent(Intent.ACTION_MAIN) - .setComponent(componentName) - .addFlags( - Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS - ) - .putExtra("android.dreams.TEST", test); - - mCurrentDreamComponent = componentName; - mCurrentDreamToken = new Binder(); - try { - if (DEBUG) Slog.v(TAG, "Adding window token: " + mCurrentDreamToken - + " for window type: " + WindowManager.LayoutParams.TYPE_DREAM); - mIWindowManager.addWindowToken(mCurrentDreamToken, - WindowManager.LayoutParams.TYPE_DREAM); - } catch (RemoteException e) { - Slog.w(TAG, "Unable to add window token. Proceed at your own risk."); - } - - if (!mContext.bindService(intent, this, Context.BIND_AUTO_CREATE)) { - Slog.w(TAG, "unable to bind service: " + componentName); - } - } - - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - if (DEBUG) Slog.v(TAG, "connected to dream: " + name + " binder=" + service + " thread=" + Thread.currentThread().getId()); - - mCurrentDream = IDreamService.Stub.asInterface(service); - try { - if (DEBUG) Slog.v(TAG, "attaching with token:" + mCurrentDreamToken); - mCurrentDream.attach(mCurrentDreamToken); - } catch (RemoteException ex) { - Slog.w(TAG, "Unable to send window token to dream:" + ex); - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - if (DEBUG) Slog.v(TAG, "disconnected: " + name + " service: " + mCurrentDream); - // Only happens in exceptional circumstances - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); - - pw.println("Dreamland:"); - pw.print(" component="); pw.println(mCurrentDreamComponent); - pw.print(" token="); pw.println(mCurrentDreamToken); - pw.print(" dream="); pw.println(mCurrentDream); - } - - public void systemReady() { - - // dream settings are kept per user, so keep track of current user - try { - mCurrentUserId = ActivityManagerNative.getDefault().getCurrentUser().id; - } catch (RemoteException e) { - Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e); - } - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_USER_SWITCHED); - mContext.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (Intent.ACTION_USER_SWITCHED.equals(action)) { - mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); - if (DEBUG) Slog.v(TAG, "userId " + mCurrentUserId + " is in the house"); - } - }}, filter); - - if (DEBUG) Slog.v(TAG, "ready to dream!"); - } - -} diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl index b6fcdf0..bd1c524 100644 --- a/core/java/android/service/dreams/IDreamManager.aidl +++ b/core/java/android/service/dreams/IDreamManager.aidl @@ -19,6 +19,7 @@ package android.service.dreams; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.content.ComponentName; +import android.os.IBinder; /** @hide */ interface IDreamManager { @@ -29,4 +30,5 @@ interface IDreamManager { ComponentName getDefaultDreamComponent(); void testDream(in ComponentName componentName); boolean isDreaming(); + void awakenSelf(in IBinder token); }
\ No newline at end of file diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java index 0babcc5..1060bd8 100644 --- a/core/java/android/text/format/DateUtils.java +++ b/core/java/android/text/format/DateUtils.java @@ -45,7 +45,6 @@ public class DateUtils private static final String FAST_FORMAT_HMMSS = "%1$d:%2$02d:%3$02d"; private static final String FAST_FORMAT_MMSS = "%1$02d:%2$02d"; - private static final char TIME_PADDING = '0'; private static final char TIME_SEPARATOR = ':'; @@ -648,33 +647,36 @@ public class DateUtils } } + private static void append(StringBuilder sb, long value, boolean pad, char zeroDigit) { + if (value < 10) { + if (pad) { + sb.append(zeroDigit); + } + } else { + sb.append((char) (zeroDigit + (value / 10))); + } + sb.append((char) (zeroDigit + (value % 10))); + } + /** - * Fast formatting of h:mm:ss + * Fast formatting of h:mm:ss. */ private static String formatElapsedTime(StringBuilder recycle, String format, long hours, long minutes, long seconds) { if (FAST_FORMAT_HMMSS.equals(format)) { + char zeroDigit = LocaleData.get(Locale.getDefault()).zeroDigit; + StringBuilder sb = recycle; if (sb == null) { sb = new StringBuilder(8); } else { sb.setLength(0); } - sb.append(hours); + append(sb, hours, false, zeroDigit); sb.append(TIME_SEPARATOR); - if (minutes < 10) { - sb.append(TIME_PADDING); - } else { - sb.append(toDigitChar(minutes / 10)); - } - sb.append(toDigitChar(minutes % 10)); + append(sb, minutes, true, zeroDigit); sb.append(TIME_SEPARATOR); - if (seconds < 10) { - sb.append(TIME_PADDING); - } else { - sb.append(toDigitChar(seconds / 10)); - } - sb.append(toDigitChar(seconds % 10)); + append(sb, seconds, true, zeroDigit); return sb.toString(); } else { return String.format(format, hours, minutes, seconds); @@ -682,40 +684,28 @@ public class DateUtils } /** - * Fast formatting of m:ss + * Fast formatting of mm:ss. */ private static String formatElapsedTime(StringBuilder recycle, String format, long minutes, long seconds) { if (FAST_FORMAT_MMSS.equals(format)) { + char zeroDigit = LocaleData.get(Locale.getDefault()).zeroDigit; + StringBuilder sb = recycle; if (sb == null) { sb = new StringBuilder(8); } else { sb.setLength(0); } - if (minutes < 10) { - sb.append(TIME_PADDING); - } else { - sb.append(toDigitChar(minutes / 10)); - } - sb.append(toDigitChar(minutes % 10)); + append(sb, minutes, false, zeroDigit); sb.append(TIME_SEPARATOR); - if (seconds < 10) { - sb.append(TIME_PADDING); - } else { - sb.append(toDigitChar(seconds / 10)); - } - sb.append(toDigitChar(seconds % 10)); + append(sb, seconds, true, zeroDigit); return sb.toString(); } else { return String.format(format, minutes, seconds); } } - private static char toDigitChar(long digit) { - return (char) (digit + '0'); - } - /** * Format a date / time such that if the then is on the same day as now, it shows * just the time and if it's a different day, it shows just the date. @@ -1387,6 +1377,14 @@ public class DateUtils String endMonthDayString = isInstant ? null : endDate.format(MONTH_DAY_FORMAT); String endYearString = isInstant ? null : endDate.format(YEAR_FORMAT); + String startStandaloneMonthString = startMonthString; + String endStandaloneMonthString = endMonthString; + // We need standalone months for these strings in Persian (fa): http://b/6811327 + if (!numericDate && !abbrevMonth && Locale.getDefault().getLanguage().equals("fa")) { + startStandaloneMonthString = startDate.format("%-B"); + endStandaloneMonthString = endDate.format("%-B"); + } + if (startMonthNum != endMonthNum) { // Same year, different month. // Example: "October 28 - November 3" @@ -1407,7 +1405,8 @@ public class DateUtils startWeekDayString, startMonthString, startMonthDayString, startYearString, startTimeString, endWeekDayString, endMonthString, endMonthDayString, - endYearString, endTimeString); + endYearString, endTimeString, + startStandaloneMonthString, endStandaloneMonthString); } if (startDay != endDay) { @@ -1426,7 +1425,8 @@ public class DateUtils startWeekDayString, startMonthString, startMonthDayString, startYearString, startTimeString, endWeekDayString, endMonthString, endMonthDayString, - endYearString, endTimeString); + endYearString, endTimeString, + startStandaloneMonthString, endStandaloneMonthString); } // Same start and end day diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java index 45d5a70..5ef86b1 100644 --- a/core/java/android/text/format/Time.java +++ b/core/java/android/text/format/Time.java @@ -151,6 +151,9 @@ public class Time { private static String sDateTimeFormat; private static String sAm; private static String sPm; + private static char sZeroDigit; + + // Referenced by native code. private static String sDateCommand = "%a %b %e %H:%M:%S %Z %Y"; /** @@ -323,6 +326,7 @@ public class Time { sAm = localeData.amPm[0]; sPm = localeData.amPm[1]; + sZeroDigit = localeData.zeroDigit; sShortMonths = localeData.shortMonthNames; sLongMonths = localeData.longMonthNames; @@ -338,12 +342,32 @@ public class Time { sLocale = locale; } - return format1(format); + String result = format1(format); + if (sZeroDigit != '0') { + result = localizeDigits(result); + } + return result; } } native private String format1(String format); + // TODO: unify this with java.util.Formatter's copy. + private String localizeDigits(String s) { + int length = s.length(); + int offsetToLocalizedDigits = sZeroDigit - '0'; + StringBuilder result = new StringBuilder(length); + for (int i = 0; i < length; ++i) { + char ch = s.charAt(i); + if (ch >= '0' && ch <= '9') { + ch += offsetToLocalizedDigits; + } + result.append(ch); + } + return result.toString(); + } + + /** * Return the current time in YYYYMMDDTHHMMSS<tz> format */ @@ -673,7 +697,7 @@ public class Time { int minutes = (offset % 3600) / 60; int hours = offset / 3600; - return String.format("%s%s%02d:%02d", base, sign, hours, minutes); + return String.format(Locale.US, "%s%s%02d:%02d", base, sign, hours, minutes); } } diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 446a51e..ef5dc56 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -1136,9 +1136,8 @@ public abstract class HardwareRenderer { } } - int status = onPreDraw(dirty); - int saveCount = canvas.save(); - callbacks.onHardwarePreDraw(canvas); + int saveCount = 0; + int status = DisplayList.STATUS_DONE; try { view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) @@ -1164,6 +1163,10 @@ public abstract class HardwareRenderer { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } + status = onPreDraw(dirty); + saveCount = canvas.save(); + callbacks.onHardwarePreDraw(canvas); + if (mProfileEnabled) { long now = System.nanoTime(); float total = (now - getDisplayListStartTime) * 0.000001f; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index c374ca6..87221e0 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -83,12 +83,18 @@ import static java.lang.Math.max; import com.android.internal.R; import com.android.internal.util.Predicate; import com.android.internal.view.menu.MenuBuilder; +import com.google.android.collect.Lists; +import com.google.android.collect.Maps; import java.lang.ref.WeakReference; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; import java.util.Locale; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; @@ -1503,7 +1509,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private SparseArray<Object> mKeyedTags; /** - * The next available accessiiblity id. + * The next available accessibility id. */ private static int sNextAccessibilityViewId; @@ -1727,7 +1733,51 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ static final int PFLAG_INVALIDATED = 0x80000000; - /* Masks for mPrivateFlags2 */ + /** + * Masks for mPrivateFlags2, as generated by dumpFlags(): + * + * -------|-------|-------|-------| + * PFLAG2_TEXT_ALIGNMENT_FLAGS[0] + * PFLAG2_TEXT_DIRECTION_FLAGS[0] + * 1 PFLAG2_DRAG_CAN_ACCEPT + * 1 PFLAG2_DRAG_HOVERED + * 1 PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT + * 11 PFLAG2_TEXT_DIRECTION_MASK_SHIFT + * 1 1 PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT + * 11 PFLAG2_LAYOUT_DIRECTION_MASK + * 11 1 PFLAG2_TEXT_ALIGNMENT_MASK_SHIFT + * 1 PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL + * 1 1 PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT + * 1 1 PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_SHIFT + * 1 PFLAG2_LAYOUT_DIRECTION_RESOLVED + * 11 PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK + * 1 PFLAG2_TEXT_DIRECTION_FLAGS[1] + * 1 PFLAG2_TEXT_DIRECTION_FLAGS[2] + * 11 PFLAG2_TEXT_DIRECTION_FLAGS[3] + * 1 PFLAG2_TEXT_DIRECTION_FLAGS[4] + * 1 1 PFLAG2_TEXT_DIRECTION_FLAGS[5] + * 111 PFLAG2_TEXT_DIRECTION_MASK + * 1 PFLAG2_TEXT_DIRECTION_RESOLVED + * 1 PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT + * 111 PFLAG2_TEXT_DIRECTION_RESOLVED_MASK + * 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[1] + * 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[2] + * 11 PFLAG2_TEXT_ALIGNMENT_FLAGS[3] + * 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[4] + * 1 1 PFLAG2_TEXT_ALIGNMENT_FLAGS[5] + * 11 PFLAG2_TEXT_ALIGNMENT_FLAGS[6] + * 111 PFLAG2_TEXT_ALIGNMENT_MASK + * 1 PFLAG2_TEXT_ALIGNMENT_RESOLVED + * 1 PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT + * 111 PFLAG2_TEXT_ALIGNMENT_RESOLVED_MASK + * 11 PFLAG2_IMPORTANT_FOR_ACCESSIBILITY_MASK + * 1 PFLAG2_HAS_TRANSIENT_STATE + * 1 PFLAG2_ACCESSIBILITY_FOCUSED + * 1 PFLAG2_ACCESSIBILITY_STATE_CHANGED + * 1 PFLAG2_VIEW_QUICK_REJECTED + * 1 PFLAG2_PADDING_RESOLVED + * -------|-------|-------|-------| + */ /** * Indicates that this view has reported that it can accept the current drag's content. @@ -1825,8 +1875,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @hide */ - static final int PFLAG2_HAS_TRANSIENT_STATE = 0x00000100; - + static final int PFLAG2_HAS_TRANSIENT_STATE = 0x1 << 22; /** * Text direction is inherited thru {@link ViewGroup} @@ -2053,7 +2102,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public static final int IMPORTANT_FOR_ACCESSIBILITY_NO = 0x00000002; /** - * The default whether the view is important for accessiblity. + * The default whether the view is important for accessibility. */ static final int IMPORTANT_FOR_ACCESSIBILITY_DEFAULT = IMPORTANT_FOR_ACCESSIBILITY_AUTO; @@ -2814,18 +2863,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int mUserPaddingEnd; /** - * Whether a left padding has been defined during layout inflation. + * Cache initial left padding. * * @hide */ - boolean mUserPaddingLeftDefined = false; + int mUserPaddingLeftInitial = UNDEFINED_PADDING; /** - * Whether a right padding has been defined during layout inflation. + * Cache initial right padding. * * @hide */ - boolean mUserPaddingRightDefined = false; + int mUserPaddingRightInitial = UNDEFINED_PADDING; /** * Default undefined padding @@ -3125,7 +3174,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private boolean mSendingHoverAccessibilityEvents; /** - * Delegate for injecting accessiblity functionality. + * Delegate for injecting accessibility functionality. */ AccessibilityDelegate mAccessibilityDelegate; @@ -3247,19 +3296,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, break; case com.android.internal.R.styleable.View_padding: padding = a.getDimensionPixelSize(attr, -1); - mUserPaddingLeftDefined = true; - mUserPaddingRightDefined = true; + mUserPaddingLeftInitial = padding; + mUserPaddingRightInitial = padding; break; case com.android.internal.R.styleable.View_paddingLeft: leftPadding = a.getDimensionPixelSize(attr, -1); - mUserPaddingLeftDefined = true; + mUserPaddingLeftInitial = leftPadding; break; case com.android.internal.R.styleable.View_paddingTop: topPadding = a.getDimensionPixelSize(attr, -1); break; case com.android.internal.R.styleable.View_paddingRight: rightPadding = a.getDimensionPixelSize(attr, -1); - mUserPaddingRightDefined = true; + mUserPaddingRightInitial = rightPadding; break; case com.android.internal.R.styleable.View_paddingBottom: bottomPadding = a.getDimensionPixelSize(attr, -1); @@ -3561,15 +3610,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, topPadding = padding; rightPadding = padding; bottomPadding = padding; + mUserPaddingLeftInitial = padding; + mUserPaddingRightInitial = padding; } // If the user specified the padding (either with android:padding or // android:paddingLeft/Top/Right/Bottom), use this padding, otherwise // use the default padding or the padding from the background drawable // (stored at this point in mPadding*) - internalSetPadding(leftPadding >= 0 ? leftPadding : mPaddingLeft, + mUserPaddingLeftInitial = leftPadding >= 0 ? leftPadding : mPaddingLeft; + mUserPaddingRightInitial = rightPadding >= 0 ? rightPadding : mPaddingRight; + internalSetPadding(mUserPaddingLeftInitial, topPadding >= 0 ? topPadding : mPaddingTop, - rightPadding >= 0 ? rightPadding : mPaddingRight, + mUserPaddingRightInitial, bottomPadding >= 0 ? bottomPadding : mPaddingBottom); if (viewFlagMasks != 0) { @@ -4475,7 +4528,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Sends an accessibility event of the given type. If accessiiblity is + * Sends an accessibility event of the given type. If accessibility is * not enabled this method has no effect. The default implementation calls * {@link #onInitializeAccessibilityEvent(AccessibilityEvent)} first * to populate information about the event source (this View), then calls @@ -4894,7 +4947,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, info.setLongClickable(isLongClickable()); // TODO: These make sense only if we are in an AdapterView but all - // views can be selected. Maybe from accessiiblity perspective + // views can be selected. Maybe from accessibility perspective // we should report as selectable view in an AdapterView. info.addAction(AccessibilityNodeInfo.ACTION_SELECT); info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_SELECTION); @@ -5004,7 +5057,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Sets a delegate for implementing accessibility support via compositon as + * Sets a delegate for implementing accessibility support via composition as * opposed to inheritance. The delegate's primary use is for implementing * backwards compatible widgets. For more details see {@link AccessibilityDelegate}. * @@ -6680,7 +6733,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Returns whether the View is considered actionable from * accessibility perspective. Such view are important for - * accessiiblity. + * accessibility. * * @return True if the view is actionable for accessibility. * @@ -6692,7 +6745,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Returns whether the View has registered callbacks wich makes it - * important for accessiiblity. + * important for accessibility. * * @return True if the view is actionable for accessibility. */ @@ -6731,7 +6784,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Reset the state indicating the this view has requested clients - * interested in its accessiblity state to be notified. + * interested in its accessibility state to be notified. * * @hide */ @@ -11540,10 +11593,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // left / right padding are used if defined. If they are not defined and start / end // padding are defined (e.g. in Frameworks resources), then we use start / end and // resolve them as left / right (layout direction is not taken into account). - if (!mUserPaddingLeftDefined && mUserPaddingStart != UNDEFINED_PADDING) { + if (mUserPaddingLeftInitial == UNDEFINED_PADDING && + mUserPaddingStart != UNDEFINED_PADDING) { mUserPaddingLeft = mUserPaddingStart; } - if (!mUserPaddingRightDefined && mUserPaddingEnd != UNDEFINED_PADDING) { + if (mUserPaddingRightInitial == UNDEFINED_PADDING + && mUserPaddingEnd != UNDEFINED_PADDING) { mUserPaddingRight = mUserPaddingEnd; } @@ -11557,6 +11612,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // left / right or right / left depending on the resolved layout direction. // If start / end padding are not defined, use the left / right ones. int resolvedLayoutDirection = getResolvedLayoutDirection(); + // Set user padding to initial values ... + mUserPaddingLeft = (mUserPaddingLeftInitial == UNDEFINED_PADDING) ? + 0 : mUserPaddingLeftInitial; + mUserPaddingRight = (mUserPaddingRightInitial == UNDEFINED_PADDING) ? + 0 : mUserPaddingRightInitial; + // ... then resolve it. switch (resolvedLayoutDirection) { case LAYOUT_DIRECTION_RTL: if (mUserPaddingStart != UNDEFINED_PADDING) { @@ -14322,10 +14383,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags2 &= ~PFLAG2_PADDING_RESOLVED; switch (background.getLayoutDirection()) { case LAYOUT_DIRECTION_RTL: + mUserPaddingLeftInitial = padding.right; + mUserPaddingRightInitial = padding.left; internalSetPadding(padding.right, padding.top, padding.left, padding.bottom); break; case LAYOUT_DIRECTION_LTR: default: + mUserPaddingLeftInitial = padding.left; + mUserPaddingRightInitial = padding.right; internalSetPadding(padding.left, padding.top, padding.right, padding.bottom); } } @@ -14422,6 +14487,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mUserPaddingStart = UNDEFINED_PADDING; mUserPaddingEnd = UNDEFINED_PADDING; + mUserPaddingLeftInitial = left; + mUserPaddingRightInitial = right; + internalSetPadding(left, top, right, bottom); } @@ -14511,10 +14579,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, switch(getResolvedLayoutDirection()) { case LAYOUT_DIRECTION_RTL: + mUserPaddingLeftInitial = end; + mUserPaddingRightInitial = start; internalSetPadding(end, top, start, bottom); break; case LAYOUT_DIRECTION_LTR: default: + mUserPaddingLeftInitial = start; + mUserPaddingRightInitial = end; internalSetPadding(start, top, end, bottom); } } @@ -18091,4 +18163,46 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return (view.mLabelForId == mLabeledId); } } + + /** + * Dump all private flags in readable format, useful for documentation and + * sanity checking. + */ + private static void dumpFlags() { + final HashMap<String, String> found = Maps.newHashMap(); + try { + for (Field field : View.class.getDeclaredFields()) { + final int modifiers = field.getModifiers(); + if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) { + if (field.getType().equals(int.class)) { + final int value = field.getInt(null); + dumpFlag(found, field.getName(), value); + } else if (field.getType().equals(int[].class)) { + final int[] values = (int[]) field.get(null); + for (int i = 0; i < values.length; i++) { + dumpFlag(found, field.getName() + "[" + i + "]", values[i]); + } + } + } + } + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + + final ArrayList<String> keys = Lists.newArrayList(); + keys.addAll(found.keySet()); + Collections.sort(keys); + for (String key : keys) { + Log.d(VIEW_LOG_TAG, found.get(key)); + } + } + + private static void dumpFlag(HashMap<String, String> found, String name, int value) { + // Sort flags by prefix, then by bits, always keeping unique keys + final String bits = String.format("%32s", Integer.toBinaryString(value)).replace('0', ' '); + final int prefix = name.indexOf('_'); + final String key = (prefix > 0 ? name.substring(0, prefix) : name) + bits + name; + final String output = bits + " " + name; + found.put(key, output); + } } diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 75554da..82f07c7 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -1111,7 +1111,15 @@ public interface WindowManagerPolicy { * @param attrs The window's LayoutParams. * @return Whether magnification can be applied. */ - public boolean canMagnifyWindow(WindowManager.LayoutParams attrs); + public boolean canMagnifyWindowLw(WindowManager.LayoutParams attrs); + + /** + * Called when the current user changes. Guaranteed to be called before the broadcast + * of the new user id is made to all listeners. + * + * @param newUserId The id of the incoming user. + */ + public void setCurrentUserLw(int newUserId); /** * Print the WindowManagerPolicy's state into the given stream. diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java index d9aeb70..e6eaa14 100644 --- a/core/java/android/webkit/JWebCoreJavaBridge.java +++ b/core/java/android/webkit/JWebCoreJavaBridge.java @@ -89,7 +89,6 @@ final class JWebCoreJavaBridge extends Handler { private void fireSharedTimer() { // clear the flag so that sharedTimerFired() can set a new timer mHasInstantTimer = false; - removeMessages(TIMER_MESSAGE); sharedTimerFired(); } diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 2d834ff..33fe834 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -2198,20 +2198,12 @@ public final class WebViewCore { mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW)); } m_skipDrawFlag = false; - m_skipDrawFlagLock.notify(); } } private void webkitDraw() { synchronized (m_skipDrawFlagLock) { if (m_skipDrawFlag) { - try { - // Aggressively throttle webkit to give the UI more CPU - // to catch up with - m_skipDrawFlagLock.wait(50); - } catch (InterruptedException e) {} - } - if (m_skipDrawFlag) { m_drawWasSkipped = true; return; } @@ -2536,6 +2528,9 @@ public final class WebViewCore { adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi / mViewportDensityDpi; } + // We make bad assumptions about multiplying and dividing by 100, force + // them to be true with this hack + adjust = ((int) (adjust * 100)) / 100.0f; // Remove any update density messages in flight. // If the density is indeed different from WebView's default scale, // a new message will be queued. diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java index 80a6782..1d864e5 100644 --- a/core/java/android/webkit/ZoomManager.java +++ b/core/java/android/webkit/ZoomManager.java @@ -287,6 +287,7 @@ class ZoomManager { if (!exceedsMinScaleIncrement(mMinZoomScale, mMaxZoomScale)) { mMaxZoomScale = mMinZoomScale; } + sanitizeMinMaxScales(); } public final float getScale() { @@ -909,6 +910,14 @@ class ZoomManager { } } + private void sanitizeMinMaxScales() { + if (mMinZoomScale > mMaxZoomScale) { + Log.w(LOGTAG, "mMinZoom > mMaxZoom!!! " + mMinZoomScale + " > " + mMaxZoomScale, + new Exception()); + mMaxZoomScale = mMinZoomScale; + } + } + public void onSizeChanged(int w, int h, int ow, int oh) { // reset zoom and anchor to the top left corner of the screen // unless we are already zooming @@ -933,6 +942,7 @@ class ZoomManager { if (mInitialScale > 0 && mInitialScale < mMinZoomScale) { mMinZoomScale = mInitialScale; } + sanitizeMinMaxScales(); } dismissZoomPicker(); @@ -1004,6 +1014,7 @@ class ZoomManager { } else { mMaxZoomScale = viewState.mMaxScale; } + sanitizeMinMaxScales(); } /** @@ -1033,6 +1044,7 @@ class ZoomManager { if (!mMinZoomScaleFixed || settings.getUseWideViewPort()) { mMinZoomScale = newZoomOverviewScale; mMaxZoomScale = Math.max(mMaxZoomScale, mMinZoomScale); + sanitizeMinMaxScales(); } // fit the content width to the current view for the first new picture // after first layout. @@ -1113,6 +1125,7 @@ class ZoomManager { mMinZoomScale = (mInitialScale > 0) ? Math.min(mInitialScale, overviewScale) : overviewScale; mMaxZoomScale = Math.max(mMaxZoomScale, mMinZoomScale); + sanitizeMinMaxScales(); } if (!mWebView.drawHistory()) { diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java index f79ec42..27d15f6 100755 --- a/core/java/android/widget/AppSecurityPermissions.java +++ b/core/java/android/widget/AppSecurityPermissions.java @@ -24,8 +24,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.PackageParser; -import android.content.pm.PackageUserState; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; import android.graphics.drawable.Drawable; @@ -272,7 +270,7 @@ public class AppSecurityPermissions { setPermissions(mPermsList); } - public AppSecurityPermissions(Context context, PackageParser.Package pkg) { + public AppSecurityPermissions(Context context, PackageInfo info) { mContext = context; mPm = mContext.getPackageManager(); loadResources(); @@ -280,14 +278,11 @@ public class AppSecurityPermissions { mPermGroupComparator = new PermissionGroupInfoComparator(); mPermsList = new ArrayList<MyPermissionInfo>(); Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>(); - if(pkg == null) { + if(info == null) { return; } // Convert to a PackageInfo - PackageInfo info = PackageParser.generatePackageInfo(pkg, null, - PackageManager.GET_PERMISSIONS, 0, 0, null, - new PackageUserState()); PackageInfo installedPkgInfo = null; // Get requested permissions if (info.requestedPermissions != null) { @@ -299,13 +294,13 @@ public class AppSecurityPermissions { extractPerms(info, permSet, installedPkgInfo); } // Get permissions related to shared user if any - if (pkg.mSharedUserId != null) { + if (info.sharedUserId != null) { int sharedUid; try { - sharedUid = mPm.getUidForSharedUser(pkg.mSharedUserId); + sharedUid = mPm.getUidForSharedUser(info.sharedUserId); getAllUsedPermissions(sharedUid, permSet); } catch (NameNotFoundException e) { - Log.w(TAG, "Could'nt retrieve shared user id for:"+pkg.packageName); + Log.w(TAG, "Could'nt retrieve shared user id for:"+info.packageName); } } // Retrieve list of permissions diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java index bcf0ea4..43a02cf 100644 --- a/core/java/com/android/internal/app/AlertController.java +++ b/core/java/com/android/internal/app/AlertController.java @@ -347,6 +347,18 @@ public class AlertController { } } + /** + * @param attrId the attributeId of the theme-specific drawable + * to resolve the resourceId for. + * + * @return resId the resourceId of the theme-specific drawable + */ + public int getIconAttributeResId(int attrId) { + TypedValue out = new TypedValue(); + mContext.getTheme().resolveAttribute(attrId, out, true); + return out.resourceId; + } + public void setInverseBackgroundForced(boolean forceInverseBackground) { mForceInverseBackground = forceInverseBackground; } @@ -740,6 +752,7 @@ public class AlertController { public int mIconId = 0; public Drawable mIcon; + public int mIconAttrId = 0; public CharSequence mTitle; public View mCustomTitleView; public CharSequence mMessage; @@ -807,6 +820,9 @@ public class AlertController { if (mIconId >= 0) { dialog.setIcon(mIconId); } + if (mIconAttrId > 0) { + dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId)); + } } if (mMessage != null) { dialog.setMessage(mMessage); diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java index 3477a90..20ecace 100644 --- a/core/java/com/android/internal/content/PackageMonitor.java +++ b/core/java/com/android/internal/content/PackageMonitor.java @@ -24,6 +24,7 @@ import android.net.Uri; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; +import android.os.UserHandle; import java.util.HashSet; @@ -62,11 +63,17 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { String[] mAppearingPackages; String[] mModifiedPackages; int mChangeType; + int mChangeUserId = UserHandle.USER_NULL; boolean mSomePackagesChanged; - + String[] mTempArray = new String[1]; - + public void register(Context context, Looper thread, boolean externalStorage) { + register(context, thread, null, externalStorage); + } + + public void register(Context context, Looper thread, UserHandle user, + boolean externalStorage) { if (mRegisteredContext != null) { throw new IllegalStateException("Already registered"); } @@ -84,10 +91,19 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { } else { mRegisteredHandler = new Handler(thread); } - context.registerReceiver(this, sPackageFilt, null, mRegisteredHandler); - context.registerReceiver(this, sNonDataFilt, null, mRegisteredHandler); - if (externalStorage) { - context.registerReceiver(this, sExternalFilt, null, mRegisteredHandler); + if (user != null) { + context.registerReceiverAsUser(this, user, sPackageFilt, null, mRegisteredHandler); + context.registerReceiverAsUser(this, user, sNonDataFilt, null, mRegisteredHandler); + if (externalStorage) { + context.registerReceiverAsUser(this, user, sExternalFilt, null, + mRegisteredHandler); + } + } else { + context.registerReceiver(this, sPackageFilt, null, mRegisteredHandler); + context.registerReceiver(this, sNonDataFilt, null, mRegisteredHandler); + if (externalStorage) { + context.registerReceiver(this, sExternalFilt, null, mRegisteredHandler); + } } } @@ -125,6 +141,13 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { public void onPackageRemoved(String packageName, int uid) { } + /** + * Called when a package is really removed (and not replaced) for + * all users on the device. + */ + public void onPackageRemovedAllUsers(String packageName, int uid) { + } + public void onPackageUpdateStarted(String packageName, int uid) { } @@ -220,7 +243,11 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { public void onFinishPackageChanges() { } - + + public int getChangingUserId() { + return mChangeUserId; + } + String getPackageName(Intent intent) { Uri uri = intent.getData(); String pkg = uri != null ? uri.getSchemeSpecificPart() : null; @@ -229,6 +256,12 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { + mChangeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, + UserHandle.USER_NULL); + if (mChangeUserId == UserHandle.USER_NULL) { + throw new IllegalArgumentException( + "Intent broadcast does not contain user handle: " + intent); + } onBeginPackageChanges(); mDisappearingPackages = mAppearingPackages = null; @@ -281,6 +314,9 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { // it when it is re-added. mSomePackagesChanged = true; onPackageRemoved(pkg, uid); + if (intent.getBooleanExtra(Intent.EXTRA_REMOVED_FOR_ALL_USERS, false)) { + onPackageRemovedAllUsers(pkg, uid); + } } onPackageDisappeared(pkg, mChangeType); } @@ -344,5 +380,6 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { } onFinishPackageChanges(); + mChangeUserId = UserHandle.USER_NULL; } } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 3207435..8756950 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -22,14 +22,11 @@ import com.google.android.collect.Lists; import android.app.ActivityManagerNative; import android.app.admin.DevicePolicyManager; -import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.PackageManager; import android.os.Binder; -import android.os.FileObserver; import android.os.IBinder; import android.os.Process; import android.os.RemoteException; @@ -45,16 +42,10 @@ import android.util.Log; import android.view.View; import android.widget.Button; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; -import java.util.Arrays; import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; /** * Utilities for the lock pattern and its settings. @@ -134,7 +125,7 @@ public class LockPatternUtils { private final ContentResolver mContentResolver; private DevicePolicyManager mDevicePolicyManager; private ILockSettings mLockSettingsService; - private int mCurrentUserId = 0; + private int mCurrentUserId = UserHandle.USER_NULL; public DevicePolicyManager getDevicePolicyManager() { if (mDevicePolicyManager == null) { @@ -233,10 +224,14 @@ public class LockPatternUtils { public int getCurrentUser() { if (Process.myUid() == Process.SYSTEM_UID) { + if (mCurrentUserId != UserHandle.USER_NULL) { + // Someone is regularly updating using setCurrentUser() use that value. + return mCurrentUserId; + } try { return ActivityManagerNative.getDefault().getCurrentUser().id; } catch (RemoteException re) { - return mCurrentUserId; + return UserHandle.USER_OWNER; } } else { throw new SecurityException("Only the system process can get the current user"); @@ -1117,8 +1112,13 @@ public class LockPatternUtils { * {@link TelephonyManager#CALL_STATE_RINGING} * {@link TelephonyManager#CALL_STATE_OFFHOOK} * @param shown indicates whether the given screen wants the emergency button to show at all + * @param button + * @param phoneState + * @param shown shown if true; hidden if false + * @param upperCase if true, converts button label string to upper case */ - public void updateEmergencyCallButtonState(Button button, int phoneState, boolean shown) { + public void updateEmergencyCallButtonState(Button button, int phoneState, boolean shown, + boolean upperCase, boolean showIcon) { if (isEmergencyCallCapable() && shown) { button.setVisibility(View.VISIBLE); } else { @@ -1130,14 +1130,30 @@ public class LockPatternUtils { if (phoneState == TelephonyManager.CALL_STATE_OFFHOOK) { // show "return to call" text and show phone icon textId = R.string.lockscreen_return_to_call; - int phoneCallIcon = R.drawable.stat_sys_phone_call; + int phoneCallIcon = showIcon ? R.drawable.stat_sys_phone_call : 0; button.setCompoundDrawablesWithIntrinsicBounds(phoneCallIcon, 0, 0, 0); } else { textId = R.string.lockscreen_emergency_call; - int emergencyIcon = R.drawable.ic_emergency; + int emergencyIcon = showIcon ? R.drawable.ic_emergency : 0; button.setCompoundDrawablesWithIntrinsicBounds(emergencyIcon, 0, 0, 0); } - button.setText(textId); + if (upperCase) { + CharSequence original = mContext.getResources().getText(textId); + String upper = original != null ? original.toString().toUpperCase() : null; + button.setText(upper); + } else { + button.setText(textId); + } + } + + /** + * @deprecated + * @param button + * @param phoneState + * @param shown + */ + public void updateEmergencyCallButtonState(Button button, int phoneState, boolean shown) { + updateEmergencyCallButtonState(button, phoneState, shown, false, true); } /** |