diff options
Diffstat (limited to 'core/java/android')
47 files changed, 885 insertions, 295 deletions
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 67d3930..61b2067 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1701,6 +1701,21 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case GET_INTENT_FOR_INTENT_SENDER_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IIntentSender r = IIntentSender.Stub.asInterface( + data.readStrongBinder()); + Intent intent = getIntentForIntentSender(r); + reply.writeNoException(); + if (intent != null) { + reply.writeInt(1); + intent.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE); + } else { + reply.writeInt(0); + } + return true; + } + case UPDATE_PERSISTENT_CONFIGURATION_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); Configuration config = Configuration.CREATOR.createFromParcel(data); @@ -3977,6 +3992,20 @@ class ActivityManagerProxy implements IActivityManager return res; } + public Intent getIntentForIntentSender(IIntentSender sender) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(sender.asBinder()); + mRemote.transact(GET_INTENT_FOR_INTENT_SENDER_TRANSACTION, data, reply, 0); + reply.readException(); + Intent res = reply.readInt() != 0 + ? Intent.CREATOR.createFromParcel(reply) : null; + data.recycle(); + reply.recycle(); + return res; + } + public void updatePersistentConfiguration(Configuration values) throws RemoteException { Parcel data = Parcel.obtain(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 456d757..d880817 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4421,12 +4421,14 @@ public final class ActivityThread { new ArrayList<IActivityManager.ContentProviderHolder>(); for (ProviderInfo cpi : providers) { - StringBuilder buf = new StringBuilder(128); - buf.append("Pub "); - buf.append(cpi.authority); - buf.append(": "); - buf.append(cpi.name); - Log.i(TAG, buf.toString()); + if (DEBUG_PROVIDER) { + StringBuilder buf = new StringBuilder(128); + buf.append("Pub "); + buf.append(cpi.authority); + buf.append(": "); + buf.append(cpi.name); + Log.i(TAG, buf.toString()); + } IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi, false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/); if (cph != null) { diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 95b6bed..f895ccc 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -168,7 +168,7 @@ class ReceiverRestrictedContext extends ContextWrapper { * context object for Activity and other application components. */ class ContextImpl extends Context { - private final static String TAG = "ApplicationContext"; + private final static String TAG = "ContextImpl"; private final static boolean DEBUG = false; private static final HashMap<String, SharedPreferencesImpl> sSharedPrefs = @@ -1715,7 +1715,7 @@ class ContextImpl extends Context { private void warnIfCallingFromSystemProcess() { if (Process.myUid() == Process.SYSTEM_UID) { Slog.w(TAG, "Calling a method in the system process without a qualified user: " - + Debug.getCallers(3)); + + Debug.getCallers(5)); } } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 8fc1c86..8af17a4 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -341,6 +341,8 @@ public interface IActivityManager extends IInterface { public boolean isIntentSenderAnActivity(IIntentSender sender) throws RemoteException; + public Intent getIntentForIntentSender(IIntentSender sender) throws RemoteException; + public void updatePersistentConfiguration(Configuration values) throws RemoteException; public long[] getProcessPss(int[] pids) throws RemoteException; @@ -621,4 +623,5 @@ public interface IActivityManager extends IInterface { int REQUEST_BUG_REPORT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+157; int INPUT_DISPATCHING_TIMED_OUT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+158; int CLEAR_PENDING_BACKUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+159; + int GET_INTENT_FOR_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+160; } diff --git a/core/java/android/app/MediaRouteButton.java b/core/java/android/app/MediaRouteButton.java index 3ecafc3..a1a147a 100644 --- a/core/java/android/app/MediaRouteButton.java +++ b/core/java/android/app/MediaRouteButton.java @@ -217,7 +217,8 @@ public class MediaRouteButton extends View { void updateRemoteIndicator() { final RouteInfo selected = mRouter.getSelectedRoute(mRouteTypes); final boolean isRemote = selected != mRouter.getSystemAudioRoute(); - final boolean isConnecting = selected.getStatusCode() == RouteInfo.STATUS_CONNECTING; + final boolean isConnecting = selected != null && + selected.getStatusCode() == RouteInfo.STATUS_CONNECTING; boolean needsRefresh = false; if (mRemoteActive != isRemote) { diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 2c92d09..3f8e16c 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -322,7 +322,7 @@ public class Notification implements Parcelable /** * Bit to be bitwise-ored into the {@link #flags} field that should be * set if the notification should be canceled when it is clicked by the - * user. On tablets, the + * user. */ public static final int FLAG_AUTO_CANCEL = 0x00000010; @@ -388,8 +388,8 @@ public class Notification implements Parcelable * Priority is an indication of how much of the user's valuable attention should be consumed by * this notification. Low-priority notifications may be hidden from the user in certain * situations, while the user might be interrupted for a higher-priority notification. The - * system will make a determination about how to interpret notification priority as described in - * MUMBLE MUMBLE. + * system will make a determination about how to interpret this priority when presenting + * the notification. */ public int priority; @@ -846,7 +846,9 @@ public class Notification implements Parcelable } // TODO(dsandler): defaults take precedence over local values, so reorder the branches below sb.append(" vibrate="); - if (this.vibrate != null) { + if ((this.defaults & DEFAULT_VIBRATE) != 0) { + sb.append("default"); + } else if (this.vibrate != null) { int N = this.vibrate.length-1; sb.append("["); for (int i=0; i<N; i++) { @@ -857,16 +859,14 @@ public class Notification implements Parcelable sb.append(this.vibrate[N]); } sb.append("]"); - } else if ((this.defaults & DEFAULT_VIBRATE) != 0) { - sb.append("default"); } else { sb.append("null"); } sb.append(" sound="); - if (this.sound != null) { - sb.append(this.sound.toString()); - } else if ((this.defaults & DEFAULT_SOUND) != 0) { + if ((this.defaults & DEFAULT_SOUND) != 0) { sb.append("default"); + } else if (this.sound != null) { + sb.append(this.sound.toString()); } else { sb.append("null"); } diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index d36d99d..5c75aff 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -790,6 +790,20 @@ public final class PendingIntent implements Parcelable { } /** + * @hide + * Return the Intent of this PendingIntent. + */ + public Intent getIntent() { + try { + return ActivityManagerNative.getDefault() + .getIntentForIntentSender(mTarget); + } catch (RemoteException e) { + // Should never happen. + return null; + } + } + + /** * Comparison operator on two PendingIntent objects, such that true * is returned then they both represent the same operation from the * same package. This allows you to use {@link #getActivity}, diff --git a/core/java/android/app/Presentation.java b/core/java/android/app/Presentation.java index 16a0c57..bb45abb4 100644 --- a/core/java/android/app/Presentation.java +++ b/core/java/android/app/Presentation.java @@ -281,7 +281,7 @@ public class Presentation extends Dialog { private boolean isConfigurationStillValid() { DisplayMetrics dm = new DisplayMetrics(); mDisplay.getMetrics(dm); - return dm.equals(getResources().getDisplayMetrics()); + return dm.equalsPhysical(getResources().getDisplayMetrics()); } private static Context createPresentationContext( diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java index cb61a71..fa3bf4d 100644 --- a/core/java/android/appwidget/AppWidgetHost.java +++ b/core/java/android/appwidget/AppWidgetHost.java @@ -60,6 +60,7 @@ public class AppWidgetHost { public void updateAppWidget(int appWidgetId, RemoteViews views) { if (isLocalBinder() && views != null) { views = views.clone(); + views.setUser(mUser); } Message msg = mHandler.obtainMessage(HANDLE_UPDATE); msg.arg1 = appWidgetId; @@ -123,6 +124,8 @@ public class AppWidgetHost { Callbacks mCallbacks = new Callbacks(); final HashMap<Integer,AppWidgetHostView> mViews = new HashMap<Integer, AppWidgetHostView>(); private OnClickHandler mOnClickHandler; + // Optionally set by lockscreen + private UserHandle mUser; public AppWidgetHost(Context context, int hostId) { this(context, hostId, null, context.getMainLooper()); @@ -137,9 +140,15 @@ public class AppWidgetHost { mOnClickHandler = handler; mHandler = new UpdateHandler(looper); mDisplayMetrics = context.getResources().getDisplayMetrics(); + mUser = Process.myUserHandle(); bindService(); } + /** @hide */ + public void setUserId(int userId) { + mUser = new UserHandle(userId); + } + private static void bindService() { synchronized (sServiceLock) { if (sService == null) { @@ -154,6 +163,15 @@ public class AppWidgetHost { * becomes visible, i.e. from onStart() in your Activity. */ public void startListening() { + startListeningAsUser(UserHandle.myUserId()); + } + + /** + * Start receiving onAppWidgetChanged calls for your AppWidgets. Call this when your activity + * becomes visible, i.e. from onStart() in your Activity. + * @hide + */ + public void startListeningAsUser(int userId) { int[] updatedIds; ArrayList<RemoteViews> updatedViews = new ArrayList<RemoteViews>(); @@ -161,7 +179,8 @@ public class AppWidgetHost { if (mPackageName == null) { mPackageName = mContext.getPackageName(); } - updatedIds = sService.startListening(mCallbacks, mPackageName, mHostId, updatedViews); + updatedIds = sService.startListeningAsUser( + mCallbacks, mPackageName, mHostId, updatedViews, userId); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -169,6 +188,9 @@ public class AppWidgetHost { final int N = updatedIds.length; for (int i=0; i<N; i++) { + if (updatedViews.get(i) != null) { + updatedViews.get(i).setUser(new UserHandle(userId)); + } updateAppWidgetView(updatedIds[i], updatedViews.get(i)); } } @@ -179,11 +201,27 @@ public class AppWidgetHost { */ public void stopListening() { try { - sService.stopListening(mHostId); + sService.stopListeningAsUser(mHostId, UserHandle.myUserId()); + } + catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } + + /** + * Stop receiving onAppWidgetChanged calls for your AppWidgets. Call this when your activity is + * no longer visible, i.e. from onStop() in your Activity. + * @hide + */ + public void stopListeningAsUser(int userId) { + try { + sService.stopListeningAsUser(mHostId, userId); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); } + // Also clear the views + clearViews(); } /** @@ -224,6 +262,22 @@ public class AppWidgetHost { } } + /** + * Gets a list of all the appWidgetIds that are bound to the current host + * + * @hide + */ + public int[] getAppWidgetIds() { + try { + if (sService == null) { + bindService(); + } + return sService.getAppWidgetIdsForHost(mHostId); + } catch (RemoteException e) { + throw new RuntimeException("system server dead?", e); + } + } + private static void checkCallerIsSystem() { int uid = Process.myUid(); if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) { @@ -308,6 +362,7 @@ public class AppWidgetHost { public final AppWidgetHostView createView(Context context, int appWidgetId, AppWidgetProviderInfo appWidget) { AppWidgetHostView view = onCreateView(context, appWidgetId, appWidget); + view.setUserId(mUser.getIdentifier()); view.setOnClickHandler(mOnClickHandler); view.setAppWidget(appWidgetId, appWidget); synchronized (mViews) { @@ -316,6 +371,9 @@ public class AppWidgetHost { RemoteViews views; try { views = sService.getAppWidgetViews(appWidgetId); + if (views != null) { + views.setUser(mUser); + } } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); } diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 52771ee..700bba8 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -31,7 +31,9 @@ import android.os.Build; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.os.Process; import android.os.SystemClock; +import android.os.UserHandle; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; @@ -85,6 +87,7 @@ public class AppWidgetHostView extends FrameLayout { Bitmap mOld; Paint mOldPaint = new Paint(); private OnClickHandler mOnClickHandler; + private UserHandle mUser; /** * Create a host view. Uses default fade animations. @@ -112,12 +115,17 @@ public class AppWidgetHostView extends FrameLayout { public AppWidgetHostView(Context context, int animationIn, int animationOut) { super(context); mContext = context; - + mUser = Process.myUserHandle(); // We want to segregate the view ids within AppWidgets to prevent // problems when those ids collide with view ids in the AppWidgetHost. setIsRootNamespace(true); } + /** @hide */ + public void setUserId(int userId) { + mUser = new UserHandle(userId); + } + /** * Pass the given handler to RemoteViews when updating this widget. Unless this * is done immediatly after construction, a call to {@link #updateAppWidget(RemoteViews)} @@ -465,7 +473,8 @@ public class AppWidgetHostView extends FrameLayout { try { // Return if cloned successfully, otherwise default - return mContext.createPackageContext(packageName, Context.CONTEXT_RESTRICTED); + return mContext.createPackageContextAsUser(packageName, Context.CONTEXT_RESTRICTED, + mUser); } catch (NameNotFoundException e) { Log.e(TAG, "Package name " + packageName + " not found"); return mContext; @@ -539,8 +548,8 @@ public class AppWidgetHostView extends FrameLayout { try { if (mInfo != null) { - Context theirContext = mContext.createPackageContext( - mInfo.provider.getPackageName(), Context.CONTEXT_RESTRICTED); + Context theirContext = mContext.createPackageContextAsUser( + mInfo.provider.getPackageName(), Context.CONTEXT_RESTRICTED, mUser); mRemoteContext = theirContext; LayoutInflater inflater = (LayoutInflater) theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index 3dd640c..9c19766 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -23,6 +23,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.util.DisplayMetrics; import android.util.TypedValue; import android.widget.RemoteViews; @@ -544,8 +545,19 @@ public class AppWidgetManager { * Return a list of the AppWidget providers that are currently installed. */ public List<AppWidgetProviderInfo> getInstalledProviders() { + return getInstalledProviders(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN); + } + + /** + * Return a list of the AppWidget providers that are currently installed. + * + * @param categoryFilter Will only return providers which register as any of the specified + * specified categories. See {@link AppWidgetProviderInfo#widgetCategory}. + * @hide + */ + public List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter) { try { - List<AppWidgetProviderInfo> providers = sService.getInstalledProviders(); + List<AppWidgetProviderInfo> providers = sService.getInstalledProviders(categoryFilter); for (AppWidgetProviderInfo info : providers) { // Converting complex to dp. info.minWidth = @@ -738,11 +750,14 @@ public class AppWidgetManager { * @param intent The intent of the service which will be providing the data to the * RemoteViewsAdapter. * @param connection The callback interface to be notified when a connection is made or lost. + * @param userHandle The user to bind to. * @hide */ - public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) { + public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection, + UserHandle userHandle) { try { - sService.bindRemoteViewsService(appWidgetId, intent, connection); + sService.bindRemoteViewsService(appWidgetId, intent, connection, + userHandle.getIdentifier()); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); @@ -758,11 +773,12 @@ public class AppWidgetManager { * @param appWidgetId The AppWidget instance for which to bind the RemoteViewsService. * @param intent The intent of the service which will be providing the data to the * RemoteViewsAdapter. + * @param userHandle The user to unbind from. * @hide */ - public void unbindRemoteViewsService(int appWidgetId, Intent intent) { + public void unbindRemoteViewsService(int appWidgetId, Intent intent, UserHandle userHandle) { try { - sService.unbindRemoteViewsService(appWidgetId, intent); + sService.unbindRemoteViewsService(appWidgetId, intent, userHandle.getIdentifier()); } catch (RemoteException e) { throw new RuntimeException("system server dead?", e); diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index f817fb4..6367e16 100755 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1212,7 +1212,7 @@ public final class BluetoothAdapter { final private IBluetoothManagerCallback mManagerCallback = new IBluetoothManagerCallback.Stub() { public void onBluetoothServiceUp(IBluetooth bluetoothService) { - if (DBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); + if (VDBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService); synchronized (mManagerCallback) { mService = bluetoothService; for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ @@ -1228,7 +1228,7 @@ public final class BluetoothAdapter { } public void onBluetoothServiceDown() { - if (DBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); + if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService); synchronized (mManagerCallback) { mService = null; for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){ diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index 26bde19..8029a1a 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -300,7 +300,6 @@ public final class BluetoothSocket implements Closeable { if (mDevice == null) throw new IOException("Connect is called on null device"); try { - // TODO(BT) derive flag from auth and encrypt if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed"); IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null); if (bluetoothProxy == null) throw new IOException("Bluetooth is off"); @@ -349,7 +348,6 @@ public final class BluetoothSocket implements Closeable { mUuid, mPort, getSecurityFlags()); } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); - // TODO(BT) right error code? return -1; } @@ -388,8 +386,13 @@ public final class BluetoothSocket implements Closeable { /*package*/ BluetoothSocket accept(int timeout) throws IOException { BluetoothSocket acceptedSocket; if (mSocketState != SocketState.LISTENING) throw new IOException("bt socket is not in listen state"); - // TODO(BT) wait on an incoming connection + if(timeout > 0) { + Log.d(TAG, "accept() set timeout (ms):" + timeout); + mSocket.setSoTimeout(timeout); + } String RemoteAddr = waitSocketSignal(mSocketIS); + if(timeout > 0) + mSocket.setSoTimeout(0); synchronized(this) { if (mSocketState != SocketState.LISTENING) @@ -397,8 +400,6 @@ public final class BluetoothSocket implements Closeable { acceptedSocket = acceptSocket(RemoteAddr); //quick drop the reference of the file handle } - // TODO(BT) rfcomm socket only supports one connection, return this? - // return this; return acceptedSocket; } @@ -428,7 +429,7 @@ public final class BluetoothSocket implements Closeable { @Override public void close() throws IOException { - Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: " + mSocketState); + if (VDBG) Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: " + mSocketState); if(mSocketState == SocketState.CLOSED) return; else @@ -451,7 +452,6 @@ public final class BluetoothSocket implements Closeable { mPfd.detachFd(); } } - // TODO(BT) unbind proxy, } /*package */ void removeChannel() { @@ -471,6 +471,8 @@ public final class BluetoothSocket implements Closeable { ByteBuffer bb = ByteBuffer.wrap(sig); bb.order(ByteOrder.nativeOrder()); int size = bb.getShort(); + if(size != SOCK_SIGNAL_SIZE) + throw new IOException("Connection failure, wrong signal size: " + size); byte [] addr = new byte[6]; bb.get(addr); int channel = bb.getInt(); @@ -487,7 +489,7 @@ public final class BluetoothSocket implements Closeable { while(left > 0) { int ret = is.read(b, b.length - left, left); if(ret <= 0) - throw new IOException("read failed, socket might closed, read ret: " + ret); + throw new IOException("read failed, socket might closed or timeout, read ret: " + ret); left -= ret; if(left != 0) Log.w(TAG, "readAll() looping, read partial size: " + (b.length - left) + diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index 977b461..e4b4b97 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -58,6 +58,7 @@ import android.util.Pair; import android.util.Slog; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.IndentingPrintWriter; import com.google.android.collect.Lists; import com.google.android.collect.Maps; @@ -155,7 +156,7 @@ public class SyncManager { private SyncStorageEngine mSyncStorageEngine; - // @GuardedBy("mSyncQueue") + @GuardedBy("mSyncQueue") private final SyncQueue mSyncQueue; protected final ArrayList<ActiveSyncContext> mActiveSyncContexts = Lists.newArrayList(); diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java index 10e7bff..1ecab09 100644 --- a/core/java/android/content/SyncStorageEngine.java +++ b/core/java/android/content/SyncStorageEngine.java @@ -16,6 +16,7 @@ package android.content; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; @@ -63,6 +64,7 @@ import java.util.List; public class SyncStorageEngine extends Handler { private static final String TAG = "SyncManager"; + private static final boolean DEBUG = false; private static final boolean DEBUG_FILE = false; private static final String XML_ATTR_NEXT_AUTHORITY_ID = "nextAuthorityId"; @@ -74,7 +76,7 @@ public class SyncStorageEngine extends Handler { private static final long DEFAULT_POLL_FREQUENCY_SECONDS = 60 * 60 * 24; // One day - // @VisibleForTesting + @VisibleForTesting static final long MILLIS_IN_4WEEKS = 1000L * 60 * 60 * 24 * 7 * 4; /** Enum value for a sync start event. */ @@ -442,7 +444,7 @@ public class SyncStorageEngine extends Handler { mChangeListeners.finishBroadcast(); } - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (DEBUG) { Log.v(TAG, "reportChange " + which + " to: " + reports); } @@ -483,13 +485,17 @@ public class SyncStorageEngine extends Handler { public void setSyncAutomatically(Account account, int userId, String providerName, boolean sync) { - Log.d(TAG, "setSyncAutomatically: " + /* account + */" provider " + providerName - + ", user " + userId + " -> " + sync); + if (DEBUG) { + Log.d(TAG, "setSyncAutomatically: " + /* account + */" provider " + providerName + + ", user " + userId + " -> " + sync); + } synchronized (mAuthorities) { AuthorityInfo authority = getOrCreateAuthorityLocked(account, userId, providerName, -1, false); if (authority.enabled == sync) { - Log.d(TAG, "setSyncAutomatically: already set to " + sync + ", doing nothing"); + if (DEBUG) { + Log.d(TAG, "setSyncAutomatically: already set to " + sync + ", doing nothing"); + } return; } authority.enabled = sync; @@ -531,13 +537,17 @@ public class SyncStorageEngine extends Handler { } else if (syncable < -1) { syncable = -1; } - Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName - + ", user " + userId + " -> " + syncable); + if (DEBUG) { + Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName + + ", user " + userId + " -> " + syncable); + } synchronized (mAuthorities) { AuthorityInfo authority = getOrCreateAuthorityLocked(account, userId, providerName, -1, false); if (authority.syncable == syncable) { - Log.d(TAG, "setIsSyncable: already set to " + syncable + ", doing nothing"); + if (DEBUG) { + Log.d(TAG, "setIsSyncable: already set to " + syncable + ", doing nothing"); + } return; } authority.syncable = syncable; @@ -563,7 +573,7 @@ public class SyncStorageEngine extends Handler { public void setBackoff(Account account, int userId, String providerName, long nextSyncTime, long nextDelay) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (DEBUG) { Log.v(TAG, "setBackoff: " + account + ", provider " + providerName + ", user " + userId + " -> nextSyncTime " + nextSyncTime + ", nextDelay " + nextDelay); @@ -614,7 +624,7 @@ public class SyncStorageEngine extends Handler { for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) { if (authorityInfo.backoffTime != NOT_IN_BACKOFF_MODE || authorityInfo.backoffDelay != NOT_IN_BACKOFF_MODE) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (DEBUG) { Log.v(TAG, "clearAllBackoffs:" + " authority:" + authorityInfo.authority + " account:" + accountInfo.accountAndUser.account.name @@ -640,7 +650,7 @@ public class SyncStorageEngine extends Handler { public void setDelayUntilTime(Account account, int userId, String providerName, long delayUntil) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (DEBUG) { Log.v(TAG, "setDelayUntil: " + account + ", provider " + providerName + ", user " + userId + " -> delayUntil " + delayUntil); } @@ -676,7 +686,7 @@ public class SyncStorageEngine extends Handler { if (extras == null) { extras = new Bundle(); } - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (DEBUG) { Log.v(TAG, "addOrRemovePeriodicSync: " + account + ", user " + userId + ", provider " + providerName + " -> period " + period + ", extras " + extras); @@ -832,7 +842,7 @@ public class SyncStorageEngine extends Handler { public PendingOperation insertIntoPending(PendingOperation op) { synchronized (mAuthorities) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (DEBUG) { Log.v(TAG, "insertIntoPending: account=" + op.account + " user=" + op.userId + " auth=" + op.authority @@ -864,7 +874,7 @@ public class SyncStorageEngine extends Handler { public boolean deleteFromPending(PendingOperation op) { boolean res = false; synchronized (mAuthorities) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (DEBUG) { Log.v(TAG, "deleteFromPending: account=" + op.account + " user=" + op.userId + " auth=" + op.authority @@ -883,7 +893,7 @@ public class SyncStorageEngine extends Handler { AuthorityInfo authority = getAuthorityLocked(op.account, op.userId, op.authority, "deleteFromPending"); if (authority != null) { - if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "removing - " + authority); + if (DEBUG) Log.v(TAG, "removing - " + authority); final int N = mPendingOperations.size(); boolean morePending = false; for (int i=0; i<N; i++) { @@ -897,7 +907,7 @@ public class SyncStorageEngine extends Handler { } if (!morePending) { - if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "no more pending!"); + if (DEBUG) Log.v(TAG, "no more pending!"); SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident); status.pending = false; } @@ -937,7 +947,7 @@ public class SyncStorageEngine extends Handler { */ public void doDatabaseCleanup(Account[] accounts, int userId) { synchronized (mAuthorities) { - if (Log.isLoggable(TAG, Log.VERBOSE)) Log.w(TAG, "Updating for new accounts..."); + if (DEBUG) Log.v(TAG, "Updating for new accounts..."); SparseArray<AuthorityInfo> removing = new SparseArray<AuthorityInfo>(); Iterator<AccountInfo> accIt = mAccounts.values().iterator(); while (accIt.hasNext()) { @@ -945,8 +955,8 @@ public class SyncStorageEngine extends Handler { if (!ArrayUtils.contains(accounts, acc.accountAndUser.account) && acc.accountAndUser.userId == userId) { // This account no longer exists... - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.w(TAG, "Account removed: " + acc.accountAndUser); + if (DEBUG) { + Log.v(TAG, "Account removed: " + acc.accountAndUser); } for (AuthorityInfo auth : acc.authorities.values()) { removing.put(auth.ident, auth); @@ -992,7 +1002,7 @@ public class SyncStorageEngine extends Handler { public SyncInfo addActiveSync(SyncManager.ActiveSyncContext activeSyncContext) { final SyncInfo syncInfo; synchronized (mAuthorities) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (DEBUG) { Log.v(TAG, "setActiveSync: account=" + activeSyncContext.mSyncOperation.account + " auth=" + activeSyncContext.mSyncOperation.authority @@ -1020,7 +1030,7 @@ public class SyncStorageEngine extends Handler { */ public void removeActiveSync(SyncInfo syncInfo, int userId) { synchronized (mAuthorities) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (DEBUG) { Log.v(TAG, "removeActiveSync: account=" + syncInfo.account + " user=" + userId + " auth=" + syncInfo.authority); @@ -1045,7 +1055,7 @@ public class SyncStorageEngine extends Handler { long now, int source, boolean initialization) { long id; synchronized (mAuthorities) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (DEBUG) { Log.v(TAG, "insertStartSyncEvent: account=" + accountName + "user=" + userId + " auth=" + authorityName + " source=" + source); } @@ -1067,7 +1077,7 @@ public class SyncStorageEngine extends Handler { mSyncHistory.remove(mSyncHistory.size()-1); } id = item.historyId; - if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "returning historyId " + id); + if (DEBUG) Log.v(TAG, "returning historyId " + id); } reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS); @@ -1095,7 +1105,7 @@ public class SyncStorageEngine extends Handler { public void stopSyncEvent(long historyId, long elapsedTime, String resultMessage, long downstreamActivity, long upstreamActivity) { synchronized (mAuthorities) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (DEBUG) { Log.v(TAG, "stopSyncEvent: historyId=" + historyId); } SyncHistoryItem item = null; @@ -1357,7 +1367,7 @@ public class SyncStorageEngine extends Handler { AccountInfo accountInfo = mAccounts.get(au); if (accountInfo == null) { if (tag != null) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (DEBUG) { Log.v(TAG, tag + ": unknown account " + au); } } @@ -1366,7 +1376,7 @@ public class SyncStorageEngine extends Handler { AuthorityInfo authority = accountInfo.authorities.get(authorityName); if (authority == null) { if (tag != null) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (DEBUG) { Log.v(TAG, tag + ": unknown authority " + authorityName); } } @@ -1391,7 +1401,7 @@ public class SyncStorageEngine extends Handler { mNextAuthorityId++; doWrite = true; } - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (DEBUG) { Log.v(TAG, "created a new AuthorityInfo for " + accountName + ", user " + userId + ", provider " + authorityName); diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index b0ae5da..b9e432c 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -378,6 +378,7 @@ interface IPackageManager { VerifierDeviceIdentity getVerifierDeviceIdentity(); boolean isFirstBoot(); + boolean isOnlyCoreApps(); void setPermissionEnforced(String permission, boolean enforced); boolean isPermissionEnforced(String permission); diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index 6def4a1..aaa0917 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -34,6 +34,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.Xml; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.FastXmlSerializer; import com.google.android.collect.Lists; import com.google.android.collect.Maps; @@ -68,6 +69,7 @@ import java.util.Map; */ public abstract class RegisteredServicesCache<V> { private static final String TAG = "PackageManager"; + private static final boolean DEBUG = false; public final Context mContext; private final String mInterfaceName; @@ -77,15 +79,15 @@ public abstract class RegisteredServicesCache<V> { private final Object mServicesLock = new Object(); - // @GuardedBy("mServicesLock") + @GuardedBy("mServicesLock") private boolean mPersistentServicesFileDidNotExist; - // @GuardedBy("mServicesLock") + @GuardedBy("mServicesLock") private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(); private static class UserServices<V> { - // @GuardedBy("mServicesLock") + @GuardedBy("mServicesLock") public final Map<V, Integer> persistentServices = Maps.newHashMap(); - // @GuardedBy("mServicesLock") + @GuardedBy("mServicesLock") public Map<V, ServiceInfo<V>> services = null; } @@ -194,7 +196,7 @@ public abstract class RegisteredServicesCache<V> { } private void notifyListener(final V type, final int userId, final boolean removed) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (DEBUG) { Log.d(TAG, "notifyListener: " + type + " is " + (removed ? "removed" : "added")); } RegisteredServicesCacheListener<V> listener; @@ -290,7 +292,9 @@ public abstract class RegisteredServicesCache<V> { * given {@link UserHandle}. */ private void generateServicesMap(int userId) { - Slog.d(TAG, "generateServicesMap() for " + userId); + if (DEBUG) { + Slog.d(TAG, "generateServicesMap() for " + userId); + } final PackageManager pm = mContext.getPackageManager(); final ArrayList<ServiceInfo<V>> serviceInfos = new ArrayList<ServiceInfo<V>>(); @@ -321,6 +325,7 @@ public abstract class RegisteredServicesCache<V> { } StringBuilder changes = new StringBuilder(); + boolean changed = false; for (ServiceInfo<V> info : serviceInfos) { // four cases: // - doesn't exist yet @@ -333,33 +338,41 @@ public abstract class RegisteredServicesCache<V> { // - add, notify user that it was added Integer previousUid = user.persistentServices.get(info.type); if (previousUid == null) { - changes.append(" New service added: ").append(info).append("\n"); + if (DEBUG) { + changes.append(" New service added: ").append(info).append("\n"); + } + changed = true; user.services.put(info.type, info); user.persistentServices.put(info.type, info.uid); if (!(mPersistentServicesFileDidNotExist && firstScan)) { notifyListener(info.type, userId, false /* removed */); } } else if (previousUid == info.uid) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (DEBUG) { changes.append(" Existing service (nop): ").append(info).append("\n"); } user.services.put(info.type, info); } else if (inSystemImage(info.uid) || !containsTypeAndUid(serviceInfos, info.type, previousUid)) { - if (inSystemImage(info.uid)) { - changes.append(" System service replacing existing: ").append(info) - .append("\n"); - } else { - changes.append(" Existing service replacing a removed service: ") - .append(info).append("\n"); + if (DEBUG) { + if (inSystemImage(info.uid)) { + changes.append(" System service replacing existing: ").append(info) + .append("\n"); + } else { + changes.append(" Existing service replacing a removed service: ") + .append(info).append("\n"); + } } + changed = true; user.services.put(info.type, info); user.persistentServices.put(info.type, info.uid); notifyListener(info.type, userId, false /* removed */); } else { // ignore - changes.append(" Existing service with new uid ignored: ").append(info) - .append("\n"); + if (DEBUG) { + changes.append(" Existing service with new uid ignored: ").append(info) + .append("\n"); + } } } @@ -370,22 +383,25 @@ public abstract class RegisteredServicesCache<V> { } } for (V v1 : toBeRemoved) { + if (DEBUG) { + changes.append(" Service removed: ").append(v1).append("\n"); + } + changed = true; user.persistentServices.remove(v1); - changes.append(" Service removed: ").append(v1).append("\n"); notifyListener(v1, userId, true /* removed */); } - if (changes.length() > 0) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { + if (DEBUG) { + if (changes.length() > 0) { Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " + serviceInfos.size() + " services:\n" + changes); - } - writePersistentServicesLocked(); - } else { - if (Log.isLoggable(TAG, Log.VERBOSE)) { + } else { Log.d(TAG, "generateServicesMap(" + mInterfaceName + "): " + serviceInfos.size() + " services unchanged"); } } + if (changed) { + writePersistentServicesLocked(); + } } } diff --git a/core/java/android/hardware/display/WifiDisplay.java b/core/java/android/hardware/display/WifiDisplay.java index 0138b1c..2fd52b8 100644 --- a/core/java/android/hardware/display/WifiDisplay.java +++ b/core/java/android/hardware/display/WifiDisplay.java @@ -107,6 +107,15 @@ public final class WifiDisplay implements Parcelable { && Objects.equal(mDeviceAlias, other.mDeviceAlias); } + /** + * Returns true if the other display is not null and has the same address as this one. + * Can be used to perform identity comparisons on displays ignoring properties + * that might change during a connection such as the name or alias. + */ + public boolean hasSameAddress(WifiDisplay other) { + return other != null && mDeviceAddress.equals(other.mDeviceAddress); + } + @Override public int hashCode() { // The address on its own should be sufficiently unique for hashing purposes. diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java index 3a06dc0..37601fc 100644 --- a/core/java/android/net/EthernetDataTracker.java +++ b/core/java/android/net/EthernetDataTracker.java @@ -74,7 +74,7 @@ public class EthernetDataTracker implements NetworkStateTracker { } public void interfaceLinkStateChanged(String iface, boolean up) { - if (mIface.equals(iface) && mLinkUp != up) { + if (mIface.equals(iface)) { Log.d(TAG, "Interface " + iface + " link " + (up ? "up" : "down")); mLinkUp = up; mTracker.mNetworkInfo.setIsAvailable(up); diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 446bbf0..c757605 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.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Objects; @@ -190,14 +191,14 @@ public class NetworkStats implements Parcelable { return clone; } - // @VisibleForTesting + @VisibleForTesting public NetworkStats addIfaceValues( String iface, long rxBytes, long rxPackets, long txBytes, long txPackets) { return addValues( iface, UID_ALL, SET_DEFAULT, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, 0L); } - // @VisibleForTesting + @VisibleForTesting public NetworkStats addValues(String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) { return addValues(new Entry( @@ -269,7 +270,7 @@ public class NetworkStats implements Parcelable { return size; } - // @VisibleForTesting + @VisibleForTesting public int internalSize() { return iface.length; } @@ -335,7 +336,7 @@ public class NetworkStats implements Parcelable { * Find first stats index that matches the requested parameters, starting * search around the hinted index as an optimization. */ - // @VisibleForTesting + @VisibleForTesting public int findIndexHinted(String iface, int uid, int set, int tag, int hintIndex) { for (int offset = 0; offset < size; offset++) { final int halfOffset = offset / 2; diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index d8e53d5..d3839ad 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -33,6 +33,7 @@ import android.content.res.Resources; import android.os.Parcel; import android.os.Parcelable; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Objects; /** @@ -63,7 +64,7 @@ public class NetworkTemplate implements Parcelable { private static boolean sForceAllNetworkTypes = false; - // @VisibleForTesting + @VisibleForTesting public static void forceAllNetworkTypes() { sForceAllNetworkTypes = true; } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 54f2fe3..9821824 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -18,6 +18,8 @@ package android.os; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.Formatter; import java.util.List; import java.util.Map; @@ -1127,8 +1129,10 @@ public abstract class BatteryStats implements Parcelable { if (totalTimeMillis != 0) { sb.append(linePrefix); formatTimeMs(sb, totalTimeMillis); - if (name != null) sb.append(name); - sb.append(' '); + if (name != null) { + sb.append(name); + sb.append(' '); + } sb.append('('); sb.append(count); sb.append(" times)"); @@ -1440,8 +1444,21 @@ public abstract class BatteryStats implements Parcelable { } } + static final class TimerEntry { + final String mName; + final int mId; + final BatteryStats.Timer mTimer; + final long mTime; + TimerEntry(String name, int id, BatteryStats.Timer timer, long time) { + mName = name; + mId = id; + mTimer = timer; + mTime = time; + } + } + @SuppressWarnings("unused") - public final void dumpLocked(PrintWriter pw, String prefix, int which, int reqUid) { + public final void dumpLocked(PrintWriter pw, String prefix, final int which, int reqUid) { final long rawUptime = SystemClock.uptimeMillis() * 1000; final long rawRealtime = SystemClock.elapsedRealtime() * 1000; final long batteryUptime = getBatteryUptime(rawUptime); @@ -1516,19 +1533,43 @@ public abstract class BatteryStats implements Parcelable { long txTotal = 0; long fullWakeLockTimeTotalMicros = 0; long partialWakeLockTimeTotalMicros = 0; - + + final Comparator<TimerEntry> timerComparator = new Comparator<TimerEntry>() { + @Override + public int compare(TimerEntry lhs, TimerEntry rhs) { + long lhsTime = lhs.mTime; + long rhsTime = rhs.mTime; + if (lhsTime < rhsTime) { + return 1; + } + if (lhsTime > rhsTime) { + return -1; + } + return 0; + } + }; + if (reqUid < 0) { Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats(); if (kernelWakelocks.size() > 0) { + final ArrayList<TimerEntry> timers = new ArrayList<TimerEntry>(); for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) { - + BatteryStats.Timer timer = ent.getValue(); + long totalTimeMillis = computeWakeLock(timer, batteryRealtime, which); + if (totalTimeMillis > 0) { + timers.add(new TimerEntry(ent.getKey(), 0, timer, totalTimeMillis)); + } + } + Collections.sort(timers, timerComparator); + for (int i=0; i<timers.size(); i++) { + TimerEntry timer = timers.get(i); String linePrefix = ": "; sb.setLength(0); sb.append(prefix); sb.append(" Kernel Wake lock "); - sb.append(ent.getKey()); - linePrefix = printWakeLock(sb, ent.getValue(), batteryRealtime, null, which, - linePrefix); + sb.append(timer.mName); + linePrefix = printWakeLock(sb, timer.mTimer, batteryRealtime, null, + which, linePrefix); if (!linePrefix.equals(": ")) { sb.append(" realtime"); // Only print out wake locks that were held @@ -1537,7 +1578,9 @@ public abstract class BatteryStats implements Parcelable { } } } - + + final ArrayList<TimerEntry> timers = new ArrayList<TimerEntry>(); + for (int iu = 0; iu < NU; iu++) { Uid u = uidStats.valueAt(iu); rxTotal += u.getTcpBytesReceived(which); @@ -1557,8 +1600,18 @@ public abstract class BatteryStats implements Parcelable { Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL); if (partialWakeTimer != null) { - partialWakeLockTimeTotalMicros += partialWakeTimer.getTotalTimeLocked( + long totalTimeMicros = partialWakeTimer.getTotalTimeLocked( batteryRealtime, which); + if (totalTimeMicros > 0) { + if (reqUid < 0) { + // Only show the ordered list of all wake + // locks if the caller is not asking for data + // about a specific uid. + timers.add(new TimerEntry(ent.getKey(), u.getUid(), + partialWakeTimer, totalTimeMicros)); + } + partialWakeLockTimeTotalMicros += totalTimeMicros; + } } } } @@ -1571,7 +1624,7 @@ public abstract class BatteryStats implements Parcelable { sb.append(prefix); sb.append(" Total full wakelock time: "); formatTimeMs(sb, (fullWakeLockTimeTotalMicros + 500) / 1000); - sb.append(", Total partial waklock time: "); formatTimeMs(sb, + sb.append(", Total partial wakelock time: "); formatTimeMs(sb, (partialWakeLockTimeTotalMicros + 500) / 1000); pw.println(sb.toString()); @@ -1676,9 +1729,26 @@ public abstract class BatteryStats implements Parcelable { pw.println(getDischargeAmountScreenOnSinceCharge()); pw.print(prefix); pw.print(" Amount discharged while screen off: "); pw.println(getDischargeAmountScreenOffSinceCharge()); - pw.println(" "); + pw.println(); + } + + if (timers.size() > 0) { + Collections.sort(timers, timerComparator); + pw.print(prefix); pw.println(" All partial wake locks:"); + for (int i=0; i<timers.size(); i++) { + TimerEntry timer = timers.get(i); + sb.setLength(0); + sb.append(" Wake lock #"); + sb.append(timer.mId); + sb.append(" "); + sb.append(timer.mName); + printWakeLock(sb, timer.mTimer, batteryRealtime, null, which, ": "); + sb.append(" realtime"); + pw.println(sb.toString()); + } + timers.clear(); + pw.println(); } - for (int iu=0; iu<NU; iu++) { final int uid = uidStats.keyAt(iu); diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index 51cb91c..460a5fe 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -796,6 +796,7 @@ public final class Bundle implements Parcelable, Cloneable { * no mapping of the desired type exists for the given key. * * @param key a String + * @param defaultValue Value to return if key does not exist * @return a boolean value */ public boolean getBoolean(String key, boolean defaultValue) { @@ -829,6 +830,7 @@ public final class Bundle implements Parcelable, Cloneable { * no mapping of the desired type exists for the given key. * * @param key a String + * @param defaultValue Value to return if key does not exist * @return a byte value */ public Byte getByte(String key, byte defaultValue) { @@ -846,7 +848,7 @@ public final class Bundle implements Parcelable, Cloneable { } /** - * Returns the value associated with the given key, or false if + * Returns the value associated with the given key, or (char) 0 if * no mapping of the desired type exists for the given key. * * @param key a String @@ -858,10 +860,11 @@ public final class Bundle implements Parcelable, Cloneable { } /** - * Returns the value associated with the given key, or (char) 0 if + * Returns the value associated with the given key, or defaultValue if * no mapping of the desired type exists for the given key. * * @param key a String + * @param defaultValue Value to return if key does not exist * @return a char value */ public char getChar(String key, char defaultValue) { @@ -895,6 +898,7 @@ public final class Bundle implements Parcelable, Cloneable { * no mapping of the desired type exists for the given key. * * @param key a String + * @param defaultValue Value to return if key does not exist * @return a short value */ public short getShort(String key, short defaultValue) { @@ -928,6 +932,7 @@ public final class Bundle implements Parcelable, Cloneable { * no mapping of the desired type exists for the given key. * * @param key a String + * @param defaultValue Value to return if key does not exist * @return an int value */ public int getInt(String key, int defaultValue) { @@ -961,6 +966,7 @@ public final class Bundle implements Parcelable, Cloneable { * no mapping of the desired type exists for the given key. * * @param key a String + * @param defaultValue Value to return if key does not exist * @return a long value */ public long getLong(String key, long defaultValue) { @@ -994,6 +1000,7 @@ public final class Bundle implements Parcelable, Cloneable { * no mapping of the desired type exists for the given key. * * @param key a String + * @param defaultValue Value to return if key does not exist * @return a float value */ public float getFloat(String key, float defaultValue) { @@ -1027,6 +1034,7 @@ public final class Bundle implements Parcelable, Cloneable { * no mapping of the desired type exists for the given key. * * @param key a String + * @param defaultValue Value to return if key does not exist * @return a double value */ public double getDouble(String key, double defaultValue) { diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 88529f8..1bada67 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -22,6 +22,8 @@ import android.os.storage.StorageVolume; import android.text.TextUtils; import android.util.Log; +import com.android.internal.annotations.GuardedBy; + import java.io.File; /** @@ -47,7 +49,7 @@ public class Environment { private static final Object sLock = new Object(); - // @GuardedBy("sLock") + @GuardedBy("sLock") private static volatile StorageVolume sPrimaryVolume; private static StorageVolume getPrimaryVolume() { diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index 3e90dfc..ec660ee 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -15,6 +15,9 @@ */ package android.os; + +import dalvik.system.CloseGuard; + import java.io.Closeable; import java.io.File; import java.io.FileDescriptor; @@ -31,12 +34,16 @@ import java.net.Socket; */ public class ParcelFileDescriptor implements Parcelable, Closeable { private final FileDescriptor mFileDescriptor; - private boolean mClosed; - //this field is to create wrapper for ParcelFileDescriptor using another - //PartialFileDescriptor but avoid invoking close twice - //consider ParcelFileDescriptor A(fileDescriptor fd), ParcelFileDescriptor B(A) - //in this particular case fd.close might be invoked twice. - private final ParcelFileDescriptor mParcelDescriptor; + + /** + * Wrapped {@link ParcelFileDescriptor}, if any. Used to avoid + * double-closing {@link #mFileDescriptor}. + */ + private final ParcelFileDescriptor mWrapped; + + private volatile boolean mClosed; + + private final CloseGuard mGuard = CloseGuard.get(); /** * For use with {@link #open}: if {@link #MODE_CREATE} has been supplied @@ -289,13 +296,15 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { if (mClosed) { throw new IllegalStateException("Already closed"); } - if (mParcelDescriptor != null) { - int fd = mParcelDescriptor.detachFd(); + if (mWrapped != null) { + int fd = mWrapped.detachFd(); mClosed = true; + mGuard.close(); return fd; } int fd = getFd(); mClosed = true; + mGuard.close(); Parcel.clearFileDescriptor(mFileDescriptor); return fd; } @@ -307,15 +316,16 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { * @throws IOException * If an error occurs attempting to close this ParcelFileDescriptor. */ + @Override public void close() throws IOException { - synchronized (this) { - if (mClosed) return; - mClosed = true; - } - if (mParcelDescriptor != null) { + if (mClosed) return; + mClosed = true; + mGuard.close(); + + if (mWrapped != null) { // If this is a proxy to another file descriptor, just call through to its // close method. - mParcelDescriptor.close(); + mWrapped.close(); } else { Parcel.closeFileDescriptor(mFileDescriptor); } @@ -374,6 +384,9 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { @Override protected void finalize() throws Throwable { + if (mGuard != null) { + mGuard.warnIfOpen(); + } try { if (!mClosed) { close(); @@ -384,21 +397,22 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { } public ParcelFileDescriptor(ParcelFileDescriptor descriptor) { - super(); - mParcelDescriptor = descriptor; - mFileDescriptor = mParcelDescriptor.mFileDescriptor; + mWrapped = descriptor; + mFileDescriptor = mWrapped.mFileDescriptor; + mGuard.open("close"); } - /*package */ParcelFileDescriptor(FileDescriptor descriptor) { - super(); + /** {@hide} */ + public ParcelFileDescriptor(FileDescriptor descriptor) { if (descriptor == null) { throw new NullPointerException("descriptor must not be null"); } + mWrapped = null; mFileDescriptor = descriptor; - mParcelDescriptor = null; + mGuard.open("close"); } - /* Parcelable interface */ + @Override public int describeContents() { return Parcelable.CONTENTS_FILE_DESCRIPTOR; } @@ -408,6 +422,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { * If {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set in flags, * the file descriptor will be closed after a copy is written to the Parcel. */ + @Override public void writeToParcel(Parcel out, int flags) { out.writeFileDescriptor(mFileDescriptor); if ((flags&PARCELABLE_WRITE_RETURN_VALUE) != 0 && !mClosed) { @@ -421,12 +436,14 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { public static final Parcelable.Creator<ParcelFileDescriptor> CREATOR = new Parcelable.Creator<ParcelFileDescriptor>() { + @Override public ParcelFileDescriptor createFromParcel(Parcel in) { return in.readFileDescriptor(); } + + @Override public ParcelFileDescriptor[] newArray(int size) { return new ParcelFileDescriptor[size]; } }; - } diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 4a01113..736762f 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -182,6 +182,8 @@ public final class PowerManager { * </p><p> * Since not all devices have proximity sensors, use {@link #isWakeLockLevelSupported} * to determine whether this wake lock level is supported. + * </p><p> + * Cannot be used with {@link #ACQUIRE_CAUSES_WAKEUP}. * </p> * * {@hide} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index b94f0b9..dc089bd 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -626,6 +626,21 @@ public final class Settings { public static final String ACTION_NFCSHARING_SETTINGS = "android.settings.NFCSHARING_SETTINGS"; + /** + * Activity Action: Show Daydream settings. + * <p> + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + * @see android.service.dreams.DreamService + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_DREAM_SETTINGS = "android.settings.DREAM_SETTINGS"; + // End of Intent actions for Settings /** @@ -5315,6 +5330,12 @@ public final class Settings { public static final String DOCK_AUDIO_MEDIA_ENABLED = "dock_audio_media_enabled"; /** + * Persisted safe headphone volume management state by AudioService + * @hide + */ + public static final String AUDIO_SAFE_VOLUME_STATE = "audio_safe_volume_state"; + + /** * Settings to backup. This is here so that it's in the same place as the settings * keys and easy to update. * diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java index 4a21374..46f2723 100644 --- a/core/java/android/server/search/SearchManagerService.java +++ b/core/java/android/server/search/SearchManagerService.java @@ -92,7 +92,7 @@ public class SearchManagerService extends ISearchManager.Stub { Searchables searchables = mSearchables.get(userId); if (searchables == null) { - Log.i(TAG, "Building list of searchable activities for userId=" + userId); + //Log.i(TAG, "Building list of searchable activities for userId=" + userId); searchables = new Searchables(mContext, userId); searchables.buildSearchableList(); mSearchables.append(userId, searchables); diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 6c9290b..f6b6c89 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -44,13 +44,13 @@ import android.view.accessibility.AccessibilityEvent; import com.android.internal.policy.PolicyManager; /** - * Extend this class to implement a custom Dream (displayed to the user as a "Sleep Mode"). + * Extend this class to implement a custom dream (available to the user as a "Daydream"). * * <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> * - * <p>The Dream lifecycle is as follows:</p> + * <p>The {@code DreamService} lifecycle is as follows:</p> * <ol> * <li>{@link #onAttachedToWindow} * <p>Use this for initial setup, such as calling {@link #setContentView setContentView()}.</li> @@ -59,14 +59,15 @@ import com.android.internal.policy.PolicyManager; * <li>{@link #onDreamingStopped} * <p>Use this to stop the things you started in {@link #onDreamingStarted}.</li> * <li>{@link #onDetachedFromWindow} - * <p>Use this to dismantle resources your dream set up. For example, detach from handlers - * and listeners.</li> + * <p>Use this to dismantle resources (for example, detach from handlers + * and listeners).</li> * </ol> * * <p>In addition, onCreate and onDestroy (from the Service interface) will also be called, but * initialization and teardown should be done by overriding the hooks above.</p> * - * <p>To be available to the system, Dreams should be declared in the manifest as follows:</p> + * <p>To be available to the system, your {@code DreamService} should be declared in the + * manifest as follows:</p> * <pre> * <service * android:name=".MyDream" diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java index 1060bd8..bcce61d 100644 --- a/core/java/android/text/format/DateUtils.java +++ b/core/java/android/text/format/DateUtils.java @@ -607,6 +607,30 @@ public class DateUtils } /** + * Return given duration in a human-friendly format. For example, "4 + * minutes" or "1 second". Returns only largest meaningful unit of time, + * from seconds up to hours. + * + * @hide + */ + public static CharSequence formatDuration(long millis) { + final Resources res = Resources.getSystem(); + if (millis >= HOUR_IN_MILLIS) { + final int hours = (int) ((millis + 1800000) / HOUR_IN_MILLIS); + return res.getQuantityString( + com.android.internal.R.plurals.duration_hours, hours, hours); + } else if (millis >= MINUTE_IN_MILLIS) { + final int minutes = (int) ((millis + 30000) / MINUTE_IN_MILLIS); + return res.getQuantityString( + com.android.internal.R.plurals.duration_minutes, minutes, minutes); + } else { + final int seconds = (int) ((millis + 500) / SECOND_IN_MILLIS); + return res.getQuantityString( + com.android.internal.R.plurals.duration_seconds, seconds, seconds); + } + } + + /** * Formats an elapsed time in the form "MM:SS" or "H:MM:SS" * for display on the call-in-progress screen. * @param elapsedSeconds the elapsed time in seconds. diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index 85e4b9d..e856501 100644 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -232,19 +232,32 @@ public class DisplayMetrics { * @return True if the display metrics are equal. */ public boolean equals(DisplayMetrics other) { + return equalsPhysical(other) + && scaledDensity == other.scaledDensity + && noncompatScaledDensity == other.noncompatScaledDensity; + } + + /** + * Returns true if the physical aspects of the two display metrics + * are equal. This ignores the scaled density, which is a logical + * attribute based on the current desired font size. + * + * @param other The display metrics with which to compare. + * @return True if the display metrics are equal. + * @hide + */ + public boolean equalsPhysical(DisplayMetrics other) { return other != null && widthPixels == other.widthPixels && heightPixels == other.heightPixels && density == other.density && densityDpi == other.densityDpi - && scaledDensity == other.scaledDensity && xdpi == other.xdpi && ydpi == other.ydpi && noncompatWidthPixels == other.noncompatWidthPixels && noncompatHeightPixels == other.noncompatHeightPixels && noncompatDensity == other.noncompatDensity && noncompatDensityDpi == other.noncompatDensityDpi - && noncompatScaledDensity == other.noncompatScaledDensity && noncompatXdpi == other.noncompatXdpi && noncompatYdpi == other.noncompatYdpi; } diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index f3841d5..305fd5c 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -285,6 +285,16 @@ public final class DisplayInfo implements Parcelable { getMetricsWithSize(outMetrics, cih, logicalWidth, logicalHeight); } + public int getNaturalWidth() { + return rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 ? + logicalWidth : logicalHeight; + } + + public int getNaturalHeight() { + return rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180 ? + logicalHeight : logicalWidth; + } + private void getMetricsWithSize(DisplayMetrics outMetrics, CompatibilityInfoHolder cih, int width, int height) { outMetrics.densityDpi = outMetrics.noncompatDensityDpi = logicalDensityDpi; diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 1c613245..5b7a5af 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -1304,17 +1304,11 @@ public abstract class HardwareRenderer { } } - if ((status & DisplayList.STATUS_INVOKE) != 0) { - scheduleFunctors(attachInfo, true); - } - } - - private void scheduleFunctors(View.AttachInfo attachInfo, boolean delayed) { - mFunctorsRunnable.attachInfo = attachInfo; - if (!attachInfo.mHandler.hasCallbacks(mFunctorsRunnable)) { - // delay the functor callback by a few ms so it isn't polled constantly - attachInfo.mHandler.postDelayed(mFunctorsRunnable, - delayed ? FUNCTOR_PROCESS_DELAY : 0); + if ((status & DisplayList.STATUS_INVOKE) != 0 || + attachInfo.mHandler.hasCallbacks(mFunctorsRunnable)) { + attachInfo.mHandler.removeCallbacks(mFunctorsRunnable); + mFunctorsRunnable.attachInfo = attachInfo; + attachInfo.mHandler.postDelayed(mFunctorsRunnable, FUNCTOR_PROCESS_DELAY); } } @@ -1329,7 +1323,9 @@ public abstract class HardwareRenderer { boolean attachFunctor(View.AttachInfo attachInfo, int functor) { if (mCanvas != null) { mCanvas.attachFunctor(functor); - scheduleFunctors(attachInfo, false); + mFunctorsRunnable.attachInfo = attachInfo; + attachInfo.mHandler.removeCallbacks(mFunctorsRunnable); + attachInfo.mHandler.postDelayed(mFunctorsRunnable, 0); return true; } return false; diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 0fe2a8e..2b6cbcf 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -74,7 +74,7 @@ interface IWindowManager void setEventDispatching(boolean enabled); void addWindowToken(IBinder token, int type); void removeWindowToken(IBinder token); - void addAppToken(int addPos, IApplicationToken token, + void addAppToken(int addPos, int userId, IApplicationToken token, int groupId, int requestedOrientation, boolean fullscreen, boolean showWhenLocked); void setAppGroupId(IBinder token, int groupId); void setAppOrientation(IApplicationToken token, int requestedOrientation); diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java index ee3f5d8..51c5c7b 100644 --- a/core/java/android/view/ScaleGestureDetector.java +++ b/core/java/android/view/ScaleGestureDetector.java @@ -259,6 +259,8 @@ public class ScaleGestureDetector { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } + mCurrTime = event.getEventTime(); + final int action = event.getActionMasked(); final boolean streamComplete = action == MotionEvent.ACTION_UP || @@ -341,6 +343,7 @@ public class ScaleGestureDetector { mPrevSpanX = mCurrSpanX = spanX; mPrevSpanY = mCurrSpanY = spanY; mPrevSpan = mCurrSpan = span; + mPrevTime = mCurrTime; mInProgress = mListener.onScaleBegin(this); } @@ -359,6 +362,7 @@ public class ScaleGestureDetector { mPrevSpanX = mCurrSpanX; mPrevSpanY = mCurrSpanY; mPrevSpan = mCurrSpan; + mPrevTime = mCurrTime; } } diff --git a/core/java/android/view/SimulatedTrackball.java b/core/java/android/view/SimulatedTrackball.java index bd472cf..b917371 100644 --- a/core/java/android/view/SimulatedTrackball.java +++ b/core/java/android/view/SimulatedTrackball.java @@ -41,6 +41,8 @@ class SimulatedTrackball { // Where the cutoff is for determining an edge swipe private static final float EDGE_SWIPE_THRESHOLD = 0.9f; private static final int FLICK_MSG_ID = 313; + // TODO: Pass touch slop from the input device + private static final int TOUCH_SLOP = 30; // The position of the previous touchpad event private float mLastTouchpadXPosition; @@ -95,7 +97,7 @@ class SimulatedTrackball { mMinFlickDistanceSquared *= mMinFlickDistanceSquared; mFlickDecay = Float.parseFloat(SystemProperties.get( "persist.sys.vr_flick_decay", "1.3")); - mTouchSlop = ViewConfiguration.getTouchSlop(); + mTouchSlop = TOUCH_SLOP; mTouchSlopSquared = mTouchSlop * mTouchSlop; } diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 550a740..0a81a71 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -225,6 +225,9 @@ public class Surface implements Parcelable { // non compatibility mode. private Matrix mCompatibleMatrix; + private int mWidth; + private int mHeight; + private native void nativeCreate(SurfaceSession session, String name, int w, int h, int format, int flags) throws OutOfResourcesException; @@ -330,6 +333,8 @@ public class Surface implements Parcelable { checkHeadless(); mName = name; + mWidth = w; + mHeight = h; nativeCreate(session, name, w, h, format, flags); mCloseGuard.open("release"); @@ -538,7 +543,7 @@ public class Surface implements Parcelable { /** @hide */ public void setPosition(int x, int y) { - nativeSetPosition((float)x, (float)y); + nativeSetPosition(x, y); } /** @hide */ @@ -548,10 +553,22 @@ public class Surface implements Parcelable { /** @hide */ public void setSize(int w, int h) { + mWidth = w; + mHeight = h; nativeSetSize(w, h); } /** @hide */ + public int getWidth() { + return mWidth; + } + + /** @hide */ + public int getHeight() { + return mHeight; + } + + /** @hide */ public void hide() { nativeSetFlags(SURFACE_HIDDEN, SURFACE_HIDDEN); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ff44475..f05371a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -623,6 +623,7 @@ import java.util.concurrent.atomic.AtomicInteger; * @attr ref android.R.styleable#View_hapticFeedbackEnabled * @attr ref android.R.styleable#View_keepScreenOn * @attr ref android.R.styleable#View_layerType + * @attr ref android.R.styleable#View_layoutDirection * @attr ref android.R.styleable#View_longClickable * @attr ref android.R.styleable#View_minHeight * @attr ref android.R.styleable#View_minWidth @@ -660,6 +661,7 @@ import java.util.concurrent.atomic.AtomicInteger; * @attr ref android.R.styleable#View_soundEffectsEnabled * @attr ref android.R.styleable#View_tag * @attr ref android.R.styleable#View_textAlignment + * @attr ref android.R.styleable#View_textDirection * @attr ref android.R.styleable#View_transformPivotX * @attr ref android.R.styleable#View_transformPivotY * @attr ref android.R.styleable#View_translationX @@ -5854,6 +5856,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link #LAYOUT_DIRECTION_RTL}, * {@link #LAYOUT_DIRECTION_INHERIT} or * {@link #LAYOUT_DIRECTION_LOCALE}. + * * @attr ref android.R.styleable#View_layoutDirection * * @hide @@ -5909,6 +5912,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * For compatibility, this will return {@link #LAYOUT_DIRECTION_LTR} if API version * is lower than {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}. + * + * @attr ref android.R.styleable#View_layoutDirection */ @ViewDebug.ExportedProperty(category = "layout", mapping = { @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR, to = "RESOLVED_DIRECTION_LTR"), @@ -7010,10 +7015,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int current = getAccessibilityCursorPosition(); if (current == ACCESSIBILITY_CURSOR_POSITION_UNDEFINED) { current = text.length(); + setAccessibilityCursorPosition(current); } else if (granularity == AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER) { // When traversing by character we always put the cursor after the character // to ease edit and have to compensate before asking the for previous segment. current--; + setAccessibilityCursorPosition(current); } final int[] range = iterator.preceding(current); if (range == null) { @@ -11849,8 +11856,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mCurrentAnimation = null; - resetRtlProperties(); - onRtlPropertiesChanged(LAYOUT_DIRECTION_DEFAULT); resetAccessibilityStateChanged(); } @@ -16627,6 +16632,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link #TEXT_DIRECTION_RTL}, * {@link #TEXT_DIRECTION_LOCALE} * + * @attr ref android.R.styleable#View_textDirection + * * @hide */ @ViewDebug.ExportedProperty(category = "text", mapping = { @@ -16656,6 +16663,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Resolution will be done if the value is set to TEXT_DIRECTION_INHERIT. The resolution * proceeds up the parent chain of the view to get the value. If there is no parent, then it will * return the default {@link #TEXT_DIRECTION_FIRST_STRONG}. + * + * @attr ref android.R.styleable#View_textDirection */ public void setTextDirection(int textDirection) { if (getRawTextDirection() != textDirection) { @@ -16684,6 +16693,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link #TEXT_DIRECTION_LTR}, * {@link #TEXT_DIRECTION_RTL}, * {@link #TEXT_DIRECTION_LOCALE} + * + * @attr ref android.R.styleable#View_textDirection */ public int getTextDirection() { return (mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_RESOLVED_MASK) >> PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT; @@ -16816,6 +16827,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link #TEXT_ALIGNMENT_VIEW_START}, * {@link #TEXT_ALIGNMENT_VIEW_END} * + * @attr ref android.R.styleable#View_textAlignment + * * @hide */ @ViewDebug.ExportedProperty(category = "text", mapping = { @@ -16879,6 +16892,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link #TEXT_ALIGNMENT_TEXT_END}, * {@link #TEXT_ALIGNMENT_VIEW_START}, * {@link #TEXT_ALIGNMENT_VIEW_END} + * + * @attr ref android.R.styleable#View_textAlignment */ @ViewDebug.ExportedProperty(category = "text", mapping = { @ViewDebug.IntToString(from = TEXT_ALIGNMENT_INHERIT, to = "INHERIT"), diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 00723f3..dbbcde6 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -3620,8 +3620,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager childHasTransientStateChanged(view, false); } - view.resetRtlProperties(); - onViewRemoved(view); needGlobalAttributesUpdate(false); @@ -5372,21 +5370,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @hide */ @Override - public void resetRtlProperties() { - super.resetRtlProperties(); - int count = getChildCount(); - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.isLayoutDirectionInherited()) { - child.resetRtlProperties(); - } - } - } - - /** - * @hide - */ - @Override public void resetResolvedLayoutDirection() { super.resetResolvedLayoutDirection(); diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index 421a324..452ad1b 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -248,6 +248,15 @@ public abstract class CompoundButton extends Button implements Checkable { return padding; } + /** + * @hide + */ + @Override + public int getHorizontalOffsetForDrawables() { + final Drawable buttonDrawable = mButtonDrawable; + return (buttonDrawable != null) ? buttonDrawable.getIntrinsicWidth() : 0; + } + @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index b1a44c5..85972c3 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -291,6 +291,7 @@ public class Editor { mErrorWasChanged = true; if (mError == null) { + setErrorIcon(null); if (mErrorPopup != null) { if (mErrorPopup.isShowing()) { mErrorPopup.dismiss(); @@ -299,21 +300,24 @@ public class Editor { mErrorPopup = null; } - setErrorIcon(null); - } else if (mTextView.isFocused()) { - showError(); + } else { setErrorIcon(icon); + if (mTextView.isFocused()) { + showError(); + } } } private void setErrorIcon(Drawable icon) { - final Drawables dr = mTextView.mDrawables; - if (dr != null) { - mTextView.setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop, icon, - dr.mDrawableBottom); - } else { - mTextView.setCompoundDrawables(null, null, icon, null); + Drawables dr = mTextView.mDrawables; + if (dr == null) { + mTextView.mDrawables = dr = new Drawables(); } + dr.setErrorDrawable(icon, mTextView); + + mTextView.resetResolvedDrawables(); + mTextView.invalidate(); + mTextView.requestLayout(); } private void hideError() { @@ -321,15 +325,13 @@ public class Editor { if (mErrorPopup.isShowing()) { mErrorPopup.dismiss(); } - - setErrorIcon(null); } mShowErrorAfterAttach = false; } /** - * Returns the Y offset to make the pointy top of the error point + * Returns the X offset to make the pointy top of the error point * at the middle of the error icon. */ private int getErrorX() { @@ -340,8 +342,23 @@ public class Editor { final float scale = mTextView.getResources().getDisplayMetrics().density; final Drawables dr = mTextView.mDrawables; - return mTextView.getWidth() - mErrorPopup.getWidth() - mTextView.getPaddingRight() - - (dr != null ? dr.mDrawableSizeRight : 0) / 2 + (int) (25 * scale + 0.5f); + + final int layoutDirection = mTextView.getLayoutDirection(); + int errorX; + int offset; + switch (layoutDirection) { + default: + case View.LAYOUT_DIRECTION_LTR: + offset = - (dr != null ? dr.mDrawableSizeRight : 0) / 2 + (int) (25 * scale + 0.5f); + errorX = mTextView.getWidth() - mErrorPopup.getWidth() - + mTextView.getPaddingRight() + offset; + break; + case View.LAYOUT_DIRECTION_RTL: + offset = (dr != null ? dr.mDrawableSizeLeft : 0) / 2 - (int) (25 * scale + 0.5f); + errorX = mTextView.getPaddingLeft() + offset; + break; + } + return errorX; } /** @@ -358,16 +375,27 @@ public class Editor { mTextView.getCompoundPaddingBottom() - compoundPaddingTop; final Drawables dr = mTextView.mDrawables; - int icontop = compoundPaddingTop + - (vspace - (dr != null ? dr.mDrawableHeightRight : 0)) / 2; + + final int layoutDirection = mTextView.getLayoutDirection(); + int height; + switch (layoutDirection) { + default: + case View.LAYOUT_DIRECTION_LTR: + height = (dr != null ? dr.mDrawableHeightRight : 0); + break; + case View.LAYOUT_DIRECTION_RTL: + height = (dr != null ? dr.mDrawableHeightLeft : 0); + break; + } + + int icontop = compoundPaddingTop + (vspace - height) / 2; /* * The "2" is the distance between the point and the top edge * of the background. */ final float scale = mTextView.getResources().getDisplayMetrics().density; - return icontop + (dr != null ? dr.mDrawableHeightRight : 0) - mTextView.getHeight() - - (int) (2 * scale + 0.5f); + return icontop + height - mTextView.getHeight() - (int) (2 * scale + 0.5f); } void createInputContentTypeIfNeeded() { @@ -3726,7 +3754,7 @@ public class Editor { super(v, width, height); mView = v; // Make sure the TextView has a background set as it will be used the first time it is - // shown and positionned. Initialized with below background, which should have + // shown and positioned. Initialized with below background, which should have // dimensions identical to the above version for this to work (and is more likely). mPopupInlineErrorBackgroundId = getResourceId(mPopupInlineErrorBackgroundId, com.android.internal.R.styleable.Theme_errorMessageBackground); diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index b6f0862..36dd13c 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -1340,7 +1340,7 @@ public class LinearLayout extends ViewGroup { private void forceUniformHeight(int count, int widthMeasureSpec) { // Pretend that the linear layout has an exact size. This is the measured height of // ourselves. The measured height should be the max height of the children, changed - // to accomodate the heightMesureSpec from the parent + // to accommodate the heightMeasureSpec from the parent int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY); for (int i = 0; i < count; ++i) { diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index e52e84d..27fda24 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -369,10 +369,10 @@ public class RelativeLayout extends ViewGroup { int width = 0; int height = 0; - int widthMode = MeasureSpec.getMode(widthMeasureSpec); - int heightMode = MeasureSpec.getMode(heightMeasureSpec); - int widthSize = MeasureSpec.getSize(widthMeasureSpec); - int heightSize = MeasureSpec.getSize(heightMeasureSpec); + final int widthMode = MeasureSpec.getMode(widthMeasureSpec); + final int heightMode = MeasureSpec.getMode(heightMeasureSpec); + final int widthSize = MeasureSpec.getSize(widthMeasureSpec); + final int heightSize = MeasureSpec.getSize(heightMeasureSpec); // Record our dimensions if they are known; if (widthMode != MeasureSpec.UNSPECIFIED) { @@ -416,6 +416,42 @@ public class RelativeLayout extends ViewGroup { View[] views = mSortedHorizontalChildren; int count = views.length; + + // We need to know our size for doing the correct computation of positioning in RTL mode + if (isLayoutRtl() && (myWidth == -1 || isWrapContentWidth)) { + int w = getPaddingStart() + getPaddingEnd(); + final int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + for (int i = 0; i < count; i++) { + View child = views[i]; + if (child.getVisibility() != GONE) { + LayoutParams params = (LayoutParams) child.getLayoutParams(); + // Would be similar to a call to measureChildHorizontal(child, params, -1, myHeight) + // but we cannot change for now the behavior of measureChildHorizontal() for + // taking care or a "-1" for "mywidth" so use here our own version of that code. + int childHeightMeasureSpec; + if (params.width == LayoutParams.MATCH_PARENT) { + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY); + } else { + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST); + } + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + + w += child.getMeasuredWidth(); + w += params.leftMargin + params.rightMargin; + } + } + if (myWidth == -1) { + // Easy case: "myWidth" was undefined before so use the width we have just computed + myWidth = w; + } else { + // "myWidth" was defined before, so take the min of it and the computed width if it + // is a non null one + if (w > 0) { + myWidth = Math.min(myWidth, w); + } + } + } + for (int i = 0; i < count; i++) { View child = views[i]; if (child.getVisibility() != GONE) { @@ -924,7 +960,7 @@ public class RelativeLayout extends ViewGroup { // Find the first non-GONE view up the chain while (v.getVisibility() == View.GONE) { - rules = ((LayoutParams) v.getLayoutParams()).getRules(); + rules = ((LayoutParams) v.getLayoutParams()).getRules(v.getLayoutDirection()); node = mGraph.mKeyNodes.get((rules[relation])); if (node == null) return null; v = node.view; @@ -975,7 +1011,7 @@ public class RelativeLayout extends ViewGroup { protected void onLayout(boolean changed, int l, int t, int r, int b) { // The layout has actually already been performed and the positions // cached. Apply the cached values to the children. - int count = getChildCount(); + final int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java index e481702..aeee111 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; + import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.Intent; @@ -29,9 +30,10 @@ import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.Process; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; -import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.view.View.MeasureSpec; @@ -40,6 +42,7 @@ import android.widget.RemoteViews.OnClickHandler; import com.android.internal.widget.IRemoteViewsAdapterConnection; import com.android.internal.widget.IRemoteViewsFactory; +import com.android.internal.widget.LockPatternUtils; /** * An adapter to a RemoteViewsService which fetches and caches RemoteViews @@ -87,13 +90,15 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback private Handler mMainQueue; // We cache the FixedSizeRemoteViewsCaches across orientation. These are the related data - // structures; - private static final HashMap<Pair<Intent.FilterComparison, Integer>, FixedSizeRemoteViewsCache> - sCachedRemoteViewsCaches = new HashMap<Pair<Intent.FilterComparison, Integer>, + // structures; + private static final HashMap<RemoteViewsCacheKey, + FixedSizeRemoteViewsCache> sCachedRemoteViewsCaches + = new HashMap<RemoteViewsCacheKey, FixedSizeRemoteViewsCache>(); - private static final HashMap<Pair<Intent.FilterComparison, Integer>, Runnable> - sRemoteViewsCacheRemoveRunnables = new HashMap<Pair<Intent.FilterComparison, Integer>, - Runnable>(); + private static final HashMap<RemoteViewsCacheKey, Runnable> + sRemoteViewsCacheRemoveRunnables + = new HashMap<RemoteViewsCacheKey, Runnable>(); + private static HandlerThread sCacheRemovalThread; private static Handler sCacheRemovalQueue; @@ -106,6 +111,8 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback // construction (happens when we have a cached FixedSizeRemoteViewsCache). private boolean mDataReady = false; + int mUserId; + /** * An interface for the RemoteAdapter to notify other classes when adapters * are actually connected to/disconnected from their actual services. @@ -146,8 +153,16 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback public synchronized void bind(Context context, int appWidgetId, Intent intent) { if (!mIsConnecting) { try { + RemoteViewsAdapter adapter; final AppWidgetManager mgr = AppWidgetManager.getInstance(context); - mgr.bindRemoteViewsService(appWidgetId, intent, asBinder()); + if (Process.myUid() == Process.SYSTEM_UID + && (adapter = mAdapter.get()) != null) { + mgr.bindRemoteViewsService(appWidgetId, intent, asBinder(), + new UserHandle(adapter.mUserId)); + } else { + mgr.bindRemoteViewsService(appWidgetId, intent, asBinder(), + Process.myUserHandle()); + } mIsConnecting = true; } catch (Exception e) { Log.e("RemoteViewsAdapterServiceConnection", "bind(): " + e.getMessage()); @@ -159,8 +174,15 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback public synchronized void unbind(Context context, int appWidgetId, Intent intent) { try { + RemoteViewsAdapter adapter; final AppWidgetManager mgr = AppWidgetManager.getInstance(context); - mgr.unbindRemoteViewsService(appWidgetId, intent); + if (Process.myUid() == Process.SYSTEM_UID + && (adapter = mAdapter.get()) != null) { + mgr.unbindRemoteViewsService(appWidgetId, intent, + new UserHandle(adapter.mUserId)); + } else { + mgr.unbindRemoteViewsService(appWidgetId, intent, Process.myUserHandle()); + } mIsConnecting = false; } catch (Exception e) { Log.e("RemoteViewsAdapterServiceConnection", "unbind(): " + e.getMessage()); @@ -296,9 +318,13 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback */ private class RemoteViewsFrameLayoutRefSet { private HashMap<Integer, LinkedList<RemoteViewsFrameLayout>> mReferences; + private HashMap<RemoteViewsFrameLayout, LinkedList<RemoteViewsFrameLayout>> + mViewToLinkedList; public RemoteViewsFrameLayoutRefSet() { mReferences = new HashMap<Integer, LinkedList<RemoteViewsFrameLayout>>(); + mViewToLinkedList = + new HashMap<RemoteViewsFrameLayout, LinkedList<RemoteViewsFrameLayout>>(); } /** @@ -315,6 +341,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback refs = new LinkedList<RemoteViewsFrameLayout>(); mReferences.put(pos, refs); } + mViewToLinkedList.put(layout, refs); // Add the references to the list refs.add(layout); @@ -333,21 +360,34 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback final LinkedList<RemoteViewsFrameLayout> refs = mReferences.get(pos); for (final RemoteViewsFrameLayout ref : refs) { ref.onRemoteViewsLoaded(view, mRemoteViewsOnClickHandler); + if (mViewToLinkedList.containsKey(ref)) { + mViewToLinkedList.remove(ref); + } } refs.clear(); - // Remove this set from the original mapping mReferences.remove(pos); } } /** + * We need to remove views from this set if they have been recycled by the AdapterView. + */ + public void removeView(RemoteViewsFrameLayout rvfl) { + if (mViewToLinkedList.containsKey(rvfl)) { + mViewToLinkedList.get(rvfl).remove(rvfl); + mViewToLinkedList.remove(rvfl); + } + } + + /** * Removes all references to all RemoteViewsFrameLayouts returned by the adapter. */ public void clear() { // We currently just clear the references, and leave all the previous layouts returned // in their default state of the loading view. mReferences.clear(); + mViewToLinkedList.clear(); } } @@ -751,6 +791,33 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback } } + static class RemoteViewsCacheKey { + final Intent.FilterComparison filter; + final int widgetId; + final int userId; + + RemoteViewsCacheKey(Intent.FilterComparison filter, int widgetId, int userId) { + this.filter = filter; + this.widgetId = widgetId; + this.userId = userId; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof RemoteViewsCacheKey)) { + return false; + } + RemoteViewsCacheKey other = (RemoteViewsCacheKey) o; + return other.filter.equals(filter) && other.widgetId == widgetId + && other.userId == userId; + } + + @Override + public int hashCode() { + return (filter == null ? 0 : filter.hashCode()) ^ (widgetId << 2) ^ (userId << 10); + } + } + public RemoteViewsAdapter(Context context, Intent intent, RemoteAdapterConnectionCallback callback) { mContext = context; mIntent = intent; @@ -761,6 +828,11 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback } mRequestedViews = new RemoteViewsFrameLayoutRefSet(); + if (Process.myUid() == Process.SYSTEM_UID) { + mUserId = new LockPatternUtils(context).getCurrentUser(); + } else { + mUserId = UserHandle.myUserId(); + } // Strip the previously injected app widget id from service intent if (intent.hasExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID)) { intent.removeExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID); @@ -782,8 +854,8 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback); mServiceConnection = new RemoteViewsAdapterServiceConnection(this); - Pair<Intent.FilterComparison, Integer> key = new Pair<Intent.FilterComparison, Integer> - (new Intent.FilterComparison(mIntent), mAppWidgetId); + RemoteViewsCacheKey key = new RemoteViewsCacheKey(new Intent.FilterComparison(mIntent), + mAppWidgetId, mUserId); synchronized(sCachedRemoteViewsCaches) { if (sCachedRemoteViewsCaches.containsKey(key)) { @@ -824,8 +896,8 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback } public void saveRemoteViewsCache() { - final Pair<Intent.FilterComparison, Integer> key = new Pair<Intent.FilterComparison, - Integer> (new Intent.FilterComparison(mIntent), mAppWidgetId); + final RemoteViewsCacheKey key = new RemoteViewsCacheKey( + new Intent.FilterComparison(mIntent), mAppWidgetId, mUserId); synchronized(sCachedRemoteViewsCaches) { // If we already have a remove runnable posted for this key, remove it. @@ -947,6 +1019,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback long itemId = 0; try { remoteViews = factory.getViewAt(position); + remoteViews.setUser(new UserHandle(mUserId)); itemId = factory.getItemId(position); } catch (RemoteException e) { Log.e(TAG, "Error in updateRemoteViews(" + position + "): " + e.getMessage()); @@ -1079,6 +1152,10 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback boolean isConnected = mServiceConnection.isConnected(); boolean hasNewItems = false; + if (convertView != null && convertView instanceof RemoteViewsFrameLayout) { + mRequestedViews.removeView((RemoteViewsFrameLayout) convertView); + } + if (!isInCache && !isConnected) { // Requesting bind service will trigger a super.notifyDataSetChanged(), which will // in turn trigger another request to getView() diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 5d90400..22bfadb 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -284,15 +284,144 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private TextUtils.TruncateAt mEllipsize; static class Drawables { + final static int DRAWABLE_NONE = -1; + final static int DRAWABLE_RIGHT = 0; + final static int DRAWABLE_LEFT = 1; + final Rect mCompoundRect = new Rect(); + Drawable mDrawableTop, mDrawableBottom, mDrawableLeft, mDrawableRight, - mDrawableStart, mDrawableEnd; + mDrawableStart, mDrawableEnd, mDrawableError, mDrawableTemp; + int mDrawableSizeTop, mDrawableSizeBottom, mDrawableSizeLeft, mDrawableSizeRight, - mDrawableSizeStart, mDrawableSizeEnd; + mDrawableSizeStart, mDrawableSizeEnd, mDrawableSizeError, mDrawableSizeTemp; + int mDrawableWidthTop, mDrawableWidthBottom, mDrawableHeightLeft, mDrawableHeightRight, - mDrawableHeightStart, mDrawableHeightEnd; + mDrawableHeightStart, mDrawableHeightEnd, mDrawableHeightError, mDrawableHeightTemp; + int mDrawablePadding; + + int mDrawableSaved = DRAWABLE_NONE; + + public void resolveWithLayoutDirection(int layoutDirection) { + switch(layoutDirection) { + case LAYOUT_DIRECTION_RTL: + if (mDrawableStart != null) { + mDrawableRight = mDrawableStart; + + mDrawableSizeRight = mDrawableSizeStart; + mDrawableHeightRight = mDrawableHeightStart; + } + if (mDrawableEnd != null) { + mDrawableLeft = mDrawableEnd; + + mDrawableSizeLeft = mDrawableSizeEnd; + mDrawableHeightLeft = mDrawableHeightEnd; + } + break; + + case LAYOUT_DIRECTION_LTR: + default: + if (mDrawableStart != null) { + mDrawableLeft = mDrawableStart; + + mDrawableSizeLeft = mDrawableSizeStart; + mDrawableHeightLeft = mDrawableHeightStart; + } + if (mDrawableEnd != null) { + mDrawableRight = mDrawableEnd; + + mDrawableSizeRight = mDrawableSizeEnd; + mDrawableHeightRight = mDrawableHeightEnd; + } + break; + } + applyErrorDrawableIfNeeded(layoutDirection); + updateDrawablesLayoutDirection(layoutDirection); + } + + private void updateDrawablesLayoutDirection(int layoutDirection) { + if (mDrawableLeft != null) { + mDrawableLeft.setLayoutDirection(layoutDirection); + } + if (mDrawableRight != null) { + mDrawableRight.setLayoutDirection(layoutDirection); + } + if (mDrawableTop != null) { + mDrawableTop.setLayoutDirection(layoutDirection); + } + if (mDrawableBottom != null) { + mDrawableBottom.setLayoutDirection(layoutDirection); + } + } + + public void setErrorDrawable(Drawable dr, TextView tv) { + if (mDrawableError != dr && mDrawableError != null) { + mDrawableError.setCallback(null); + } + mDrawableError = dr; + + final Rect compoundRect = mCompoundRect; + int[] state = tv.getDrawableState(); + + if (mDrawableError != null) { + mDrawableError.setState(state); + mDrawableError.copyBounds(compoundRect); + mDrawableError.setCallback(tv); + mDrawableSizeError = compoundRect.width(); + mDrawableHeightError = compoundRect.height(); + } else { + mDrawableSizeError = mDrawableHeightError = 0; + } + } + + private void applyErrorDrawableIfNeeded(int layoutDirection) { + // first restore the initial state if needed + switch (mDrawableSaved) { + case DRAWABLE_LEFT: + mDrawableLeft = mDrawableTemp; + mDrawableSizeLeft = mDrawableSizeTemp; + mDrawableHeightLeft = mDrawableHeightTemp; + break; + case DRAWABLE_RIGHT: + mDrawableRight = mDrawableTemp; + mDrawableSizeRight = mDrawableSizeTemp; + mDrawableHeightRight = mDrawableHeightTemp; + break; + case DRAWABLE_NONE: + default: + } + // then, if needed, assign the Error drawable to the correct location + if (mDrawableError != null) { + switch(layoutDirection) { + case LAYOUT_DIRECTION_RTL: + mDrawableSaved = DRAWABLE_LEFT; + + mDrawableTemp = mDrawableLeft; + mDrawableSizeTemp = mDrawableSizeLeft; + mDrawableHeightTemp = mDrawableHeightLeft; + + mDrawableLeft = mDrawableError; + mDrawableSizeLeft = mDrawableSizeError; + mDrawableHeightLeft = mDrawableHeightError; + break; + case LAYOUT_DIRECTION_LTR: + default: + mDrawableSaved = DRAWABLE_RIGHT; + + mDrawableTemp = mDrawableRight; + mDrawableSizeTemp = mDrawableSizeRight; + mDrawableHeightTemp = mDrawableHeightRight; + + mDrawableRight = mDrawableError; + mDrawableSizeRight = mDrawableSizeError; + mDrawableHeightRight = mDrawableHeightError; + break; + } + } + } } + Drawables mDrawables; private CharWrapper mCharWrapper; @@ -4734,6 +4863,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return highlight; } + /** + * @hide + */ + public int getHorizontalOffsetForDrawables() { + return 0; + } + @Override protected void onDraw(Canvas canvas) { restartMarqueeIfNeeded(); @@ -4751,6 +4887,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int left = mLeft; final int bottom = mBottom; final int top = mTop; + final boolean isLayoutRtl = isLayoutRtl(); + final int offset = getHorizontalOffsetForDrawables(); + final int leftOffset = isLayoutRtl ? 0 : offset; + final int rightOffset = isLayoutRtl ? offset : 0 ; final Drawables dr = mDrawables; if (dr != null) { @@ -4766,7 +4906,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Make sure to update invalidateDrawable() when changing this code. if (dr.mDrawableLeft != null) { canvas.save(); - canvas.translate(scrollX + mPaddingLeft, + canvas.translate(scrollX + mPaddingLeft + leftOffset, scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightLeft) / 2); dr.mDrawableLeft.draw(canvas); @@ -4777,7 +4917,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Make sure to update invalidateDrawable() when changing this code. if (dr.mDrawableRight != null) { canvas.save(); - canvas.translate(scrollX + right - left - mPaddingRight - dr.mDrawableSizeRight, + canvas.translate(scrollX + right - left - mPaddingRight + - dr.mDrawableSizeRight - rightOffset, scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightRight) / 2); dr.mDrawableRight.draw(canvas); canvas.restore(); @@ -4862,8 +5003,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } canvas.translate(compoundPaddingLeft, extendedPaddingTop + voffsetText); - final boolean isLayoutRtl = isLayoutRtl(); - final int layoutDirection = getLayoutDirection(); final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection); if (mEllipsize == TextUtils.TruncateAt.MARQUEE && @@ -8229,9 +8368,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener TextDirectionHeuristic getTextDirectionHeuristic() { if (hasPasswordTransformationMethod()) { - // TODO: take care of the content direction to show the password text and dots justified - // to the left or to the right - return TextDirectionHeuristics.LOCALE; + // passwords fields should be LTR + return TextDirectionHeuristics.LTR; } // Always need to resolve layout direction first @@ -8264,63 +8402,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return; } mLastLayoutDirection = layoutDirection; - // No drawable to resolve - if (mDrawables == null) { - return; - } - // No relative drawable to resolve - if (mDrawables.mDrawableStart == null && mDrawables.mDrawableEnd == null) { - return; - } - - Drawables dr = mDrawables; - switch(layoutDirection) { - case LAYOUT_DIRECTION_RTL: - if (dr.mDrawableStart != null) { - dr.mDrawableRight = dr.mDrawableStart; - dr.mDrawableSizeRight = dr.mDrawableSizeStart; - dr.mDrawableHeightRight = dr.mDrawableHeightStart; - } - if (dr.mDrawableEnd != null) { - dr.mDrawableLeft = dr.mDrawableEnd; - - dr.mDrawableSizeLeft = dr.mDrawableSizeEnd; - dr.mDrawableHeightLeft = dr.mDrawableHeightEnd; - } - break; - - case LAYOUT_DIRECTION_LTR: - default: - if (dr.mDrawableStart != null) { - dr.mDrawableLeft = dr.mDrawableStart; - - dr.mDrawableSizeLeft = dr.mDrawableSizeStart; - dr.mDrawableHeightLeft = dr.mDrawableHeightStart; - } - if (dr.mDrawableEnd != null) { - dr.mDrawableRight = dr.mDrawableEnd; - - dr.mDrawableSizeRight = dr.mDrawableSizeEnd; - dr.mDrawableHeightRight = dr.mDrawableHeightEnd; - } - break; - } - updateDrawablesLayoutDirection(dr, layoutDirection); - } - - private void updateDrawablesLayoutDirection(Drawables dr, int layoutDirection) { - if (dr.mDrawableLeft != null) { - dr.mDrawableLeft.setLayoutDirection(layoutDirection); - } - if (dr.mDrawableRight != null) { - dr.mDrawableRight.setLayoutDirection(layoutDirection); - } - if (dr.mDrawableTop != null) { - dr.mDrawableTop.setLayoutDirection(layoutDirection); - } - if (dr.mDrawableBottom != null) { - dr.mDrawableBottom.setLayoutDirection(layoutDirection); + // Resolve drawables + if (mDrawables != null) { + mDrawables.resolveWithLayoutDirection(layoutDirection); } } @@ -8328,6 +8413,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @hide */ protected void resetResolvedDrawables() { + super.resetResolvedDrawables(); mLastLayoutDirection = -1; } diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index 485bd37..1d85126 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -374,8 +374,11 @@ public class Toast { // remove the old view if necessary handleHide(); mView = mNextView; - mWM = (WindowManager)mView.getContext().getApplicationContext() - .getSystemService(Context.WINDOW_SERVICE); + Context context = mView.getContext().getApplicationContext(); + if (context == null) { + context = mView.getContext(); + } + mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); // We can resolve the Gravity here by using the Locale for getting // the layout direction final Configuration config = mView.getContext().getResources().getConfiguration(); diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java index 7c8196d..329b0df 100644 --- a/core/java/android/widget/VideoView.java +++ b/core/java/android/widget/VideoView.java @@ -54,7 +54,6 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { // settable by the client private Uri mUri; private Map<String, String> mHeaders; - private int mDuration; // all possible internal states private static final int STATE_ERROR = -1; @@ -229,7 +228,6 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { mMediaPlayer = new MediaPlayer(); mMediaPlayer.setOnPreparedListener(mPreparedListener); mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener); - mDuration = -1; mMediaPlayer.setOnCompletionListener(mCompletionListener); mMediaPlayer.setOnErrorListener(mErrorListener); mMediaPlayer.setOnInfoListener(mOnInfoListener); @@ -608,17 +606,12 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { openVideo(); } - // cache duration as mDuration for faster access public int getDuration() { if (isInPlaybackState()) { - if (mDuration > 0) { - return mDuration; - } - mDuration = mMediaPlayer.getDuration(); - return mDuration; + return mMediaPlayer.getDuration(); } - mDuration = -1; - return mDuration; + + return -1; } public int getCurrentPosition() { |
