diff options
93 files changed, 2226 insertions, 861 deletions
diff --git a/Android.mk b/Android.mk index fdf0933f4638..d4dc08847ae3 100644 --- a/Android.mk +++ b/Android.mk @@ -183,7 +183,8 @@ LOCAL_SRC_FILES += \ media/java/android/media/IAudioFocusDispatcher.aidl \ media/java/android/media/IMediaScannerListener.aidl \ media/java/android/media/IMediaScannerService.aidl \ - media/java/android/media/IRemoteControlClientDispatcher.aidl \ + media/java/android/media/IRemoteControlClient.aidl \ + media/java/android/media/IRemoteControlDisplay.aidl \ telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \ telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \ telephony/java/com/android/internal/telephony/ITelephony.aidl \ diff --git a/CleanSpec.mk b/CleanSpec.mk index 25930652e51f..3cec66ff1584 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -105,6 +105,7 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framew $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/SystemUI_intermediates) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/R/com/android/systemui/R.java) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java/android/media/IAudioService.P) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java/android/media/IAudioService.P) # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ diff --git a/api/current.txt b/api/current.txt index cd85eea7db6e..fb16830b8f64 100644 --- a/api/current.txt +++ b/api/current.txt @@ -958,6 +958,8 @@ package android { field public static final int textAppearanceLarge = 16842816; // 0x1010040 field public static final int textAppearanceLargeInverse = 16842819; // 0x1010043 field public static final int textAppearanceLargePopupMenu = 16843521; // 0x1010301 + field public static final int textAppearanceListItem = 16843688; // 0x10103a8 + field public static final int textAppearanceListItemSmall = 16843689; // 0x10103a9 field public static final int textAppearanceMedium = 16842817; // 0x1010041 field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044 field public static final int textAppearanceSearchResultSubtitle = 16843424; // 0x10102a0 @@ -7205,6 +7207,7 @@ package android.database.sqlite { ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int); ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int, android.database.DatabaseErrorHandler); method public synchronized void close(); + method public java.lang.String getDatabaseName(); method public synchronized android.database.sqlite.SQLiteDatabase getReadableDatabase(); method public synchronized android.database.sqlite.SQLiteDatabase getWritableDatabase(); method public abstract void onCreate(android.database.sqlite.SQLiteDatabase); @@ -9362,6 +9365,7 @@ package android.hardware { field public static final int FOCUS_DISTANCE_NEAR_INDEX = 0; // 0x0 field public static final int FOCUS_DISTANCE_OPTIMAL_INDEX = 1; // 0x1 field public static final java.lang.String FOCUS_MODE_AUTO = "auto"; + field public static final java.lang.String FOCUS_MODE_CONTINUOUS_PICTURE = "continuous-picture"; field public static final java.lang.String FOCUS_MODE_CONTINUOUS_VIDEO = "continuous-video"; field public static final java.lang.String FOCUS_MODE_EDOF = "edof"; field public static final java.lang.String FOCUS_MODE_FIXED = "fixed"; @@ -18400,6 +18404,7 @@ package android.service.wallpaper { public class WallpaperService.Engine { ctor public WallpaperService.Engine(); + method protected void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]); method public int getDesiredMinimumHeight(); method public int getDesiredMinimumWidth(); method public android.view.SurfaceHolder getSurfaceHolder(); @@ -23503,6 +23508,7 @@ package android.view { method public abstract void setContentView(android.view.View); method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams); method protected void setDefaultWindowFormat(int); + method public void setDimAmount(float); method public abstract void setFeatureDrawable(int, android.graphics.drawable.Drawable); method public abstract void setFeatureDrawableAlpha(int, int); method public abstract void setFeatureDrawableResource(int, int); diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 8901fc8c5b10..77997796a8f7 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1541,6 +1541,15 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case SHOW_BOOT_MESSAGE_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + CharSequence msg = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(data); + boolean always = data.readInt() != 0; + showBootMessage(msg, always); + reply.writeNoException(); + return true; + } + } return super.onTransact(code, data, reply, flags); @@ -3483,5 +3492,17 @@ class ActivityManagerProxy implements IActivityManager return res; } + public void showBootMessage(CharSequence msg, boolean always) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + TextUtils.writeToParcel(msg, data, 0); + data.writeInt(always ? 1 : 0); + mRemote.transact(SHOW_BOOT_MESSAGE_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + private IBinder mRemote; } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 49f84497fa73..27dd6916af18 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -370,6 +370,8 @@ public interface IActivityManager extends IInterface { public long[] getProcessPss(int[] pids) throws RemoteException; + public void showBootMessage(CharSequence msg, boolean always) throws RemoteException; + /* * Private non-Binder interfaces */ @@ -599,4 +601,5 @@ public interface IActivityManager extends IInterface { int IS_INTENT_SENDER_TARGETED_TO_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+134; int UPDATE_PERSISTENT_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+135; int GET_PROCESS_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+136; + int SHOW_BOOT_MESSAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+137; } diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 28bc424d937d..264db1917d1d 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -774,6 +774,31 @@ public final class BluetoothAdapter { } /** + * Get the current connection state of a profile. + * This function can be used to check whether the local Bluetooth adapter + * is connected to any remote device for a specific profile. + * Profile can be one of {@link BluetoothProfile.HEADSET}, + * {@link BluetoothProfile.A2DP}. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH}. + * + * <p> Return value can be one of + * {@link * BluetoothProfile.STATE_DISCONNECTED}, + * {@link * BluetoothProfile.STATE_CONNECTING}, + * {@link * BluetoothProfile.STATE_CONNECTED}, + * {@link * BluetoothProfile.STATE_DISCONNECTING} + * @hide + */ + public int getProfileConnectionState(int profile) { + if (getState() != STATE_ON) return BluetoothProfile.STATE_DISCONNECTED; + try { + return mService.getProfileConnectionState(profile); + } catch (RemoteException e) {Log.e(TAG, "getProfileConnectionState:", e);} + return BluetoothProfile.STATE_DISCONNECTED; + } + + /** + /** * Picks RFCOMM channels until none are left. * Avoids reserved channels. */ diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index 6cd81fde7225..58b386838799 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -83,6 +83,12 @@ public interface BluetoothProfile { public static final int PAN = 5; /** + * PBAP + * @hide + */ + public static final int PBAP = 6; + + /** * Default priority for devices that we try to auto-connect to and * and allow incoming connections for the profile * @hide diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index 48dfed82caad..d4e7f7d48ede 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -53,6 +53,7 @@ interface IBluetooth byte[] readOutOfBandData(); int getAdapterConnectionState(); + int getProfileConnectionState(int profile); boolean changeApplicationBluetoothState(boolean on, in IBluetoothStateChangeCallback callback, in IBinder b); @@ -121,5 +122,5 @@ interface IBluetooth List<BluetoothDevice> getHealthDevicesMatchingConnectionStates(in int[] states); int getHealthDeviceConnectionState(in BluetoothDevice device); - void sendConnectionStateChange(in BluetoothDevice device, int state, int prevState); + void sendConnectionStateChange(in BluetoothDevice device, int profile, int state, int prevState); } diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index d7607e33d2d9..08aef16eb69c 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -320,7 +320,13 @@ interface IPackageManager { boolean isSafeMode(); void systemReady(); boolean hasSystemUidErrors(); - + + /** + * Ask the package manager to perform boot-time dex-opt of all + * existing packages. + */ + void performBootDexOpt(); + /** * Ask the package manager to perform dex-opt (if needed) on the given * package, if it already hasn't done mode. Only does this if running diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java index e2befca4a2c2..56cf94832cda 100644 --- a/core/java/android/database/sqlite/SQLiteOpenHelper.java +++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java @@ -100,6 +100,14 @@ public abstract class SQLiteOpenHelper { } /** + * Return the name of the SQLite database being opened, as given tp + * the constructor. + */ + public String getDatabaseName() { + return mName; + } + + /** * Create and/or open a database that will be used for reading and writing. * The first time this is called, the database will be opened and * {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 08bb133b9eb2..a41a330bb493 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -1674,7 +1674,6 @@ public class Camera { * the focus mode to other modes. * * @see #FOCUS_MODE_CONTINUOUS_VIDEO - * @hide */ public static final String FOCUS_MODE_CONTINUOUS_PICTURE = "continuous-picture"; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 554afd2247f1..03cba3da4293 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2815,6 +2815,16 @@ public final class Settings { public static final String TTS_DEFAULT_VARIANT = "tts_default_variant"; /** + * Stores the default tts locales on a per engine basis. Stored as + * a comma seperated list of values, each value being of the form + * {@code engine_name:locale} for example, + * {@code com.foo.ttsengine:eng-USA,com.bar.ttsengine:esp-ESP}. + * + * @hide + */ + public static final String TTS_DEFAULT_LOCALE = "tts_default_locale"; + + /** * Space delimited list of plugin packages that are enabled. */ public static final String TTS_ENABLED_PLUGINS = "tts_enabled_plugins"; diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java index 8c04853535d6..c4cb3a5f4155 100644 --- a/core/java/android/server/BluetoothA2dpService.java +++ b/core/java/android/server/BluetoothA2dpService.java @@ -523,7 +523,8 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { if (DBG) log("A2DP state : device: " + device + " State:" + prevState + "->" + state); - mBluetoothService.sendConnectionStateChange(device, state, prevState); + mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.A2DP, state, + prevState); } } diff --git a/core/java/android/server/BluetoothHealthProfileHandler.java b/core/java/android/server/BluetoothHealthProfileHandler.java index 105ff332ed04..51c995e7c4f4 100644 --- a/core/java/android/server/BluetoothHealthProfileHandler.java +++ b/core/java/android/server/BluetoothHealthProfileHandler.java @@ -20,6 +20,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHealth; import android.bluetooth.BluetoothHealthAppConfiguration; +import android.bluetooth.BluetoothProfile; import android.bluetooth.IBluetoothHealthCallback; import android.content.Context; import android.os.Handler; @@ -567,7 +568,8 @@ final class BluetoothHealthProfileHandler { private void updateAndSendIntent(BluetoothDevice device, int prevDeviceState, int newDeviceState) { mHealthDevices.put(device, newDeviceState); - mBluetoothService.sendConnectionStateChange(device, prevDeviceState, newDeviceState); + mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.HEALTH, + newDeviceState, prevDeviceState); } /** diff --git a/core/java/android/server/BluetoothInputProfileHandler.java b/core/java/android/server/BluetoothInputProfileHandler.java index 247e297f5031..31764b063ecf 100644 --- a/core/java/android/server/BluetoothInputProfileHandler.java +++ b/core/java/android/server/BluetoothInputProfileHandler.java @@ -20,6 +20,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDeviceProfileState; import android.bluetooth.BluetoothInputDevice; +import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfileState; import android.content.Context; import android.content.Intent; @@ -191,7 +192,8 @@ final class BluetoothInputProfileHandler { mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM); debugLog("InputDevice state : device: " + device + " State:" + prevState + "->" + state); - mBluetoothService.sendConnectionStateChange(device, state, prevState); + mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.INPUT_DEVICE, state, + prevState); } void handleInputDevicePropertyChange(String address, boolean connected) { diff --git a/core/java/android/server/BluetoothPanProfileHandler.java b/core/java/android/server/BluetoothPanProfileHandler.java index 37cfdc4db8c9..bfad74727d1a 100644 --- a/core/java/android/server/BluetoothPanProfileHandler.java +++ b/core/java/android/server/BluetoothPanProfileHandler.java @@ -19,6 +19,7 @@ package android.server; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothPan; +import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothTetheringDataTracker; import android.content.BroadcastReceiver; import android.content.Context; @@ -303,7 +304,8 @@ final class BluetoothPanProfileHandler { mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM); debugLog("Pan Device state : device: " + device + " State:" + prevState + "->" + state); - mBluetoothService.sendConnectionStateChange(device, state, prevState); + mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.PAN, state, + prevState); } private class BluetoothPanDevice { diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index ee1467350f25..e942969e1372 100755 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -166,6 +166,7 @@ public class BluetoothService extends IBluetooth.Stub { private static final String INCOMING_CONNECTION_FILE = "/data/misc/bluetooth/incoming_connection.conf"; private HashMap<String, Pair<Integer, String>> mIncomingConnections; + private HashMap<Integer, Pair<Integer, Integer>> mProfileConnectionState; private static class RemoteService { public String address; @@ -237,6 +238,7 @@ public class BluetoothService extends IBluetooth.Stub { mBluetoothPanProfileHandler = BluetoothPanProfileHandler.getInstance(mContext, this); mBluetoothHealthProfileHandler = BluetoothHealthProfileHandler.getInstance(mContext, this); mIncomingConnections = new HashMap<String, Pair<Integer, String>>(); + mProfileConnectionState = new HashMap<Integer, Pair<Integer, Integer>>(); } public static synchronized String readDockBluetoothAddress() { @@ -600,6 +602,11 @@ public class BluetoothService extends IBluetooth.Stub { * It inits bond state and profile state before STATE_ON intent is broadcasted. */ /*package*/ void initBluetoothAfterTurningOn() { + String discoverable = getProperty("Discoverable", false); + String timeout = getProperty("DiscoverableTimeout", false); + if (discoverable.equals("true") && Integer.valueOf(timeout) != 0) { + setAdapterPropertyBooleanNative("Discoverable", 0); + } mBondState.initBondState(); initProfileState(); } @@ -1742,6 +1749,19 @@ public class BluetoothService extends IBluetooth.Stub { dumpInputDeviceProfile(pw); dumpPanProfile(pw); dumpApplicationServiceRecords(pw); + dumpProfileState(pw); + } + + private void dumpProfileState(PrintWriter pw) { + pw.println("\n--Profile State dump--"); + pw.println("\n Headset profile state:" + + mAdapter.getProfileConnectionState(BluetoothProfile.HEADSET)); + pw.println("\n A2dp profile state:" + + mAdapter.getProfileConnectionState(BluetoothProfile.A2DP)); + pw.println("\n HID profile state:" + + mAdapter.getProfileConnectionState(BluetoothProfile.INPUT_DEVICE)); + pw.println("\n PAN profile state:" + + mAdapter.getProfileConnectionState(BluetoothProfile.PAN)); } private void dumpHeadsetService(PrintWriter pw) { @@ -2443,23 +2463,85 @@ public class BluetoothService extends IBluetooth.Stub { return mAdapterConnectionState; } - public synchronized void sendConnectionStateChange(BluetoothDevice device, int state, - int prevState) { + public int getProfileConnectionState(int profile) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + + Pair<Integer, Integer> state = mProfileConnectionState.get(profile); + if (state == null) return BluetoothProfile.STATE_DISCONNECTED; + + return state.first; + } + + private void updateProfileConnectionState(int profile, int newState, int oldState) { + // mProfileConnectionState is a hashmap - + // <Integer, Pair<Integer, Integer>> + // The key is the profile, the value is a pair. first element + // is the state and the second element is the number of devices + // in that state. + int numDev = 1; + int newHashState = newState; + boolean update = true; + + // The following conditions are considered in this function: + // 1. If there is no record of profile and state - update + // 2. If a new device's state is current hash state - increment + // number of devices in the state. + // 3. If a state change has happened to Connected or Connecting + // (if current state is not connected), update. + // 4. If numDevices is 1 and that device state is being updated, update + // 5. If numDevices is > 1 and one of the devices is changing state, + // decrement numDevices but maintain oldState if it is Connected or + // Connecting + Pair<Integer, Integer> stateNumDev = mProfileConnectionState.get(profile); + if (stateNumDev != null) { + int currHashState = stateNumDev.first; + numDev = stateNumDev.second; + + if (newState == currHashState) { + numDev ++; + } else if (newState == BluetoothProfile.STATE_CONNECTED || + (newState == BluetoothProfile.STATE_CONNECTING && + currHashState != BluetoothProfile.STATE_CONNECTED)) { + numDev = 1; + } else if (numDev == 1 && oldState == currHashState) { + update = true; + } else if (numDev > 1 && oldState == currHashState) { + numDev --; + + if (currHashState == BluetoothProfile.STATE_CONNECTED || + currHashState == BluetoothProfile.STATE_CONNECTING) { + newHashState = currHashState; + } + } else { + update = false; + } + } + + if (update) { + mProfileConnectionState.put(profile, new Pair<Integer, Integer>(newHashState, + numDev)); + } + } + + public synchronized void sendConnectionStateChange(BluetoothDevice + device, int profile, int state, int prevState) { // Since this is a binder call check if Bluetooth is on still if (getBluetoothStateInternal() == BluetoothAdapter.STATE_OFF) return; - if (updateCountersAndCheckForConnectionStateChange(state, prevState)) { - if (!validateProfileConnectionState(state) || - !validateProfileConnectionState(prevState)) { - // Previously, an invalid state was broadcast anyway, - // with the invalid state converted to -1 in the intent. - // Better to log an error and not send an intent with - // invalid contents or set mAdapterConnectionState to -1. - Log.e(TAG, "Error in sendConnectionStateChange: " - + "prevState " + prevState + " state " + state); - return; - } + if (!validateProfileConnectionState(state) || + !validateProfileConnectionState(prevState)) { + // Previously, an invalid state was broadcast anyway, + // with the invalid state converted to -1 in the intent. + // Better to log an error and not send an intent with + // invalid contents or set mAdapterConnectionState to -1. + Log.e(TAG, "Error in sendConnectionStateChange: " + + "prevState " + prevState + " state " + state); + return; + } + updateProfileConnectionState(profile, state, prevState); + + if (updateCountersAndCheckForConnectionStateChange(state, prevState)) { mAdapterConnectionState = state; if (state == BluetoothProfile.STATE_DISCONNECTED) { diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index c51ba2a0a643..4c563ce99e9b 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -46,7 +46,6 @@ import android.view.InputChannel; import android.view.InputDevice; import android.view.InputHandler; import android.view.InputQueue; -import android.view.KeyEvent; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.View; @@ -54,8 +53,9 @@ import android.view.ViewGroup; import android.view.ViewRootImpl; import android.view.WindowManager; import android.view.WindowManagerImpl; -import android.view.WindowManagerPolicy; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.ArrayList; /** @@ -459,6 +459,44 @@ public abstract class WallpaperService extends Service { public void onSurfaceDestroyed(SurfaceHolder holder) { } + protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) { + out.print(prefix); out.print("mInitializing="); out.print(mInitializing); + out.print(" mDestroyed="); out.println(mDestroyed); + out.print(prefix); out.print("mVisible="); out.print(mVisible); + out.print(" mScreenOn="); out.print(mScreenOn); + out.print(" mReportedVisible="); out.println(mReportedVisible); + out.print(prefix); out.print("mCreated="); out.print(mCreated); + out.print(" mSurfaceCreated="); out.print(mSurfaceCreated); + out.print(" mIsCreating="); out.print(mIsCreating); + out.print(" mDrawingAllowed="); out.println(mDrawingAllowed); + out.print(prefix); out.print("mWidth="); out.print(mWidth); + out.print(" mCurWidth="); out.print(mCurWidth); + out.print(" mHeight="); out.print(mHeight); + out.print(" mCurHeight="); out.println(mCurHeight); + out.print(prefix); out.print("mType="); out.print(mType); + out.print(" mWindowFlags="); out.print(mWindowFlags); + out.print(" mCurWindowFlags="); out.println(mCurWindowFlags); + out.print(prefix); out.print("mVisibleInsets="); + out.print(mVisibleInsets.toShortString()); + out.print(" mWinFrame="); out.print(mWinFrame.toShortString()); + out.print(" mContentInsets="); out.println(mContentInsets.toShortString()); + out.print(prefix); out.print("mConfiguration="); out.println(mConfiguration); + out.print(prefix); out.print("mLayout="); out.println(mLayout); + synchronized (mLock) { + out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset); + out.print(" mPendingXOffset="); out.println(mPendingXOffset); + out.print(prefix); out.print("mPendingXOffsetStep="); + out.print(mPendingXOffsetStep); + out.print(" mPendingXOffsetStep="); out.println(mPendingXOffsetStep); + out.print(prefix); out.print("mOffsetMessageEnqueued="); + out.print(mOffsetMessageEnqueued); + out.print(" mPendingSync="); out.println(mPendingSync); + if (mPendingMove != null) { + out.print(prefix); out.print("mPendingMove="); out.println(mPendingMove); + } + } + } + private void dispatchPointer(MotionEvent event) { if (event.isTouchEvent()) { synchronized (mLock) { @@ -1012,4 +1050,14 @@ public abstract class WallpaperService extends Service { * is in the wallpaper picker viewing a preview of it as well. */ public abstract Engine onCreateEngine(); + + @Override + protected void dump(FileDescriptor fd, PrintWriter out, String[] args) { + out.print("State of wallpaper "); out.print(this); out.println(":"); + for (int i=0; i<mActiveEngines.size(); i++) { + Engine engine = mActiveEngines.get(i); + out.print(" Engine "); out.print(engine); out.println(":"); + engine.dump(" ", fd, out, args); + } + } } diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index b4e8ab4e698e..a08ba2a441ae 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -65,6 +65,7 @@ import java.util.Locale; * * {@link #onStop} tells the engine that it should stop all ongoing synthesis, if * any. Any pending data from the current synthesis will be discarded. + * */ // TODO: Add a link to the sample TTS engine once it's done. public abstract class TextToSpeechService extends Service { @@ -80,6 +81,7 @@ public abstract class TextToSpeechService extends Service { // associated with this TTS engine. Will handle all requests except synthesis // to file requests, which occur on the synthesis thread. private AudioPlaybackHandler mAudioPlaybackHandler; + private TtsEngines mEngineHelper; private CallbackMap mCallbacks; private String mPackageName; @@ -96,12 +98,15 @@ public abstract class TextToSpeechService extends Service { mAudioPlaybackHandler = new AudioPlaybackHandler(); mAudioPlaybackHandler.start(); + mEngineHelper = new TtsEngines(this); + mCallbacks = new CallbackMap(); mPackageName = getApplicationInfo().packageName; + String[] defaultLocale = getSettingsLocale(); // Load default language - onLoadLanguage(getDefaultLanguage(), getDefaultCountry(), getDefaultVariant()); + onLoadLanguage(defaultLocale[0], defaultLocale[1], defaultLocale[2]); } @Override @@ -195,30 +200,15 @@ public abstract class TextToSpeechService extends Service { return getSecureSettingInt(Settings.Secure.TTS_DEFAULT_RATE, Engine.DEFAULT_RATE); } - private String getDefaultLanguage() { - return getSecureSettingString(Settings.Secure.TTS_DEFAULT_LANG, - Locale.getDefault().getISO3Language()); - } - - private String getDefaultCountry() { - return getSecureSettingString(Settings.Secure.TTS_DEFAULT_COUNTRY, - Locale.getDefault().getISO3Country()); - } - - private String getDefaultVariant() { - return getSecureSettingString(Settings.Secure.TTS_DEFAULT_VARIANT, - Locale.getDefault().getVariant()); + private String[] getSettingsLocale() { + final String locale = mEngineHelper.getLocalePrefForEngine(mPackageName); + return TtsEngines.parseLocalePref(locale); } private int getSecureSettingInt(String name, int defaultValue) { return Settings.Secure.getInt(getContentResolver(), name, defaultValue); } - private String getSecureSettingString(String name, String defaultValue) { - String value = Settings.Secure.getString(getContentResolver(), name); - return value != null ? value : defaultValue; - } - /** * Synthesizer thread. This thread is used to run {@link SynthHandler}. */ @@ -458,6 +448,7 @@ public abstract class TextToSpeechService extends Service { class SynthesisSpeechItem extends SpeechItem { private final String mText; private final SynthesisRequest mSynthesisRequest; + private final String[] mDefaultLocale; // Non null after synthesis has started, and all accesses // guarded by 'this'. private AbstractSynthesisCallback mSynthesisCallback; @@ -467,6 +458,7 @@ public abstract class TextToSpeechService extends Service { super(callingApp, params); mText = text; mSynthesisRequest = new SynthesisRequest(mText, mParams); + mDefaultLocale = getSettingsLocale(); setRequestParams(mSynthesisRequest); mEventLogger = new EventLogger(mSynthesisRequest, getCallingApp(), mPackageName); } @@ -523,7 +515,7 @@ public abstract class TextToSpeechService extends Service { } public String getLanguage() { - return getStringParam(Engine.KEY_PARAM_LANGUAGE, getDefaultLanguage()); + return getStringParam(Engine.KEY_PARAM_LANGUAGE, mDefaultLocale[0]); } private boolean hasLanguage() { @@ -531,12 +523,12 @@ public abstract class TextToSpeechService extends Service { } private String getCountry() { - if (!hasLanguage()) return getDefaultCountry(); + if (!hasLanguage()) return mDefaultLocale[1]; return getStringParam(Engine.KEY_PARAM_COUNTRY, ""); } private String getVariant() { - if (!hasLanguage()) return getDefaultVariant(); + if (!hasLanguage()) return mDefaultLocale[2]; return getStringParam(Engine.KEY_PARAM_VARIANT, ""); } diff --git a/core/java/android/speech/tts/TtsEngines.java b/core/java/android/speech/tts/TtsEngines.java index 5f0cb74b1fb0..bb72bea37d04 100644 --- a/core/java/android/speech/tts/TtsEngines.java +++ b/core/java/android/speech/tts/TtsEngines.java @@ -17,6 +17,7 @@ package android.speech.tts; import org.xmlpull.v1.XmlPullParserException; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -27,6 +28,8 @@ import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; +import static android.provider.Settings.Secure.getString; + import android.provider.Settings; import android.speech.tts.TextToSpeech.Engine; import android.speech.tts.TextToSpeech.EngineInfo; @@ -40,6 +43,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Locale; /** * Support class for querying the list of available engines @@ -52,6 +56,9 @@ import java.util.List; */ public class TtsEngines { private static final String TAG = "TtsEngines"; + private static final boolean DBG = false; + + private static final String LOCALE_DELIMITER = "-"; private final Context mContext; @@ -65,7 +72,7 @@ public class TtsEngines { * the highest ranked engine is returned as per {@link EngineInfoComparator}. */ public String getDefaultEngine() { - String engine = Settings.Secure.getString(mContext.getContentResolver(), + String engine = getString(mContext.getContentResolver(), Settings.Secure.TTS_DEFAULT_SYNTH); return isEngineInstalled(engine) ? engine : getHighestRankedEngineName(); } @@ -129,12 +136,6 @@ public class TtsEngines { return engines; } - // TODO: Used only by the settings app. Remove once - // the settings UI change has been finalized. - public boolean isEngineEnabled(String engine) { - return isEngineInstalled(engine); - } - private boolean isSystemEngine(ServiceInfo info) { final ApplicationInfo appInfo = info.applicationInfo; return appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; @@ -182,7 +183,7 @@ public class TtsEngines { * The name of the XML tag that text to speech engines must use to * declare their meta data. * - * {@link com.android.internal.R.styleable.TextToSpeechEngine} + * {@link com.android.internal.R.styleable#TextToSpeechEngine} */ private static final String XML_TAG_NAME = "tts-engine"; @@ -279,4 +280,175 @@ public class TtsEngines { } } + /** + * Returns the locale string for a given TTS engine. Attempts to read the + * value from {@link Settings.Secure#TTS_DEFAULT_LOCALE}, failing which the + * old style value from {@link Settings.Secure#TTS_DEFAULT_LANG} is read. If + * both these values are empty, the default phone locale is returned. + * + * @param engineName the engine to return the locale for. + * @return the locale string preference for this engine. Will be non null + * and non empty. + */ + public String getLocalePrefForEngine(String engineName) { + String locale = parseEnginePrefFromList( + getString(mContext.getContentResolver(), Settings.Secure.TTS_DEFAULT_LOCALE), + engineName); + + if (TextUtils.isEmpty(locale)) { + // The new style setting is unset, attempt to return the old style setting. + locale = getV1Locale(); + } + + if (DBG) Log.d(TAG, "getLocalePrefForEngine(" + engineName + ")= " + locale); + + return locale; + } + + /** + * Parses a locale preference value delimited by {@link #LOCALE_DELIMITER}. + * Varies from {@link String#split} in that it will always return an array + * of length 3 with non null values. + */ + public static String[] parseLocalePref(String pref) { + String[] returnVal = new String[] { "", "", ""}; + if (!TextUtils.isEmpty(pref)) { + String[] split = pref.split(LOCALE_DELIMITER); + System.arraycopy(split, 0, returnVal, 0, split.length); + } + + if (DBG) Log.d(TAG, "parseLocalePref(" + returnVal[0] + "," + returnVal[1] + + "," + returnVal[2] +")"); + + return returnVal; + } + + /** + * @return the old style locale string constructed from + * {@link Settings.Secure#TTS_DEFAULT_LANG}, + * {@link Settings.Secure#TTS_DEFAULT_COUNTRY} and + * {@link Settings.Secure#TTS_DEFAULT_VARIANT}. If no such locale is set, + * then return the default phone locale. + */ + private String getV1Locale() { + final ContentResolver cr = mContext.getContentResolver(); + + final String lang = Settings.Secure.getString(cr, Settings.Secure.TTS_DEFAULT_LANG); + final String country = Settings.Secure.getString(cr, Settings.Secure.TTS_DEFAULT_COUNTRY); + final String variant = Settings.Secure.getString(cr, Settings.Secure.TTS_DEFAULT_VARIANT); + + if (TextUtils.isEmpty(lang)) { + return getDefaultLocale(); + } + + String v1Locale = lang; + if (!TextUtils.isEmpty(country)) { + v1Locale += LOCALE_DELIMITER + country; + } + if (!TextUtils.isEmpty(variant)) { + v1Locale += LOCALE_DELIMITER + variant; + } + + return v1Locale; + } + + private String getDefaultLocale() { + final Locale locale = Locale.getDefault(); + + return locale.getISO3Language() + LOCALE_DELIMITER + locale.getISO3Country() + + LOCALE_DELIMITER + locale.getVariant(); + } + + /** + * Parses a comma separated list of engine locale preferences. The list is of the + * form {@code "engine_name_1:locale_1,engine_name_2:locale2"} and so on and + * so forth. Returns null if the list is empty, malformed or if there is no engine + * specific preference in the list. + */ + private static String parseEnginePrefFromList(String prefValue, String engineName) { + if (TextUtils.isEmpty(prefValue)) { + return null; + } + + String[] prefValues = prefValue.split(","); + + for (String value : prefValues) { + final int delimiter = value.indexOf(':'); + if (delimiter > 0) { + if (engineName.equals(value.substring(0, delimiter))) { + return value.substring(delimiter + 1); + } + } + } + + return null; + } + + public synchronized void updateLocalePrefForEngine(String name, String newLocale) { + final String prefList = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.TTS_DEFAULT_LOCALE); + if (DBG) { + Log.d(TAG, "updateLocalePrefForEngine(" + name + ", " + newLocale + + "), originally: " + prefList); + } + + final String newPrefList = updateValueInCommaSeparatedList(prefList, + name, newLocale); + + if (DBG) Log.d(TAG, "updateLocalePrefForEngine(), writing: " + newPrefList.toString()); + + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.TTS_DEFAULT_LOCALE, newPrefList.toString()); + } + + /** + * Updates the value for a given key in a comma separated list of key value pairs, + * each of which are delimited by a colon. If no value exists for the given key, + * the kay value pair are appended to the end of the list. + */ + private String updateValueInCommaSeparatedList(String list, String key, + String newValue) { + StringBuilder newPrefList = new StringBuilder(); + if (TextUtils.isEmpty(list)) { + // If empty, create a new list with a single entry. + newPrefList.append(key).append(':').append(newValue); + } else { + String[] prefValues = list.split(","); + // Whether this is the first iteration in the loop. + boolean first = true; + // Whether we found the given key. + boolean found = false; + for (String value : prefValues) { + final int delimiter = value.indexOf(':'); + if (delimiter > 0) { + if (key.equals(value.substring(0, delimiter))) { + if (first) { + first = false; + } else { + newPrefList.append(','); + } + found = true; + newPrefList.append(key).append(':').append(newValue); + } else { + if (first) { + first = false; + } else { + newPrefList.append(','); + } + // Copy across the entire key + value as is. + newPrefList.append(value); + } + } + } + + if (!found) { + // Not found, but the rest of the keys would have been copied + // over already, so just append it to the end. + newPrefList.append(','); + newPrefList.append(key).append(':').append(newValue); + } + } + + return newPrefList.toString(); + } } diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java index 5ed2df4724e8..6debc6b3ecf1 100644 --- a/core/java/android/text/TextDirectionHeuristics.java +++ b/core/java/android/text/TextDirectionHeuristics.java @@ -50,22 +50,6 @@ public class TextDirectionHeuristics { new TextDirectionHeuristicInternal(FirstStrong.INSTANCE, true); /** - * If the text contains any strong left to right non-format character, determines - * that the direction is left to right, falling back to left to right if it - * finds none. - */ - public static final TextDirectionHeuristic ANYLTR_LTR = - new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_LTR, false); - - /** - * If the text contains any strong left to right non-format character, determines - * that the direction is left to right, falling back to right to left if it - * finds none. - */ - public static final TextDirectionHeuristic ANYLTR_RTL = - new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_LTR, true); - - /** * If the text contains any strong right to left non-format character, determines * that the direction is right to left, falling back to left to right if it * finds none. @@ -74,14 +58,6 @@ public class TextDirectionHeuristics { new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_RTL, false); /** - * If the text contains any strong right to left non-format character, determines - * that the direction is right to left, falling back to right to left if it - * finds none. - */ - public static final TextDirectionHeuristic ANYRTL_RTL = - new TextDirectionHeuristicInternal(AnyStrong.INSTANCE_RTL, true); - - /** * Examines only the strong directional non-format characters, and if either * left to right or right to left characters are 60% or more of this total, * determines that the direction follows the majority of characters. Falls diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 7a96a5031d0d..5e7e50987ecb 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -163,7 +163,9 @@ public class Surface implements Parcelable { * It is an error to lock a Blur surface, since it doesn't have * a backing store. * @hide + * @deprecated */ + @Deprecated public static final int FX_SURFACE_BLUR = 0x00010000; /** Creates a Dim surface. Everything behind this surface is dimmed diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index abd9ad6ce616..17e637c57cc5 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -8087,6 +8087,11 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE); } + if ((mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null) { + // Noop for views which are not visible and which are not running an animation. They + // will not get drawn and they should not set dirty flags as if they will be drawn + return; + } if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) || (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID || (mPrivateFlags & INVALIDATED) != INVALIDATED) { @@ -8130,6 +8135,11 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE); } + if ((mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null) { + // Noop for views which are not visible and which are not running an animation. They + // will not get drawn and they should not set dirty flags as if they will be drawn + return; + } if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) || (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID || (mPrivateFlags & INVALIDATED) != INVALIDATED) { @@ -8182,6 +8192,11 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE); } + if ((mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null) { + // Noop for views which are not visible and which are not running an animation. They + // will not get drawn and they should not set dirty flags as if they will be drawn + return; + } if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) || (invalidateCache && (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID) || (mPrivateFlags & INVALIDATED) != INVALIDATED || isOpaque() != mLastIsOpaque) { @@ -8217,6 +8232,11 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit * @hide */ public void fastInvalidate() { + if ((mViewFlags & VISIBILITY_MASK) != VISIBLE && mCurrentAnimation == null) { + // Noop for views which are not visible and which are not running an animation. They + // will not get drawn and they should not set dirty flags as if they will be drawn + return; + } if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) || (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID || (mPrivateFlags & INVALIDATED) != INVALIDATED) { @@ -9165,7 +9185,8 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit mPrivateFlags &= ~AWAKEN_SCROLL_BARS_ON_ATTACH; } jumpDrawablesToCurrentState(); - // Order is important here: LayoutDirection should be resolved before Padding and TextDirection + // Order is important here: LayoutDirection MUST be resolved before Padding + // and TextDirection resolveLayoutDirectionIfNeeded(); resolvePadding(); resolveTextDirection(); diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index e0e1a1a83b91..75c75927a769 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -127,6 +127,7 @@ public abstract class Window { private int mLocalFeatures = DEFAULT_FEATURES; private boolean mHaveWindowFormat = false; + private boolean mHaveDimAmount = false; private int mDefaultWindowFormat = PixelFormat.OPAQUE; private boolean mHasSoftInputMode = false; @@ -745,6 +746,23 @@ public abstract class Window { } /** + * Set the amount of dim behind the window when using + * {@link WindowManager.LayoutParams#FLAG_DIM_BEHIND}. This overrides + * the default dim amount of that is selected by the Window based on + * its theme. + * + * @param amount The new dim amount, from 0 for no dim to 1 for full dim. + */ + public void setDimAmount(float amount) { + final WindowManager.LayoutParams attrs = getAttributes(); + attrs.dimAmount = amount; + mHaveDimAmount = true; + if (mCallback != null) { + mCallback.onWindowAttributesChanged(attrs); + } + } + + /** * Specify custom window attributes. <strong>PLEASE NOTE:</strong> the * layout params you give here should generally be from values previously * retrieved with {@link #getAttributes()}; you probably do not want to @@ -1193,6 +1211,11 @@ public abstract class Window { } } + /** @hide */ + protected boolean haveDimAmount() { + return mHaveDimAmount; + } + public abstract void setChildDrawable(int featureId, Drawable drawable); public abstract void setChildInt(int featureId, int value); diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 8a30c7b7a02f..1b4edf12ac09 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -871,6 +871,16 @@ public interface WindowManagerPolicy { public void systemReady(); /** + * Show boot time message to the user. + */ + public void showBootMessage(final CharSequence msg, final boolean always); + + /** + * Hide the UI for showing boot messages, never to be displayed again. + */ + public void hideBootMessages(); + + /** * Called when userActivity is signalled in the power manager. * This is safe to call from any thread, with any window manager locks held or not. */ diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index dae118e29f24..86f061aedfe6 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -54,6 +54,7 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.ViewParent; import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -2788,7 +2789,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); // Time to start stealing events! Once we've stolen them, don't let anyone // steal from us - requestDisallowInterceptTouchEvent(true); + final ViewParent parent = getParent(); + if (parent != null) { + parent.requestDisallowInterceptTouchEvent(true); + } return true; } @@ -2848,9 +2852,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te View v; int deltaY; - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } + initVelocityTrackerIfNotExists(); mVelocityTracker.addMovement(ev); switch (action & MotionEvent.ACTION_MASK) { @@ -2955,7 +2957,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te // Make sure that we do so in case we're in a parent that can intercept. if ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) == 0 && Math.abs(deltaY) > mTouchSlop) { - requestDisallowInterceptTouchEvent(true); + final ViewParent parent = getParent(); + if (parent != null) { + parent.requestDisallowInterceptTouchEvent(true); + } } final int rawDeltaY = deltaY; @@ -2998,7 +3003,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te 0, mOverscrollDistance, true); if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) { // Don't allow overfling if we're at the edge. - mVelocityTracker.clear(); + if (mVelocityTracker != null) { + mVelocityTracker.clear(); + } } final int overscrollMode = getOverScrollMode(); @@ -3259,10 +3266,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te handler.removeCallbacks(mPendingCheckForLongPress); } - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } + recycleVelocityTracker(); mActivePointerId = INVALID_POINTER; @@ -3307,10 +3311,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te handler.removeCallbacks(mPendingCheckForLongPress); } - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } + recycleVelocityTracker(); } if (mEdgeGlowTop != null) { @@ -3450,6 +3451,35 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mGlowPaddingRight = rightPadding; } + private void initOrResetVelocityTracker() { + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } else { + mVelocityTracker.clear(); + } + } + + private void initVelocityTrackerIfNotExists() { + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + } + + private void recycleVelocityTracker() { + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + } + + @Override + public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { + if (disallowIntercept) { + recycleVelocityTracker(); + } + super.requestDisallowInterceptTouchEvent(disallowIntercept); + } + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { int action = ev.getAction(); @@ -3487,6 +3517,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te clearScrollingCache(); } mLastY = Integer.MIN_VALUE; + initOrResetVelocityTracker(); + mVelocityTracker.addMovement(ev); if (touchMode == TOUCH_MODE_FLING) { return true; } @@ -3502,6 +3534,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mActivePointerId = ev.getPointerId(pointerIndex); } final int y = (int) ev.getY(pointerIndex); + initVelocityTrackerIfNotExists(); + mVelocityTracker.addMovement(ev); if (startScrollIfNeeded(y - mMotionY)) { return true; } @@ -3510,9 +3544,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te break; } + case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: { mTouchMode = TOUCH_MODE_REST; mActivePointerId = INVALID_POINTER; + recycleVelocityTracker(); reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); break; } diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java index b428301bfaef..d638732ad0d2 100644 --- a/core/java/android/widget/HorizontalScrollView.java +++ b/core/java/android/widget/HorizontalScrollView.java @@ -399,6 +399,35 @@ public class HorizontalScrollView extends FrameLayout { return false; } + private void initOrResetVelocityTracker() { + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } else { + mVelocityTracker.clear(); + } + } + + private void initVelocityTrackerIfNotExists() { + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + } + + private void recycleVelocityTracker() { + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + } + + @Override + public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { + if (disallowIntercept) { + recycleVelocityTracker(); + } + super.requestDisallowInterceptTouchEvent(disallowIntercept); + } + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { /* @@ -440,6 +469,8 @@ public class HorizontalScrollView extends FrameLayout { if (xDiff > mTouchSlop) { mIsBeingDragged = true; mLastMotionX = x; + initVelocityTrackerIfNotExists(); + mVelocityTracker.addMovement(ev); if (mParent != null) mParent.requestDisallowInterceptTouchEvent(true); } break; @@ -449,6 +480,7 @@ public class HorizontalScrollView extends FrameLayout { final float x = ev.getX(); if (!inChild((int) x, (int) ev.getY())) { mIsBeingDragged = false; + recycleVelocityTracker(); break; } @@ -459,6 +491,9 @@ public class HorizontalScrollView extends FrameLayout { mLastMotionX = x; mActivePointerId = ev.getPointerId(0); + initOrResetVelocityTracker(); + mVelocityTracker.addMovement(ev); + /* * If being flinged and user touches the screen, initiate drag; * otherwise don't. mScroller.isFinished should be false when @@ -498,9 +533,7 @@ public class HorizontalScrollView extends FrameLayout { @Override public boolean onTouchEvent(MotionEvent ev) { - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } + initVelocityTrackerIfNotExists(); mVelocityTracker.addMovement(ev); final int action = ev.getAction(); @@ -584,11 +617,8 @@ public class HorizontalScrollView extends FrameLayout { mActivePointerId = INVALID_POINTER; mIsBeingDragged = false; + recycleVelocityTracker(); - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } if (mEdgeGlowLeft != null) { mEdgeGlowLeft.onRelease(); mEdgeGlowRight.onRelease(); @@ -602,10 +632,8 @@ public class HorizontalScrollView extends FrameLayout { } mActivePointerId = INVALID_POINTER; mIsBeingDragged = false; - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } + recycleVelocityTracker(); + if (mEdgeGlowLeft != null) { mEdgeGlowLeft.onRelease(); mEdgeGlowRight.onRelease(); diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index e59f731b6148..09c875bd337e 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -408,6 +408,36 @@ public class ScrollView extends FrameLayout { return false; } + private void initOrResetVelocityTracker() { + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } else { + mVelocityTracker.clear(); + } + } + + private void initVelocityTrackerIfNotExists() { + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + } + + private void recycleVelocityTracker() { + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + } + + @Override + public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { + if (disallowIntercept) { + recycleVelocityTracker(); + } + super.requestDisallowInterceptTouchEvent(disallowIntercept); + } + + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { /* @@ -449,6 +479,8 @@ public class ScrollView extends FrameLayout { if (yDiff > mTouchSlop) { mIsBeingDragged = true; mLastMotionY = y; + initVelocityTrackerIfNotExists(); + mVelocityTracker.addMovement(ev); if (mScrollStrictSpan == null) { mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll"); } @@ -460,6 +492,7 @@ public class ScrollView extends FrameLayout { final float y = ev.getY(); if (!inChild((int) ev.getX(), (int) y)) { mIsBeingDragged = false; + recycleVelocityTracker(); break; } @@ -470,6 +503,8 @@ public class ScrollView extends FrameLayout { mLastMotionY = y; mActivePointerId = ev.getPointerId(0); + initOrResetVelocityTracker(); + mVelocityTracker.addMovement(ev); /* * If being flinged and user touches the screen, initiate drag; * otherwise don't. mScroller.isFinished should be false when @@ -487,6 +522,7 @@ public class ScrollView extends FrameLayout { /* Release the drag */ mIsBeingDragged = false; mActivePointerId = INVALID_POINTER; + recycleVelocityTracker(); if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) { invalidate(); } @@ -505,9 +541,7 @@ public class ScrollView extends FrameLayout { @Override public boolean onTouchEvent(MotionEvent ev) { - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } + initVelocityTrackerIfNotExists(); mVelocityTracker.addMovement(ev); final int action = ev.getAction(); @@ -1441,10 +1475,7 @@ public class ScrollView extends FrameLayout { private void endDrag() { mIsBeingDragged = false; - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } + recycleVelocityTracker(); if (mEdgeGlowTop != null) { mEdgeGlowTop.onRelease(); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 7a5a091a848b..662b964fc3c7 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -10745,8 +10745,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener TextDirectionHeuristics.FIRSTSTRONG_LTR); break; case TEXT_DIRECTION_ANY_RTL: - mTextDir = (defaultIsRtl ? TextDirectionHeuristics.ANYRTL_RTL: - TextDirectionHeuristics.ANYRTL_LTR); + mTextDir = TextDirectionHeuristics.ANYRTL_LTR; break; case TEXT_DIRECTION_CHAR_COUNT: mTextDir = (defaultIsRtl ? TextDirectionHeuristics.CHARCOUNT_RTL: diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java index d1c3e64f0159..daabf425a461 100644 --- a/core/java/com/android/internal/app/ShutdownThread.java +++ b/core/java/com/android/internal/app/ShutdownThread.java @@ -122,10 +122,6 @@ public final class ShutdownThread extends Thread { closer.dialog = dialog; dialog.setOnDismissListener(closer); dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - if (!context.getResources().getBoolean( - com.android.internal.R.bool.config_sf_slowBlur)) { - dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND); - } dialog.show(); } else { beginShutdownSequence(context); @@ -185,10 +181,6 @@ public final class ShutdownThread extends Thread { pd.setIndeterminate(true); pd.setCancelable(false); pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - if (!context.getResources().getBoolean( - com.android.internal.R.bool.config_sf_slowBlur)) { - pd.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND); - } pd.show(); diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java index df4243a4826f..246c4dec1ff3 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java @@ -252,7 +252,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter { * @return true if the overflow menu was shown, false otherwise. */ public boolean showOverflowMenu() { - if (mReserveOverflow && !isOverflowMenuShowing() && mMenuView != null && + if (mReserveOverflow && !isOverflowMenuShowing() && mMenu != null && mMenuView != null && mPostedOpenRunnable == null) { OverflowPopup popup = new OverflowPopup(mContext, mMenu, mOverflowButton, true); mPostedOpenRunnable = new OpenOverflowRunnable(popup); diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java index 5622b4459b4c..c30e83b670ed 100644 --- a/core/java/com/android/internal/view/menu/MenuBuilder.java +++ b/core/java/com/android/internal/view/menu/MenuBuilder.java @@ -1057,9 +1057,8 @@ public class MenuBuilder implements Menu { mNonActionItems.add(item); } } - } else if (mActionItems.size() + mNonActionItems.size() != getVisibleItems().size()) { - // Nobody flagged anything, but if something doesn't add up then treat everything - // as non-action items. + } else { + // Nobody flagged anything, everything is a non-action item. // (This happens during a first pass with no action-item presenters.) mActionItems.clear(); mNonActionItems.clear(); diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 28181ba02823..4efb29f2ef32 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -550,10 +550,9 @@ public class ActionBarView extends AbsActionBarView { if (mTitleLayout != null && (flagsChanged & (ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME)) != 0) { - final boolean homeAsUp = (options & ActionBar.DISPLAY_HOME_AS_UP) != 0; - final boolean titleUp = homeAsUp && !showHome; - mTitleUpView.setVisibility(titleUp ? VISIBLE : GONE); - mTitleLayout.setEnabled(titleUp); + final boolean homeAsUp = (mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0; + mTitleUpView.setVisibility(!showHome ? (homeAsUp ? VISIBLE : INVISIBLE) : GONE); + mTitleLayout.setEnabled(!showHome && homeAsUp); } if ((flagsChanged & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomNavView != null) { @@ -730,10 +729,9 @@ public class ActionBarView extends AbsActionBarView { } final boolean homeAsUp = (mDisplayOptions & ActionBar.DISPLAY_HOME_AS_UP) != 0; - final boolean titleUp = homeAsUp && - (mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) == 0; - mTitleUpView.setVisibility(titleUp ? VISIBLE : GONE); - mTitleLayout.setEnabled(titleUp); + final boolean showHome = (mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) != 0; + mTitleUpView.setVisibility(!showHome ? (homeAsUp ? VISIBLE : INVISIBLE) : GONE); + mTitleLayout.setEnabled(homeAsUp && !showHome); } addView(mTitleLayout); @@ -805,7 +803,7 @@ public class ActionBarView extends AbsActionBarView { int leftOfCenter = availableWidth / 2; int rightOfCenter = leftOfCenter; - View homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout; + HomeView homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout; if (homeLayout.getVisibility() != GONE) { final LayoutParams lp = homeLayout.getLayoutParams(); @@ -817,7 +815,7 @@ public class ActionBarView extends AbsActionBarView { } homeLayout.measure(homeWidthSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); - final int homeWidth = homeLayout.getMeasuredWidth(); + final int homeWidth = homeLayout.getMeasuredWidth() + homeLayout.getLeftOffset(); availableWidth = Math.max(0, availableWidth - homeWidth); leftOfCenter = Math.max(0, availableWidth - homeWidth); } @@ -962,9 +960,10 @@ public class ActionBarView extends AbsActionBarView { return; } - View homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout; + HomeView homeLayout = mExpandedActionView != null ? mExpandedHomeLayout : mHomeLayout; if (homeLayout.getVisibility() != GONE) { - x += positionChild(homeLayout, x, y, contentHeight); + final int leftOffset = homeLayout.getLeftOffset(); + x += positionChild(homeLayout, x + leftOffset, y, contentHeight) + leftOffset; } if (mExpandedActionView == null) { @@ -1171,6 +1170,7 @@ public class ActionBarView extends AbsActionBarView { private static class HomeView extends FrameLayout { private View mUpView; private ImageView mIconView; + private int mUpWidth; public HomeView(Context context) { this(context, null); @@ -1194,15 +1194,16 @@ public class ActionBarView extends AbsActionBarView { mIconView = (ImageView) findViewById(com.android.internal.R.id.home); } - public int getVerticalIconPadding() { - return mIconView.getPaddingTop() + mIconView.getPaddingBottom(); + public int getLeftOffset() { + return mUpView.getVisibility() == GONE ? mUpWidth : 0; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { measureChildWithMargins(mUpView, widthMeasureSpec, 0, heightMeasureSpec, 0); final LayoutParams upLp = (LayoutParams) mUpView.getLayoutParams(); - int width = upLp.leftMargin + mUpView.getMeasuredWidth() + upLp.rightMargin; + mUpWidth = upLp.leftMargin + mUpView.getMeasuredWidth() + upLp.rightMargin; + int width = mUpView.getVisibility() == GONE ? 0 : mUpWidth; int height = upLp.topMargin + mUpView.getMeasuredHeight() + upLp.bottomMargin; measureChildWithMargins(mIconView, widthMeasureSpec, width, heightMeasureSpec, 0); final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams(); diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index e4322c632739..804f28a38607 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -83,6 +83,13 @@ public class LockPatternUtils { */ public static final long FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS = 1000L; + + /** + * This dictates when we start telling the user that continued failed attempts will wipe + * their device. + */ + public static final int FAILED_ATTEMPTS_BEFORE_WIPE_GRACE = 5; + /** * The minimum number of dots in a valid pattern. */ @@ -93,7 +100,7 @@ public class LockPatternUtils { * attempt for it to be counted against the counts that affect * {@link #FAILED_ATTEMPTS_BEFORE_TIMEOUT} and {@link #FAILED_ATTEMPTS_BEFORE_RESET} */ - public static final int MIN_PATTERN_REGISTER_FAIL = 3; + public static final int MIN_PATTERN_REGISTER_FAIL = MIN_LOCK_PATTERN_SIZE; private final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently"; private final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline"; @@ -112,6 +119,7 @@ public class LockPatternUtils { private static final AtomicBoolean sHaveNonZeroPatternFile = new AtomicBoolean(false); private static final AtomicBoolean sHaveNonZeroPasswordFile = new AtomicBoolean(false); + private static FileObserver sPasswordObserver; private static class PasswordFileObserver extends FileObserver { diff --git a/core/res/res/layout/activity_list_item.xml b/core/res/res/layout/activity_list_item.xml index 25d95fd74e5d..7022fe1858e3 100644 --- a/core/res/res/layout/activity_list_item.xml +++ b/core/res/res/layout/activity_list_item.xml @@ -22,8 +22,8 @@ android:layout_height="wrap_content" android:paddingTop="1dip" android:paddingBottom="1dip" - android:paddingLeft="6dip" - android:paddingRight="6dip"> + android:paddingLeft="8dip" + android:paddingRight="8dip"> <ImageView android:id="@+id/icon" android:layout_width="24dip" diff --git a/core/res/res/layout/input_method_extract_view.xml b/core/res/res/layout/input_method_extract_view.xml index 60bc24c246ab..a3e4961ff5ff 100644 --- a/core/res/res/layout/input_method_extract_view.xml +++ b/core/res/res/layout/input_method_extract_view.xml @@ -40,7 +40,6 @@ android:layout_height="match_parent" android:paddingLeft="8dip" android:paddingRight="8dip" - android:background="@android:drawable/keyboard_accessory_bg_landscape" > <android.inputmethodservice.ExtractButton android:id="@+id/inputExtractAction" diff --git a/core/res/res/layout/list_menu_item_layout.xml b/core/res/res/layout/list_menu_item_layout.xml index aaff4c789a81..93bd76b732de 100644 --- a/core/res/res/layout/list_menu_item_layout.xml +++ b/core/res/res/layout/list_menu_item_layout.xml @@ -26,8 +26,8 @@ android:layout_weight="1" android:layout_height="wrap_content" android:layout_gravity="center_vertical" - android:layout_marginLeft="6dip" - android:layout_marginRight="6dip" + android:layout_marginLeft="16dip" + android:layout_marginRight="16dip" android:duplicateParentState="true"> <TextView @@ -36,7 +36,7 @@ android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" - android:textAppearance="?android:attr/textAppearanceLarge" + android:textAppearance="?android:attr/textAppearanceListItemSmall" android:singleLine="true" android:duplicateParentState="true" android:ellipsize="marquee" diff --git a/core/res/res/layout/select_dialog_item_holo.xml b/core/res/res/layout/select_dialog_item_holo.xml index 0c700cfd1c97..3d19c06a147b 100644 --- a/core/res/res/layout/select_dialog_item_holo.xml +++ b/core/res/res/layout/select_dialog_item_holo.xml @@ -27,7 +27,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="?android:attr/listPreferredItemHeightSmall" - android:textAppearance="?android:attr/textAppearanceMedium" + android:textAppearance="?android:attr/textAppearanceListItemSmall" android:textColor="?android:attr/textColorAlertDialogListItem" android:gravity="center_vertical" android:paddingLeft="16dip" diff --git a/core/res/res/layout/simple_expandable_list_item_1.xml b/core/res/res/layout/simple_expandable_list_item_1.xml index dc3e58e7f3a4..df4324b17f4a 100644 --- a/core/res/res/layout/simple_expandable_list_item_1.xml +++ b/core/res/res/layout/simple_expandable_list_item_1.xml @@ -19,6 +19,6 @@ android:layout_width="match_parent" android:layout_height="?android:attr/listPreferredItemHeight" android:paddingLeft="?android:attr/expandableListPreferredItemPaddingLeft" - android:textAppearance="?android:attr/textAppearanceLarge" + android:textAppearance="?android:attr/textAppearanceListItem" android:gravity="center_vertical" /> diff --git a/core/res/res/layout/simple_expandable_list_item_2.xml b/core/res/res/layout/simple_expandable_list_item_2.xml index b48b444e66d8..c0935fa282e5 100644 --- a/core/res/res/layout/simple_expandable_list_item_2.xml +++ b/core/res/res/layout/simple_expandable_list_item_2.xml @@ -27,7 +27,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="6dip" - android:textAppearance="?android:attr/textAppearanceLarge" + android:textAppearance="?android:attr/textAppearanceListItem" /> <TextView android:id="@android:id/text2" diff --git a/core/res/res/layout/simple_list_item_1.xml b/core/res/res/layout/simple_list_item_1.xml index c9c77a5f9c11..252e006a307f 100644 --- a/core/res/res/layout/simple_list_item_1.xml +++ b/core/res/res/layout/simple_list_item_1.xml @@ -18,8 +18,9 @@ android:id="@android:id/text1" android:layout_width="match_parent" android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceLarge" + android:textAppearance="?android:attr/textAppearanceListItem" android:gravity="center_vertical" - android:paddingLeft="6dip" + android:paddingLeft="8dip" + android:paddingRight="8dip" android:minHeight="?android:attr/listPreferredItemHeight" /> diff --git a/core/res/res/layout/simple_list_item_2.xml b/core/res/res/layout/simple_list_item_2.xml index c87922cef9b2..9b6c62a707fe 100644 --- a/core/res/res/layout/simple_list_item_2.xml +++ b/core/res/res/layout/simple_list_item_2.xml @@ -24,9 +24,9 @@ <TextView android:id="@android:id/text1" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="6dip" - android:layout_marginTop="6dip" - android:textAppearance="?android:attr/textAppearanceLarge" + android:layout_marginLeft="8dip" + android:layout_marginTop="8dip" + android:textAppearance="?android:attr/textAppearanceListItem" /> <TextView android:id="@android:id/text2" diff --git a/core/res/res/layout/simple_list_item_activated_1.xml b/core/res/res/layout/simple_list_item_activated_1.xml index 8416df2632e5..d60f93b46a86 100644 --- a/core/res/res/layout/simple_list_item_activated_1.xml +++ b/core/res/res/layout/simple_list_item_activated_1.xml @@ -18,7 +18,7 @@ android:id="@android:id/text1" android:layout_width="match_parent" android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceLarge" + android:textAppearance="?android:attr/textAppearanceListItem" android:gravity="center_vertical" android:background="?android:attr/activatedBackgroundIndicator" android:minHeight="?android:attr/listPreferredItemHeight" diff --git a/core/res/res/layout/simple_list_item_activated_2.xml b/core/res/res/layout/simple_list_item_activated_2.xml index 2ffbf0268233..5be5c9220547 100644 --- a/core/res/res/layout/simple_list_item_activated_2.xml +++ b/core/res/res/layout/simple_list_item_activated_2.xml @@ -27,8 +27,8 @@ <TextView android:id="@android:id/text1" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="6dip" - android:layout_marginTop="6dip" + android:layout_marginLeft="8dip" + android:layout_marginTop="8dip" android:textAppearance="?android:attr/textAppearanceLarge" /> diff --git a/core/res/res/layout/simple_list_item_checked.xml b/core/res/res/layout/simple_list_item_checked.xml index 5f99044e58fd..79d3a18464e1 100644 --- a/core/res/res/layout/simple_list_item_checked.xml +++ b/core/res/res/layout/simple_list_item_checked.xml @@ -18,9 +18,9 @@ android:id="@android:id/text1" android:layout_width="match_parent" android:layout_height="?android:attr/listPreferredItemHeight" - android:textAppearance="?android:attr/textAppearanceLarge" + android:textAppearance="?android:attr/textAppearanceListItem" android:gravity="center_vertical" android:checkMark="?android:attr/textCheckMark" - android:paddingLeft="6dip" - android:paddingRight="6dip" + android:paddingLeft="8dip" + android:paddingRight="8dip" /> diff --git a/core/res/res/layout/simple_list_item_multiple_choice.xml b/core/res/res/layout/simple_list_item_multiple_choice.xml index 05c66f31510a..03054278bd94 100644 --- a/core/res/res/layout/simple_list_item_multiple_choice.xml +++ b/core/res/res/layout/simple_list_item_multiple_choice.xml @@ -18,9 +18,9 @@ android:id="@android:id/text1" android:layout_width="match_parent" android:layout_height="?android:attr/listPreferredItemHeight" - android:textAppearance="?android:attr/textAppearanceLarge" + android:textAppearance="?android:attr/textAppearanceListItem" android:gravity="center_vertical" android:checkMark="?android:attr/listChoiceIndicatorMultiple" - android:paddingLeft="6dip" - android:paddingRight="6dip" + android:paddingLeft="8dip" + android:paddingRight="8dip" /> diff --git a/core/res/res/layout/simple_list_item_single_choice.xml b/core/res/res/layout/simple_list_item_single_choice.xml index 27afd1d6f4b5..ac4a4a8cfa35 100644 --- a/core/res/res/layout/simple_list_item_single_choice.xml +++ b/core/res/res/layout/simple_list_item_single_choice.xml @@ -18,9 +18,9 @@ android:id="@android:id/text1" android:layout_width="match_parent" android:layout_height="?android:attr/listPreferredItemHeight" - android:textAppearance="?android:attr/textAppearanceLarge" + android:textAppearance="?android:attr/textAppearanceListItem" android:gravity="center_vertical" android:checkMark="?android:attr/listChoiceIndicatorSingle" - android:paddingLeft="6dip" - android:paddingRight="6dip" + android:paddingLeft="8dip" + android:paddingRight="8dip" /> diff --git a/core/res/res/layout/simple_selectable_list_item.xml b/core/res/res/layout/simple_selectable_list_item.xml index 518bcd0fcbd4..6ce22d611d92 100644 --- a/core/res/res/layout/simple_selectable_list_item.xml +++ b/core/res/res/layout/simple_selectable_list_item.xml @@ -18,9 +18,9 @@ android:id="@android:id/text1" android:layout_width="match_parent" android:layout_height="?android:attr/listPreferredItemHeight" - android:textAppearance="?android:attr/textAppearanceLarge" + android:textAppearance="?android:attr/textAppearanceListItem" android:gravity="center_vertical" android:background="?android:attr/listChoiceBackgroundIndicator" - android:paddingLeft="6dip" - android:paddingRight="9dip" + android:paddingLeft="8dip" + android:paddingRight="8dip" /> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index aa3397fac6ab..8db6b4f346a7 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -236,6 +236,11 @@ <!-- The list item height for search results. @hide --> <attr name="searchResultListItemHeight" format="dimension" /> + <!-- The preferred TextAppearance for the primary text of list items. --> + <attr name="textAppearanceListItem" format="reference" /> + <!-- The preferred TextAppearance for the primary text of small list items. --> + <attr name="textAppearanceListItemSmall" format="reference" /> + <!-- The drawable for the list divider. --> <attr name="listDivider" format="reference" /> <!-- The list divider used in alert dialogs. --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 74989e617eac..8fbb09e068d1 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -57,8 +57,9 @@ <!-- Flag indicating whether the surface flinger is inefficient at performing a blur. Used by parts of the UI to turn off - the blur effect where it isn't worth the performance hit. --> - <bool name="config_sf_slowBlur">false</bool> + the blur effect where it isn't worth the performance hit. + As of Honeycomb, blurring is not supported anymore. --> + <bool name="config_sf_slowBlur">true</bool> <!-- The duration (in milliseconds) of a short animation. --> <integer name="config_shortAnimTime">200</integer> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 730d971d05ea..a6bf1e0b6a16 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1792,6 +1792,9 @@ <public type="attr" name="actionBarItemBackground" /> <public type="attr" name="actionModeSplitBackground" /> + <public type="attr" name="textAppearanceListItem" /> + <public type="attr" name="textAppearanceListItemSmall" /> + <public type="style" name="TextAppearance.SuggestionHighlight" /> <public type="style" name="Theme.Holo.Light.DarkActionBar" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 2548eb5d2e0b..c80923d21246 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1300,8 +1300,8 @@ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_writeApnSettings">change/intercept network settings and traffic</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_writeApnSettings">Allows an application to change network settings and to intercept and inspect all network traffic, - for example to change the proxy and port of any APN. Malicious applications could monitor, redirect, or modify network + <string name="permdesc_writeApnSettings">Allows an application to change network settings and to intercept and inspect all network traffic, + for example to change the proxy and port of any APN. Malicious applications could monitor, redirect, or modify network packets without your knowledge.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> @@ -1857,7 +1857,7 @@ \n\nPlease try again in <xliff:g id="number">%d</xliff:g> seconds. </string> - <!-- For the unlock screen, Information message shown in dialog when user is almost at the limit + <!-- For the unlock screen, informational message shown in dialog when user is almost at the limit where they will be locked out and may have to enter an alternate username/password to unlock the phone --> <string name="lockscreen_failed_attempts_almost_glogin" product="tablet"> You have incorrectly drawn your unlock pattern <xliff:g id="number">%d</xliff:g> times. @@ -1865,7 +1865,8 @@ you will be asked to unlock your tablet using your Google sign-in.\n\n Please try again in <xliff:g id="number">%d</xliff:g> seconds. </string> - <!-- For the unlock screen, Information message shown in dialog when user is almost at the limit + + <!-- For the unlock screen, informational message shown in dialog when user is almost at the limit where they will be locked out and may have to enter an alternate username/password to unlock the phone --> <string name="lockscreen_failed_attempts_almost_glogin" product="default"> You have incorrectly drawn your unlock pattern <xliff:g id="number">%d</xliff:g> times. @@ -1874,6 +1875,36 @@ Please try again in <xliff:g id="number">%d</xliff:g> seconds. </string> + <!-- For the unlock screen, informational message shown in dialog when user is almost at the limit + where the device will be wiped. --> + <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet"> + You have incorrectly attempted to unlock the tablet <xliff:g id="number">%d</xliff:g> times. + After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts, + the tablet will be reset to factory default and all user data will be lost. + </string> + + <!-- For the unlock screen, informational message shown in dialog when user is almost at the limit + where the device will be wiped. --> + <string name="lockscreen_failed_attempts_almost_at_wipe" product="default"> + You have incorrectly attempted to unlock the phone <xliff:g id="number">%d</xliff:g> times. + After <xliff:g id="number">%d</xliff:g> more unsuccessful attempts, + the phone will be reset to factory default and all user data will be lost. + </string> + + <!-- For the unlock screen, informational message shown in dialog when user has exceeded the + maximum attempts and the device will now be wiped --> + <string name="lockscreen_failed_attempts_now_wiping" product="tablet"> + You have incorrectly attempted to unlock the tablet <xliff:g id="number">%d</xliff:g> times. + The tablet will now be reset to factory default. + </string> + + <!-- For the unlock screen, informational message shown in dialog when user has exceeded the + maximum attempts and the device will now be wiped --> + <string name="lockscreen_failed_attempts_now_wiping" product="default"> + You have incorrectly attempted to unlock the phone <xliff:g id="number">%d</xliff:g> times. + The phone will now be reset to factory default. + </string> + <!-- On the unlock screen, countdown message shown while user is waiting to try again after too many failed attempts --> <string name="lockscreen_too_many_failed_attempts_countdown">Try again in <xliff:g id="number">%d</xliff:g> seconds.</string> @@ -2540,6 +2571,20 @@ <string name="smv_process">The process <xliff:g id="process">%1$s</xliff:g> has has violated its self-enforced StrictMode policy.</string> + <!-- [CHAR LIMIT=40] Title of dialog that is shown when performing a system upgrade. --> + <string name="android_upgrading_title">Android is upgrading...</string> + + <!-- [CHAR LIMIT=NONE] Message shown in upgrading dialog for each .apk that is optimized. --> + <string name="android_upgrading_apk">Optimizing application + <xliff:g id="number" example="123">%1$d</xliff:g> of + <xliff:g id="number" example="123">%2$d</xliff:g>.</string> + + <!-- [CHAR LIMIT=NONE] Message to show in upgrading dialog when reached the point of starting apps. --> + <string name="android_upgrading_starting_apps">Starting applications.</string> + + <!-- [CHAR LIMIT=NONE] Message to show in upgrading dialog when the bulk of the upgrade work is done. --> + <string name="android_upgrading_complete">Finishing boot.</string> + <!-- Notification text to tell the user that a heavy-weight application is running. --> <string name="heavy_weight_notification"><xliff:g id="app">%1$s</xliff:g> running</string> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 786cdb544f94..903fc047883f 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -122,6 +122,8 @@ please see themes_device_defaults.xml. <item name="listPreferredItemHeightSmall">?android:attr/listPreferredItemHeight</item> <item name="listPreferredItemHeightLarge">?android:attr/listPreferredItemHeight</item> <item name="dropdownListPreferredItemHeight">?android:attr/listPreferredItemHeight</item> + <item name="textAppearanceListItem">?android:attr/textAppearanceLarge</item> + <item name="textAppearanceListItemSmall">?android:attr/textAppearanceLarge</item> <!-- @hide --> <item name="searchResultListItemHeight">58dip</item> @@ -715,9 +717,9 @@ please see themes_device_defaults.xml. {@link android.inputmethodservice.InputMethodService} class. this inherits from Theme.Panel, but sets up IME appropriate animations and a few custom attributes. --> - <style name="Theme.Holo.InputMethod" parent="Theme.Holo.Panel"> + <style name="Theme.Holo.InputMethod" parent="Theme.Holo.Light.Panel"> <item name="android:windowAnimationStyle">@android:style/Animation.InputMethod</item> - <item name="android:imeFullscreenBackground">@android:drawable/input_method_fullscreen_background_holo</item> + <item name="android:imeFullscreenBackground">@android:drawable/screen_background_selector_light</item> <item name="android:imeExtractEnterAnimation">@android:anim/input_method_extract_enter</item> <item name="android:imeExtractExitAnimation">@android:anim/input_method_extract_exit</item> </style> @@ -920,6 +922,7 @@ please see themes_device_defaults.xml. <item name="listPreferredItemHeightSmall">48dip</item> <item name="listPreferredItemHeightLarge">80dip</item> <item name="dropdownListPreferredItemHeight">?android:attr/listPreferredItemHeightSmall</item> + <item name="textAppearanceListItemSmall">?android:attr/textAppearanceMedium</item> <!-- @hide --> <item name="searchResultListItemHeight">58dip</item> @@ -1222,6 +1225,7 @@ please see themes_device_defaults.xml. <item name="listPreferredItemHeightSmall">48dip</item> <item name="listPreferredItemHeightLarge">80dip</item> <item name="dropdownListPreferredItemHeight">?android:attr/listPreferredItemHeightSmall</item> + <item name="textAppearanceListItemSmall">?android:attr/textAppearanceMedium</item> <!-- @hide --> <item name="searchResultListItemHeight">58dip</item> diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index 0af017787305..52981259336e 100644 --- a/libs/hwui/LayerCache.cpp +++ b/libs/hwui/LayerCache.cpp @@ -162,7 +162,7 @@ bool LayerCache::put(Layer* layer) { // TODO: Use an LRU while (mSize + size > mMaxSize) { size_t position = 0; -#if LAYER_REMOVE_BIGGEST +#if LAYER_REMOVE_BIGGEST_FIRST position = mCache.size() - 1; #endif Layer* victim = mCache.itemAt(position).mLayer; diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h index 63bb824c1281..c14c9caa9a5a 100644 --- a/libs/hwui/LayerCache.h +++ b/libs/hwui/LayerCache.h @@ -19,6 +19,7 @@ #include "Debug.h" #include "Layer.h" +#include "Properties.h" #include "utils/SortedList.h" namespace android { @@ -28,11 +29,6 @@ namespace uirenderer { // Defines /////////////////////////////////////////////////////////////////////////////// -// Indicates whether to remove the biggest layers first, or the smaller ones -#define LAYER_REMOVE_BIGGEST 0 -// Textures used by layers must have dimensions multiples of this number -#define LAYER_SIZE 64 - // Debug #if DEBUG_LAYERS #define LAYER_LOGD(...) LOGD(__VA_ARGS__) diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index a0f806a209ba..e89d6ecd469f 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -1476,10 +1476,10 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, const float width = texture->width; const float height = texture->height; - const float u1 = (srcLeft + 0.5f) / width; - const float v1 = (srcTop + 0.5f) / height; - const float u2 = (srcRight - 0.5f) / width; - const float v2 = (srcBottom - 0.5f) / height; + const float u1 = fmax(0.0f, srcLeft / width); + const float v1 = fmax(0.0f, srcTop / height); + const float u2 = fmin(1.0f, srcRight / width); + const float v2 = fmin(1.0f, srcBottom / height); mCaches.unbindMeshBuffer(); resetDrawTextureTexCoords(u1, v1, u2, v2); diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 923978f67d7a..5bd0d4ffd217 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -31,6 +31,12 @@ // If turned on, text is interpreted as glyphs instead of UTF-16 #define RENDER_TEXT_AS_GLYPHS 1 +// Indicates whether to remove the biggest layers first, or the smaller ones +#define LAYER_REMOVE_BIGGEST_FIRST 0 + +// Textures used by layers must have dimensions multiples of this number +#define LAYER_SIZE 64 + /** * Debug level for app developers. */ diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index f9efd3ca6a69..e3ef71785dba 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1715,161 +1715,54 @@ public class AudioManager { } } - /** - * Acts as a proxy between AudioService and the RemoteControlClient - */ - private IRemoteControlClientDispatcher mRcClientDispatcher = - new IRemoteControlClientDispatcher.Stub() { - - public String getMetadataStringForClient(String clientName, int field) { - RemoteControlClient realClient; - synchronized(mRcClientMap) { - realClient = mRcClientMap.get(clientName); - } - if (realClient != null) { - return realClient.getMetadataString(field); - } else { - return null; - } - } - - public int getPlaybackStateForClient(String clientName) { - RemoteControlClient realClient; - synchronized(mRcClientMap) { - realClient = mRcClientMap.get(clientName); - } - if (realClient != null) { - return realClient.getPlaybackState(); - } else { - return 0; - } - } - - public int getTransportControlFlagsForClient(String clientName) { - RemoteControlClient realClient; - synchronized(mRcClientMap) { - realClient = mRcClientMap.get(clientName); - } - if (realClient != null) { - return realClient.getTransportControlFlags(); - } else { - return 0; - } - } - - public Bitmap getAlbumArtForClient(String clientName, int maxWidth, int maxHeight) { - RemoteControlClient realClient; - synchronized(mRcClientMap) { - realClient = mRcClientMap.get(clientName); - } - if (realClient != null) { - return realClient.getAlbumArt(maxWidth, maxHeight); - } else { - return null; - } - } - }; - - private HashMap<String, RemoteControlClient> mRcClientMap = - new HashMap<String, RemoteControlClient>(); - - private String getIdForRcClient(RemoteControlClient client) { - // client is guaranteed to be non-null - return client.toString(); - } /** * @hide + * CANDIDATE FOR SDK * Registers the remote control client for providing information to display on the remote * controls. - * @param eventReceiver identifier of a {@link android.content.BroadcastReceiver} - * that will receive the media button intent, and associated with the remote control - * client. This method has no effect if - * {@link #registerMediaButtonEventReceiver(ComponentName)} hasn't been called - * with the same eventReceiver, or if - * {@link #unregisterMediaButtonEventReceiver(ComponentName)} has been called. - * @param rcClient the remote control client associated with the event receiver, responsible + * @param rcClient the remote control client associated responsible * for providing the information to display on the remote control. */ - public void registerRemoteControlClient(ComponentName eventReceiver, - RemoteControlClient rcClient) { - if ((eventReceiver == null) || (rcClient == null)) { + public void registerRemoteControlClient(RemoteControlClient rcClient) { + if ((rcClient == null) || (rcClient.getRcEventReceiver() == null)) { return; } - String clientKey = getIdForRcClient(rcClient); - synchronized(mRcClientMap) { - if (mRcClientMap.containsKey(clientKey)) { - return; - } - mRcClientMap.put(clientKey, rcClient); - } IAudioService service = getService(); try { - service.registerRemoteControlClient(eventReceiver, mRcClientDispatcher, clientKey, + service.registerRemoteControlClient(rcClient.getRcEventReceiver(), /* eventReceiver */ + rcClient.getIRemoteControlClient(), /* rcClient */ + rcClient.toString(), /* clientName */ // used to match media button event receiver and audio focus - mContext.getPackageName()); + mContext.getPackageName()); /* packageName */ } catch (RemoteException e) { Log.e(TAG, "Dead object in registerRemoteControlClient"+e); - synchronized(mRcClientMap) { - mRcClientMap.remove(clientKey); - } } } /** * @hide + * CANDIDATE FOR SDK * Unregisters the remote control client that was providing information to display on the * remotes. - * @param eventReceiver identifier of a {@link android.content.BroadcastReceiver} - * that receives the media button intent, and associated with the remote control - * client. * @param rcClient the remote control client to unregister - * @see #registerRemoteControlClient(ComponentName, RemoteControlClient) + * @see #registerRemoteControlClient(RemoteControlClient) */ - public void unregisterRemoteControlClient(ComponentName eventReceiver, - RemoteControlClient rcClient) { - if ((eventReceiver == null) || (rcClient == null)) { + public void unregisterRemoteControlClient(RemoteControlClient rcClient) { + if ((rcClient == null) || (rcClient.getRcEventReceiver() == null)) { return; } IAudioService service = getService(); try { - // remove locally - boolean unregister = true; - synchronized(mRcClientMap) { - if (mRcClientMap.remove(getIdForRcClient(rcClient)) == null) { - unregister = false; - } - } - if (unregister) { - // unregistering a RemoteControlClient is equivalent to setting it to null - service.registerRemoteControlClient(eventReceiver, null, null, - mContext.getPackageName()); - } + service.unregisterRemoteControlClient(rcClient.getRcEventReceiver(), /* eventReceiver */ + rcClient.getIRemoteControlClient()); /* rcClient */ } catch (RemoteException e) { Log.e(TAG, "Dead object in unregisterRemoteControlClient"+e); } } - /** - * @hide - * Returns the current remote control client. - * @param rcClientId the generation counter that matches the extra - * {@link AudioManager#EXTRA_REMOTE_CONTROL_CLIENT_GENERATION} in the - * {@link AudioManager#REMOTE_CONTROL_CLIENT_CHANGED} event - * @return the current RemoteControlClient from which information to display on the remote - * control can be retrieved, or null if rcClientId doesn't match the current generation - * counter. - */ - public IRemoteControlClientDispatcher getRemoteControlClientDispatcher(int rcClientId) { - IAudioService service = getService(); - try { - return service.getRemoteControlClientDispatcher(rcClientId); - } catch (RemoteException e) { - Log.e(TAG, "Dead object in getRemoteControlClient "+e); - return null; - } - } + // FIXME remove because we are not using intents anymore between AudioService and RcDisplay /** * @hide * Broadcast intent action indicating that the displays on the remote controls @@ -1882,6 +1775,7 @@ public class AudioManager { public static final String REMOTE_CONTROL_CLIENT_CHANGED = "android.media.REMOTE_CONTROL_CLIENT_CHANGED"; + // FIXME remove because we are not using intents anymore between AudioService and RcDisplay /** * @hide * The IRemoteControlClientDispatcher monotonically increasing generation counter. @@ -1891,6 +1785,7 @@ public class AudioManager { public static final String EXTRA_REMOTE_CONTROL_CLIENT_GENERATION = "android.media.EXTRA_REMOTE_CONTROL_CLIENT_GENERATION"; + // FIXME remove because we are not using intents anymore between AudioService and RcDisplay /** * @hide * The name of the RemoteControlClient. @@ -1902,6 +1797,7 @@ public class AudioManager { public static final String EXTRA_REMOTE_CONTROL_CLIENT_NAME = "android.media.EXTRA_REMOTE_CONTROL_CLIENT_NAME"; + // FIXME remove because we are not using intents anymore between AudioService and RcDisplay /** * @hide * The media button event receiver associated with the RemoteControlClient. @@ -1913,6 +1809,7 @@ public class AudioManager { public static final String EXTRA_REMOTE_CONTROL_EVENT_RECEIVER = "android.media.EXTRA_REMOTE_CONTROL_EVENT_RECEIVER"; + // FIXME remove because we are not using intents anymore between AudioService and RcDisplay /** * @hide * The flags describing what information has changed in the current remote control client. @@ -1923,33 +1820,6 @@ public class AudioManager { "android.media.EXTRA_REMOTE_CONTROL_CLIENT_INFO_CHANGED"; /** - * @hide - * Notifies the users of the associated remote control client that the information to display - * has changed. - @param eventReceiver identifier of a {@link android.content.BroadcastReceiver} - * that will receive the media button intent, and associated with the remote control - * client. This method has no effect if - * {@link #registerMediaButtonEventReceiver(ComponentName)} hasn't been called - * with the same eventReceiver, or if - * {@link #unregisterMediaButtonEventReceiver(ComponentName)} has been called. - * @param infoFlag the type of information that has changed since this method was last called, - * or the event receiver was registered. Use one or multiple of the following flags to - * describe what changed: - * {@link RemoteControlClient#FLAG_INFORMATION_CHANGED_METADATA}, - * {@link RemoteControlClient#FLAG_INFORMATION_CHANGED_KEY_MEDIA}, - * {@link RemoteControlClient#FLAG_INFORMATION_CHANGED_PLAYSTATE}, - * {@link RemoteControlClient#FLAG_INFORMATION_CHANGED_ALBUM_ART}. - */ - public void notifyRemoteControlInformationChanged(ComponentName eventReceiver, int infoFlag) { - IAudioService service = getService(); - try { - service.notifyRemoteControlInformationChanged(eventReceiver, infoFlag); - } catch (RemoteException e) { - Log.e(TAG, "Dead object in refreshRemoteControlDisplay"+e); - } - } - - /** * @hide * Reload audio settings. This method is called by Settings backup * agent when audio settings are restored and causes the AudioService diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 85c7dba423d0..acc2b23a76ce 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -34,7 +34,6 @@ import android.content.pm.PackageManager; import android.database.ContentObserver; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; -import android.media.IRemoteControlClientDispatcher; import android.os.Binder; import android.os.Environment; import android.os.Handler; @@ -1557,17 +1556,14 @@ public class AudioService extends IAudioService.Stub { int newRingerMode = mRingerMode; if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) { - if ((direction == AudioManager.ADJUST_LOWER) && ((oldIndex + 5) / 10 <= 1)) { - // enter silent mode if current index is the last audible one and not repeating a - // volume key down - if (mPrevVolDirection != AudioManager.ADJUST_LOWER) { - // "silent mode", but which one? - newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1 - ? AudioManager.RINGER_MODE_VIBRATE - : AudioManager.RINGER_MODE_SILENT; - } else { - adjustVolumeIndex = false; - } + // audible mode, at the bottom of the scale + if ((direction == AudioManager.ADJUST_LOWER && + mPrevVolDirection != AudioManager.ADJUST_LOWER) && + ((oldIndex + 5) / 10 == 0)) { + // "silent mode", but which one? + newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1 + ? AudioManager.RINGER_MODE_VIBRATE + : AudioManager.RINGER_MODE_SILENT; } } else { if (direction == AudioManager.ADJUST_RAISE) { @@ -2170,45 +2166,12 @@ public class AudioService extends IAudioService.Stub { break; case MSG_RCDISPLAY_CLEAR: - // TODO remove log before release - Log.i(TAG, "Clear remote control display"); - Intent clearIntent = new Intent(AudioManager.REMOTE_CONTROL_CLIENT_CHANGED); - // no extra means no IRemoteControlClientDispatcher, which is a request to clear - clearIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - mContext.sendBroadcast(clearIntent); + onRcDisplayClear(); break; case MSG_RCDISPLAY_UPDATE: - synchronized(mCurrentRcLock) { - // msg.obj is guaranteed to be non null - RemoteControlStackEntry rcse = (RemoteControlStackEntry)msg.obj; - if ((mCurrentRcClient == null) || - (!mCurrentRcClient.equals(rcse.mRcClient))) { - // the remote control display owner has changed between the - // the message to update the display was sent, and the time it - // gets to be processed (now) - } else { - mCurrentRcClientGen++; - // TODO remove log before release - Log.i(TAG, "Display/update remote control "); - Intent rcClientIntent = new Intent( - AudioManager.REMOTE_CONTROL_CLIENT_CHANGED); - rcClientIntent.putExtra( - AudioManager.EXTRA_REMOTE_CONTROL_CLIENT_GENERATION, - mCurrentRcClientGen); - rcClientIntent.putExtra( - AudioManager.EXTRA_REMOTE_CONTROL_CLIENT_INFO_CHANGED, - msg.arg1); - rcClientIntent.putExtra( - AudioManager.EXTRA_REMOTE_CONTROL_EVENT_RECEIVER, - rcse.mReceiverComponent.flattenToString()); - rcClientIntent.putExtra( - AudioManager.EXTRA_REMOTE_CONTROL_CLIENT_NAME, - rcse.mRcClientName); - rcClientIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - mContext.sendBroadcast(rcClientIntent); - } - } + // msg.obj is guaranteed to be non null + onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1); break; case MSG_BT_HEADSET_CNCT_FAILED: @@ -2896,18 +2859,18 @@ public class AudioService extends IAudioService.Stub { private final Object mCurrentRcLock = new Object(); /** - * The one remote control client to be polled for display information. + * The one remote control client which will receive a request for display information. * This object may be null. * Access protected by mCurrentRcLock. */ - private IRemoteControlClientDispatcher mCurrentRcClient = null; + private IRemoteControlClient mCurrentRcClient = null; private final static int RC_INFO_NONE = 0; private final static int RC_INFO_ALL = - RemoteControlClient.FLAG_INFORMATION_CHANGED_ALBUM_ART | - RemoteControlClient.FLAG_INFORMATION_CHANGED_KEY_MEDIA | - RemoteControlClient.FLAG_INFORMATION_CHANGED_METADATA | - RemoteControlClient.FLAG_INFORMATION_CHANGED_PLAYSTATE; + RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART | + RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA | + RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA | + RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE; /** * A monotonically increasing generation counter for mCurrentRcClient. @@ -2917,25 +2880,6 @@ public class AudioService extends IAudioService.Stub { private int mCurrentRcClientGen = 0; /** - * Returns the current remote control client. - * @param rcClientId the counter value that matches the extra - * {@link AudioManager#EXTRA_REMOTE_CONTROL_CLIENT_GENERATION} in the - * {@link AudioManager#REMOTE_CONTROL_CLIENT_CHANGED} event - * @return the current IRemoteControlClientDispatcher from which information to display on the - * remote control can be retrieved, or null if rcClientId doesn't match the current - * generation counter. - */ - public IRemoteControlClientDispatcher getRemoteControlClientDispatcher(int rcClientId) { - synchronized(mCurrentRcLock) { - if (rcClientId == mCurrentRcClientGen) { - return mCurrentRcClient; - } else { - return null; - } - } - } - - /** * Inner class to monitor remote control client deaths, and remove the client for the * remote control stack if necessary. */ @@ -2968,7 +2912,7 @@ public class AudioService extends IAudioService.Stub { public int mCallingUid; /** provides access to the information to display on the remote control */ - public IRemoteControlClientDispatcher mRcClient; + public IRemoteControlClient mRcClient; public RcClientDeathHandler mRcClientDeathHandler; public RemoteControlStackEntry(ComponentName r) { @@ -3122,6 +3066,103 @@ public class AudioService extends IAudioService.Stub { return false; } + //========================================================================================== + // Remote control display / client + //========================================================================================== + /** + * Update the remote control displays with the new "focused" client generation + */ + private void setNewRcClientGenerationOnDisplays_syncRcStack(int newClientGeneration) { + // NOTE: Only one IRemoteControlDisplay supported in this implementation + if (mRcDisplay != null) { + try { + mRcDisplay.setCurrentClientGenerationId(newClientGeneration); + } catch (RemoteException e) { + Log.e(TAG, "Dead display in onRcDisplayUpdate() "+e); + // if we had a display before, stop monitoring its death + rcDisplay_stopDeathMonitor_syncRcStack(); + mRcDisplay = null; + } + } + } + + /** + * Update the remote control clients with the new "focused" client generation + */ + private void setNewRcClientGenerationOnClients_syncRcStack(int newClientGeneration) { + Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); + while(stackIterator.hasNext()) { + RemoteControlStackEntry se = stackIterator.next(); + if ((se != null) && (se.mRcClient != null)) { + try { + se.mRcClient.setCurrentClientGenerationId(newClientGeneration); + } catch (RemoteException e) { + Log.w(TAG, "Dead client in onRcDisplayUpdate()"+e); + stackIterator.remove(); + se.unlinkToRcClientDeath(); + } + } + } + } + + /** + * Update the displays and clients with the new "focused" client generation + */ + private void setNewRcClientGeneration(int newClientGeneration) { + synchronized(mRCStack) { + // send the new valid client generation ID to all displays + setNewRcClientGenerationOnDisplays_syncRcStack(newClientGeneration); + // send the new valid client generation ID to all clients + setNewRcClientGenerationOnClients_syncRcStack(newClientGeneration); + } + } + + /** + * Called when processing MSG_RCDISPLAY_CLEAR event + */ + private void onRcDisplayClear() { + // TODO remove log before release + Log.i(TAG, "Clear remote control display"); + + synchronized(mCurrentRcLock) { + mCurrentRcClientGen++; + + // synchronously update the displays and clients with the new client generation + setNewRcClientGeneration(mCurrentRcClientGen); + } + } + + /** + * Called when processing MSG_RCDISPLAY_UPDATE event + */ + private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) { + synchronized(mCurrentRcLock) { + if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) { + // TODO remove log before release + Log.i(TAG, "Display/update remote control "); + + mCurrentRcClientGen++; + + // synchronously update the displays and clients with the new client generation + setNewRcClientGeneration(mCurrentRcClientGen); + + // ask the current client that it needs to send info + try { + mCurrentRcClient.onInformationRequested(mCurrentRcClientGen, + flags, mArtworkExpectedWidth, mArtworkExpectedHeight); + } catch (RemoteException e) { + Log.e(TAG, "Current valid remote client is dead: "+e); + mCurrentRcClient = null; + } + } else { + // the remote control display owner has changed between the + // the message to update the display was sent, and the time it + // gets to be processed (now) + } + } + } + + /** * Helper function: * Called synchronized on mRCStack @@ -3130,6 +3171,7 @@ public class AudioService extends IAudioService.Stub { synchronized(mCurrentRcLock) { mCurrentRcClient = null; } + // will cause onRcDisplayClear() to be called in AudioService's handler thread mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) ); } @@ -3155,6 +3197,7 @@ public class AudioService extends IAudioService.Stub { } mCurrentRcClient = rcse.mRcClient; } + // will cause onRcDisplayUpdate() to be called in AudioService's handler thread mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE, infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) ); } @@ -3223,7 +3266,7 @@ public class AudioService extends IAudioService.Stub { /** see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...) */ public void registerRemoteControlClient(ComponentName eventReceiver, - IRemoteControlClientDispatcher rcClient, String clientName, String callingPackageName) { + IRemoteControlClient rcClient, String clientName, String callingPackageName) { synchronized(mAudioFocusLock) { synchronized(mRCStack) { // store the new display information @@ -3238,6 +3281,14 @@ public class AudioService extends IAudioService.Stub { } // save the new remote control client rcse.mRcClient = rcClient; + if (mRcDisplay != null) { + try { + rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay); + } catch (RemoteException e) { + Log.e(TAG, "Error connecting remote control display to client: "+e); + e.printStackTrace(); + } + } rcse.mCallingPackageName = callingPackageName; rcse.mRcClientName = clientName; rcse.mCallingUid = Binder.getCallingUid(); @@ -3269,18 +3320,121 @@ public class AudioService extends IAudioService.Stub { } } - /** see AudioManager.notifyRemoteControlInformationChanged(ComponentName er, int infoFlag) */ - public void notifyRemoteControlInformationChanged(ComponentName eventReceiver, int infoFlag) { - synchronized(mAudioFocusLock) { + /** see AudioManager.unregisterRemoteControlClient(ComponentName eventReceiver, ...) */ + public void unregisterRemoteControlClient(ComponentName eventReceiver, + IRemoteControlClient rcClient) { + //FIXME implement + } + + /** + * The remote control displays. + * Access synchronized on mRCStack + * NOTE: Only one IRemoteControlDisplay supported in this implementation + */ + private IRemoteControlDisplay mRcDisplay; + private RcDisplayDeathHandler mRcDisplayDeathHandler; + private int mArtworkExpectedWidth = -1; + private int mArtworkExpectedHeight = -1; + /** + * Inner class to monitor remote control display deaths, and unregister them from the list + * of displays if necessary. + */ + private class RcDisplayDeathHandler implements IBinder.DeathRecipient { + public void binderDied() { synchronized(mRCStack) { - // only refresh if the eventReceiver is at the top of the stack - if (isCurrentRcController(eventReceiver)) { - checkUpdateRemoteControlDisplay(infoFlag); + Log.w(TAG, " RemoteControl: display died"); + mRcDisplay = null; + } + } + + } + + private void rcDisplay_stopDeathMonitor_syncRcStack() { + if (mRcDisplay != null) { + // we had a display before, stop monitoring its death + IBinder b = mRcDisplay.asBinder(); + try { + b.unlinkToDeath(mRcDisplayDeathHandler, 0); + } catch (java.util.NoSuchElementException e) { + // being conservative here + Log.e(TAG, "Error while trying to unlink display death handler " + e); + e.printStackTrace(); + } + } + } + + private void rcDisplay_startDeathMonitor_syncRcStack() { + if (mRcDisplay != null) { + // new non-null display, monitor its death + IBinder b = mRcDisplay.asBinder(); + mRcDisplayDeathHandler = new RcDisplayDeathHandler(); + try { + b.linkToDeath(mRcDisplayDeathHandler, 0); + } catch (RemoteException e) { + // remote control display is DOA, disqualify it + Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + b); + mRcDisplay = null; + } + } + } + + public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) { + synchronized(mRCStack) { + if (mRcDisplay == rcd) { + return; + } + // if we had a display before, stop monitoring its death + rcDisplay_stopDeathMonitor_syncRcStack(); + mRcDisplay = rcd; + // new display, start monitoring its death + rcDisplay_startDeathMonitor_syncRcStack(); + + // let all the remote control clients there is a new display + Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); + while(stackIterator.hasNext()) { + RemoteControlStackEntry rcse = stackIterator.next(); + if(rcse.mRcClient != null) { + try { + rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay); + } catch (RemoteException e) { + Log.e(TAG, "Error connecting remote control display to client: " + e); + e.printStackTrace(); + } + } + } + } + } + + public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) { + synchronized(mRCStack) { + // if we had a display before, stop monitoring its death + rcDisplay_stopDeathMonitor_syncRcStack(); + mRcDisplay = null; + + // disconnect this remote control display from all the clients + Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); + while(stackIterator.hasNext()) { + RemoteControlStackEntry rcse = stackIterator.next(); + if(rcse.mRcClient != null) { + try { + rcse.mRcClient.unplugRemoteControlDisplay(rcd); + } catch (RemoteException e) { + Log.e(TAG, "Error disconnecting remote control display to client: " + e); + e.printStackTrace(); + } } } } } + public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) { + synchronized(mRCStack) { + // NOTE: Only one IRemoteControlDisplay supported in this implementation + mArtworkExpectedWidth = w; + mArtworkExpectedHeight = h; + } + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { // TODO probably a lot more to do here than just the audio focus and remote control stacks diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 7f9ced9f055e..7bf981442b27 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -18,7 +18,8 @@ package android.media; import android.content.ComponentName; import android.media.IAudioFocusDispatcher; -import android.media.IRemoteControlClientDispatcher; +import android.media.IRemoteControlClient; +import android.media.IRemoteControlDisplay; /** * {@hide} @@ -88,13 +89,14 @@ interface IAudioService { void unregisterMediaButtonEventReceiver(in ComponentName eventReceiver); - void registerRemoteControlClient(in ComponentName eventReceiver, - in IRemoteControlClientDispatcher rcClient, in String clientName, - in String callingPackageName); + oneway void registerRemoteControlClient(in ComponentName eventReceiver, + in IRemoteControlClient rcClient, in String clientName, in String callingPackageName); + oneway void unregisterRemoteControlClient(in ComponentName eventReceiver, + in IRemoteControlClient rcClient); - IRemoteControlClientDispatcher getRemoteControlClientDispatcher(in int rcClientId); - - void notifyRemoteControlInformationChanged(in ComponentName eventReceiver, int infoFlag); + oneway void registerRemoteControlDisplay(in IRemoteControlDisplay rcd); + oneway void unregisterRemoteControlDisplay(in IRemoteControlDisplay rcd); + oneway void remoteControlDisplayUsesBitmapSize(in IRemoteControlDisplay rcd, int w, int h); void startBluetoothSco(IBinder cb); diff --git a/media/java/android/media/IRemoteControlClient.aidl b/media/java/android/media/IRemoteControlClient.aidl new file mode 100644 index 000000000000..0fbba2026ac0 --- /dev/null +++ b/media/java/android/media/IRemoteControlClient.aidl @@ -0,0 +1,51 @@ +/* Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.graphics.Bitmap; +import android.media.IRemoteControlDisplay; + +/** + * @hide + * Interface registered by AudioManager to notify a source of remote control information + * that information is requested to be displayed on the remote control (through + * IRemoteControlDisplay). + * {@see AudioManager#registerRemoteControlClient(RemoteControlClient)}. + */ +oneway interface IRemoteControlClient +{ + /** + * Notifies a remote control client that information for the given generation ID is + * requested. If the flags contains + * {@link RemoteControlClient#FLAG_INFORMATION_REQUESTED_ALBUM_ART} then the width and height + * parameters are valid. + * @param generationId + * @param infoFlags + * @param artWidth if > 0, artHeight must be > 0 too. + * @param artHeight + * FIXME: is infoFlags required? since the RCC pushes info, this might always be called + * with RC_INFO_ALL + */ + void onInformationRequested(int generationId, int infoFlags, int artWidth, int artHeight); + + /** + * Sets the generation counter of the current client that is displayed on the remote control. + */ + void setCurrentClientGenerationId(int clientGeneration); + + void plugRemoteControlDisplay(IRemoteControlDisplay rcd); + void unplugRemoteControlDisplay(IRemoteControlDisplay rcd); +}
\ No newline at end of file diff --git a/media/java/android/media/IRemoteControlClientDispatcher.aidl b/media/java/android/media/IRemoteControlClientDispatcher.aidl deleted file mode 100644 index 98142cc5dd9b..000000000000 --- a/media/java/android/media/IRemoteControlClientDispatcher.aidl +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.media; - -import android.graphics.Bitmap; - -/** - * @hide - * Interface registered by AudioManager to dispatch remote control information requests - * to the RemoteControlClient implementation. This is used by AudioService. - * {@see AudioManager#registerRemoteControlClient(ComponentName, RemoteControlClient)}. - */ -interface IRemoteControlClientDispatcher -{ - /** - * Called by a remote control to retrieve a String of information to display. - * @param field the identifier for a metadata field to retrieve. Valid values are - * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUM}, - * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUMARTIST}, - * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE}, - * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ARTIST}, - * {@link android.media.MediaMetadataRetriever#METADATA_KEY_AUTHOR}, - * {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER}, - * {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPILATION}, - * {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPOSER}, - * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DATE}, - * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER}, - * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION}, - * {@link android.media.MediaMetadataRetriever#METADATA_KEY_GENRE}, - * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE}, - * {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER}, - * {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}. - * @return null if the requested field is not supported, or the String matching the - * metadata field. - */ - String getMetadataStringForClient(String clientName, int field); - - /** - * Called by a remote control to retrieve the current playback state. - * @return one of the following values: - * {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_STOPPED}, - * {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_PAUSED}, - * {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_PLAYING}, - * {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_FAST_FORWARDING}, - * {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_REWINDING}, - * {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_SKIPPING_FORWARDS}, - * {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_SKIPPING_BACKWARDS}, - * {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_BUFFERING}, - * {@link android.media.AudioManager.RemoteControlParameters#PLAYSTATE_ERROR}. - */ - int getPlaybackStateForClient(String clientName); - - /** - * Called by a remote control to retrieve the flags for the media transport control buttons - * that this client supports. - * @see {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_PREVIOUS}, - * {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_REWIND}, - * {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_PLAY}, - * {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_PLAY_PAUSE}, - * {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_PAUSE}, - * {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_STOP}, - * {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_FAST_FORWARD}, - * {@link android.media.AudioManager.RemoteControlParameters#FLAG_KEY_MEDIA_NEXT} - */ - int getTransportControlFlagsForClient(String clientName); - - /** - * Called by a remote control to retrieve the album art picture at the requested size. - * Note that returning a bitmap smaller than the maximum requested dimension is accepted - * and it will be scaled as needed, but exceeding the maximum dimensions may produce - * unspecified results, such as the image being cropped or simply not being displayed. - * @param maxWidth the maximum width of the requested bitmap expressed in pixels. - * @param maxHeight the maximum height of the requested bitmap expressed in pixels. - * @return the bitmap for the album art, or null if there isn't any. - * @see android.graphics.Bitmap - */ - Bitmap getAlbumArtForClient(String clientName, int maxWidth, int maxHeight); -} diff --git a/media/java/android/media/IRemoteControlDisplay.aidl b/media/java/android/media/IRemoteControlDisplay.aidl new file mode 100644 index 000000000000..19ea2028f114 --- /dev/null +++ b/media/java/android/media/IRemoteControlDisplay.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.graphics.Bitmap; +import android.os.Bundle; + +/** + * @hide + * Interface registered through AudioManager of an object that displays information + * received from a remote control client. + * {@see AudioManager#registerRemoteControlDisplay(IRemoteControlDisplay)}. + */ +oneway interface IRemoteControlDisplay +{ + /** + * Sets the generation counter of the current client that is displayed on the remote control. + */ + void setCurrentClientGenerationId(int clientGeneration); + + void setPlaybackState(int generationId, int state); + + void setMetadata(int generationId, in Bundle metadata); + + void setTransportControlFlags(int generationId, int transportControlFlags); + + void setArtwork(int generationId, in Bitmap artwork); +} diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java index c384636ae780..bfe08b93699d 100644 --- a/media/java/android/media/RemoteControlClient.java +++ b/media/java/android/media/RemoteControlClient.java @@ -17,69 +17,84 @@ package android.media; import android.content.ComponentName; +import android.content.SharedPreferences.Editor; import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.util.Log; + +import java.util.HashMap; /** * @hide - * Interface for an object that exposes information meant to be consumed by remote controls + * CANDIDATE FOR SDK + * RemoteControlClient enables exposing information meant to be consumed by remote controls * capable of displaying metadata, album art and media transport control buttons. - * Such a remote control client object is associated with a media button event receiver + * A remote control client object is associated with a media button event receiver * when registered through - * {@link AudioManager#registerRemoteControlClient(ComponentName, RemoteControlClient)}. + * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}. */ -public interface RemoteControlClient +public class RemoteControlClient { + private final static String TAG = "RemoteControlClient"; + /** * Playback state of a RemoteControlClient which is stopped. * - * @see android.media.RemoteControlClient#getPlaybackState() + * @see #setPlaybackState(int) */ public final static int PLAYSTATE_STOPPED = 1; /** * Playback state of a RemoteControlClient which is paused. * - * @see android.media.RemoteControlClient#getPlaybackState() + * @see #setPlaybackState(int) */ public final static int PLAYSTATE_PAUSED = 2; /** * Playback state of a RemoteControlClient which is playing media. * - * @see android.media.RemoteControlClient#getPlaybackState() + * @see #setPlaybackState(int) */ public final static int PLAYSTATE_PLAYING = 3; /** * Playback state of a RemoteControlClient which is fast forwarding in the media * it is currently playing. * - * @see android.media.RemoteControlClient#getPlaybackState() + * @see #setPlaybackState(int) */ public final static int PLAYSTATE_FAST_FORWARDING = 4; /** * Playback state of a RemoteControlClient which is fast rewinding in the media * it is currently playing. * - * @see android.media.RemoteControlClient#getPlaybackState() + * @see #setPlaybackState(int) */ public final static int PLAYSTATE_REWINDING = 5; /** * Playback state of a RemoteControlClient which is skipping to the next * logical chapter (such as a song in a playlist) in the media it is currently playing. * - * @see android.media.RemoteControlClient#getPlaybackState() + * @see #setPlaybackState(int) */ public final static int PLAYSTATE_SKIPPING_FORWARDS = 6; /** * Playback state of a RemoteControlClient which is skipping back to the previous * logical chapter (such as a song in a playlist) in the media it is currently playing. * - * @see android.media.RemoteControlClient#getPlaybackState() + * @see #setPlaybackState(int) */ public final static int PLAYSTATE_SKIPPING_BACKWARDS = 7; /** * Playback state of a RemoteControlClient which is buffering data to play before it can * start or resume playback. * - * @see android.media.RemoteControlClient#getPlaybackState() + * @see #setPlaybackState(int) */ public final static int PLAYSTATE_BUFFERING = 8; /** @@ -88,98 +103,188 @@ public interface RemoteControlClient * connectivity when attempting to stream data from a server, or expired user credentials * when trying to play subscription-based content. * - * @see android.media.RemoteControlClient#getPlaybackState() + * @see #setPlaybackState(int) */ public final static int PLAYSTATE_ERROR = 9; + /** + * @hide + * The value of a playback state when none has been declared + */ + public final static int PLAYSTATE_NONE = 0; /** * Flag indicating a RemoteControlClient makes use of the "previous" media key. * - * @see android.media.RemoteControlClient#getTransportControlFlags() + * @see #setTransportControlFlags(int) * @see android.view.KeyEvent#KEYCODE_MEDIA_PREVIOUS */ public final static int FLAG_KEY_MEDIA_PREVIOUS = 1 << 0; /** * Flag indicating a RemoteControlClient makes use of the "rewing" media key. * - * @see android.media.RemoteControlClient#getTransportControlFlags() + * @see #setTransportControlFlags(int) * @see android.view.KeyEvent#KEYCODE_MEDIA_REWIND */ public final static int FLAG_KEY_MEDIA_REWIND = 1 << 1; /** * Flag indicating a RemoteControlClient makes use of the "play" media key. * - * @see android.media.RemoteControlClient#getTransportControlFlags() + * @see #setTransportControlFlags(int) * @see android.view.KeyEvent#KEYCODE_MEDIA_PLAY */ public final static int FLAG_KEY_MEDIA_PLAY = 1 << 2; /** * Flag indicating a RemoteControlClient makes use of the "play/pause" media key. * - * @see android.media.RemoteControlClient#getTransportControlFlags() + * @see #setTransportControlFlags(int) * @see android.view.KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE */ public final static int FLAG_KEY_MEDIA_PLAY_PAUSE = 1 << 3; /** * Flag indicating a RemoteControlClient makes use of the "pause" media key. * - * @see android.media.RemoteControlClient#getTransportControlFlags() + * @see #setTransportControlFlags(int) * @see android.view.KeyEvent#KEYCODE_MEDIA_PAUSE */ public final static int FLAG_KEY_MEDIA_PAUSE = 1 << 4; /** * Flag indicating a RemoteControlClient makes use of the "stop" media key. * - * @see android.media.RemoteControlClient#getTransportControlFlags() + * @see #setTransportControlFlags(int) * @see android.view.KeyEvent#KEYCODE_MEDIA_STOP */ public final static int FLAG_KEY_MEDIA_STOP = 1 << 5; /** * Flag indicating a RemoteControlClient makes use of the "fast forward" media key. * - * @see android.media.RemoteControlClient#getTransportControlFlags() + * @see #setTransportControlFlags(int) * @see android.view.KeyEvent#KEYCODE_MEDIA_FAST_FORWARD */ public final static int FLAG_KEY_MEDIA_FAST_FORWARD = 1 << 6; /** * Flag indicating a RemoteControlClient makes use of the "next" media key. * - * @see android.media.RemoteControlClient#getTransportControlFlags() + * @see #setTransportControlFlags(int) * @see android.view.KeyEvent#KEYCODE_MEDIA_NEXT */ public final static int FLAG_KEY_MEDIA_NEXT = 1 << 7; /** - * Flag used to signal that the metadata exposed by the RemoteControlClient has changed. - * - * @see #notifyRemoteControlInformationChanged(ComponentName, int) + * @hide + * The flags for when no media keys are declared supported + */ + public final static int FLAGS_KEY_MEDIA_NONE = 0; + + /** + * @hide + * Flag used to signal some type of metadata exposed by the RemoteControlClient is requested. */ - public final static int FLAG_INFORMATION_CHANGED_METADATA = 1 << 0; + public final static int FLAG_INFORMATION_REQUEST_METADATA = 1 << 0; /** + * @hide + * FIXME doc not valid * Flag used to signal that the transport control buttons supported by the * RemoteControlClient have changed. * This can for instance happen when playback is at the end of a playlist, and the "next" * operation is not supported anymore. - * - * @see #notifyRemoteControlInformationChanged(ComponentName, int) */ - public final static int FLAG_INFORMATION_CHANGED_KEY_MEDIA = 1 << 1; + public final static int FLAG_INFORMATION_REQUEST_KEY_MEDIA = 1 << 1; /** + * @hide + * FIXME doc not valid * Flag used to signal that the playback state of the RemoteControlClient has changed. - * - * @see #notifyRemoteControlInformationChanged(ComponentName, int) */ - public final static int FLAG_INFORMATION_CHANGED_PLAYSTATE = 1 << 2; + public final static int FLAG_INFORMATION_REQUEST_PLAYSTATE = 1 << 2; /** + * @hide + * FIXME doc not valid * Flag used to signal that the album art for the RemoteControlClient has changed. - * - * @see #notifyRemoteControlInformationChanged(ComponentName, int) */ - public final static int FLAG_INFORMATION_CHANGED_ALBUM_ART = 1 << 3; + public final static int FLAG_INFORMATION_REQUEST_ALBUM_ART = 1 << 3; + + /** + * Class constructor. + * @param mediaButtonEventReceiver the receiver for the media button events. + * @see AudioManager#registerMediaButtonEventReceiver(ComponentName) + * @see AudioManager#registerRemoteControlClient(RemoteControlClient) + */ + public RemoteControlClient(ComponentName mediaButtonEventReceiver) { + mRcEventReceiver = mediaButtonEventReceiver; + + Looper looper; + if ((looper = Looper.myLooper()) != null) { + mEventHandler = new EventHandler(this, looper); + } else if ((looper = Looper.getMainLooper()) != null) { + mEventHandler = new EventHandler(this, looper); + } else { + mEventHandler = null; + Log.e(TAG, "RemoteControlClient() couldn't find main application thread"); + } + } + + /** + * Class constructor for a remote control client whose internal event handling + * happens on a user-provided Looper. + * @param mediaButtonEventReceiver the receiver for the media button events. + * @param looper the Looper running the event loop. + * @see AudioManager#registerMediaButtonEventReceiver(ComponentName) + * @see AudioManager#registerRemoteControlClient(RemoteControlClient) + */ + public RemoteControlClient(ComponentName mediaButtonEventReceiver, Looper looper) { + mRcEventReceiver = mediaButtonEventReceiver; + + mEventHandler = new EventHandler(this, looper); + } + + /** + * Class used to modify metadata in a {@link RemoteControlClient} object. + */ + public class MetadataEditor { + + private MetadataEditor() { /* only use factory */ } + + public MetadataEditor putString(int key, String value) { + return this; + } + + public MetadataEditor putBitmap(int key, Bitmap bitmap) { + return this; + } + + public void clear() { + + } + + public void apply() { + + } + } + + public MetadataEditor editMetadata(boolean startEmpty) { + return (new MetadataEditor()); + } + + + /** + * @hide + * FIXME migrate this functionality under MetadataEditor + * Start collecting information to be displayed. + * Use {@link #commitMetadata()} to signal the end of the collection which has been created + * through one or multiple calls to {@link #addMetadataString(int, int, String)}. + */ + public void startMetadata() { + synchronized(mCacheLock) { + mMetadata.clear(); + } + } /** - * Called by a remote control to retrieve a String of information to display. - * @param field the identifier for a metadata field to retrieve. Valid values are + * @hide + * FIXME migrate this functionality under MetadataEditor + * Adds textual information to be displayed. + * Note that none of the information added before {@link #startMetadata()}, + * and after {@link #commitMetadata()} has been called, will be displayed. + * @param key the identifier of a the metadata field to set. Valid values are * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUM}, * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUMARTIST}, * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE}, @@ -195,14 +300,54 @@ public interface RemoteControlClient * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE}, * {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER}, * {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}. - * @return null if the requested field is not supported, or the String matching the - * metadata field. + * @param value the String for the field value, or null to signify there is no valid + * information for the field. */ - String getMetadataString(int field); + public void addMetadataString(int key, String value) { + synchronized(mCacheLock) { + // store locally + mMetadata.putString(String.valueOf(key), value); + } + } /** - * Called by a remote control to retrieve the current playback state. - * @return one of the following values: + * @hide + * FIXME migrate this functionality under MetadataEditor + * Marks all the metadata previously set with {@link #addMetadataString(int, int, String)} as + * eligible to be displayed. + */ + public void commitMetadata() { + synchronized(mCacheLock) { + // send to remote control display if conditions are met + sendMetadata_syncCacheLock(); + } + } + + /** + * @hide + * FIXME migrate this functionality under MetadataEditor + * Sets the album / artwork picture to be displayed on the remote control. + * @param artwork the bitmap for the artwork, or null if there isn't any. + * @see android.graphics.Bitmap + */ + public void setArtwork(Bitmap artwork) { + synchronized(mCacheLock) { + // resize and store locally + if (mArtworkExpectedWidth > 0) { + mArtwork = scaleBitmapIfTooBig(artwork, + mArtworkExpectedWidth, mArtworkExpectedHeight); + } else { + // no valid resize dimensions, store as is + mArtwork = artwork; + } + // send to remote control display if conditions are met + sendArtwork_syncCacheLock(); + } + } + + /** + * Sets the current playback state. + * @param state the current playback state, one of the following values: * {@link #PLAYSTATE_STOPPED}, * {@link #PLAYSTATE_PAUSED}, * {@link #PLAYSTATE_PLAYING}, @@ -213,12 +358,20 @@ public interface RemoteControlClient * {@link #PLAYSTATE_BUFFERING}, * {@link #PLAYSTATE_ERROR}. */ - int getPlaybackState(); + public void setPlaybackState(int state) { + synchronized(mCacheLock) { + // store locally + mPlaybackState = state; + + // send to remote control display if conditions are met + sendPlaybackState_syncCacheLock(); + } + } /** - * Called by a remote control to retrieve the flags for the media transport control buttons - * that this client supports. - * @see {@link #FLAG_KEY_MEDIA_PREVIOUS}, + * Sets the flags for the media transport control buttons that this client supports. + * @param a combination of the following flags: + * {@link #FLAG_KEY_MEDIA_PREVIOUS}, * {@link #FLAG_KEY_MEDIA_REWIND}, * {@link #FLAG_KEY_MEDIA_PLAY}, * {@link #FLAG_KEY_MEDIA_PLAY_PAUSE}, @@ -227,17 +380,311 @@ public interface RemoteControlClient * {@link #FLAG_KEY_MEDIA_FAST_FORWARD}, * {@link #FLAG_KEY_MEDIA_NEXT} */ - int getTransportControlFlags(); + public void setTransportControlFlags(int transportControlFlags) { + synchronized(mCacheLock) { + // store locally + mTransportControlFlags = transportControlFlags; + + // send to remote control display if conditions are met + sendTransportControlFlags_syncCacheLock(); + } + } /** - * Called by a remote control to retrieve the album art picture at the requested size. - * Note that returning a bitmap smaller than the maximum requested dimension is accepted - * and it will be scaled as needed, but exceeding the maximum dimensions may produce - * unspecified results, such as the image being cropped or simply not being displayed. - * @param maxWidth the maximum width of the requested bitmap expressed in pixels. - * @param maxHeight the maximum height of the requested bitmap expressed in pixels. - * @return the bitmap for the album art, or null if there isn't any. - * @see android.graphics.Bitmap + * Lock for all cached data + */ + private final Object mCacheLock = new Object(); + /** + * Cache for the playback state. + * Access synchronized on mCacheLock */ - Bitmap getAlbumArt(int maxWidth, int maxHeight); + private int mPlaybackState = PLAYSTATE_NONE; + /** + * Cache for the artwork bitmap. + * Access synchronized on mCacheLock + */ + private Bitmap mArtwork; + private final int ARTWORK_DEFAULT_SIZE = 256; + private int mArtworkExpectedWidth = ARTWORK_DEFAULT_SIZE; + private int mArtworkExpectedHeight = ARTWORK_DEFAULT_SIZE; + /** + * Cache for the transport control mask. + * Access synchronized on mCacheLock + */ + private int mTransportControlFlags = FLAGS_KEY_MEDIA_NONE; + /** + * Cache for the metadata strings. + * Access synchronized on mCacheLock + */ + private Bundle mMetadata = new Bundle(); + /** + * The current remote control client generation ID across the system + */ + private int mCurrentClientGenId = -1; + /** + * The remote control client generation ID, the last time it was told it was the current RC. + * If (mCurrentClientGenId == mInternalClientGenId) is true, it means that this remote control + * client is the "focused" one, and that whenever this client's info is updated, it needs to + * send it to the known IRemoteControlDisplay interfaces. + */ + private int mInternalClientGenId = -2; + + /** + * The media button event receiver associated with this remote control client + */ + private final ComponentName mRcEventReceiver; + + /** + * The remote control display to which this client will send information. + * NOTE: Only one IRemoteControlDisplay supported in this implementation + */ + private IRemoteControlDisplay mRcDisplay; + + /** + * @hide + * Accessor to media button event receiver + */ + public ComponentName getRcEventReceiver() { + return mRcEventReceiver; + } + /** + * @hide + * Accessor to IRemoteControlClient + */ + public IRemoteControlClient getIRemoteControlClient() { + return mIRCC; + } + + /** + * The IRemoteControlClient implementation + */ + private IRemoteControlClient mIRCC = new IRemoteControlClient.Stub() { + + public void onInformationRequested(int clientGeneration, int infoFlags, + int artWidth, int artHeight) { + // only post messages, we can't block here + if (mEventHandler != null) { + // signal new client + mEventHandler.removeMessages(MSG_NEW_INTERNAL_CLIENT_GEN); + mEventHandler.dispatchMessage( + mEventHandler.obtainMessage( + MSG_NEW_INTERNAL_CLIENT_GEN, + artWidth, artHeight, + new Integer(clientGeneration))); + // send the information + mEventHandler.removeMessages(MSG_REQUEST_PLAYBACK_STATE); + mEventHandler.removeMessages(MSG_REQUEST_METADATA); + mEventHandler.removeMessages(MSG_REQUEST_TRANSPORTCONTROL); + mEventHandler.removeMessages(MSG_REQUEST_ARTWORK); + mEventHandler.dispatchMessage( + mEventHandler.obtainMessage(MSG_REQUEST_PLAYBACK_STATE)); + mEventHandler.dispatchMessage( + mEventHandler.obtainMessage(MSG_REQUEST_TRANSPORTCONTROL)); + mEventHandler.dispatchMessage(mEventHandler.obtainMessage(MSG_REQUEST_METADATA)); + mEventHandler.dispatchMessage(mEventHandler.obtainMessage(MSG_REQUEST_ARTWORK)); + } + } + + public void setCurrentClientGenerationId(int clientGeneration) { + // only post messages, we can't block here + if (mEventHandler != null) { + mEventHandler.removeMessages(MSG_NEW_CURRENT_CLIENT_GEN); + mEventHandler.dispatchMessage(mEventHandler.obtainMessage( + MSG_NEW_CURRENT_CLIENT_GEN, clientGeneration, 0/*ignored*/)); + } + } + + public void plugRemoteControlDisplay(IRemoteControlDisplay rcd) { + // only post messages, we can't block here + if (mEventHandler != null) { + mEventHandler.dispatchMessage(mEventHandler.obtainMessage( + MSG_PLUG_DISPLAY, rcd)); + } + } + + public void unplugRemoteControlDisplay(IRemoteControlDisplay rcd) { + // only post messages, we can't block here + if (mEventHandler != null) { + mEventHandler.dispatchMessage(mEventHandler.obtainMessage( + MSG_UNPLUG_DISPLAY, rcd)); + } + } + }; + + private EventHandler mEventHandler; + private final static int MSG_REQUEST_PLAYBACK_STATE = 1; + private final static int MSG_REQUEST_METADATA = 2; + private final static int MSG_REQUEST_TRANSPORTCONTROL = 3; + private final static int MSG_REQUEST_ARTWORK = 4; + private final static int MSG_NEW_INTERNAL_CLIENT_GEN = 5; + private final static int MSG_NEW_CURRENT_CLIENT_GEN = 6; + private final static int MSG_PLUG_DISPLAY = 7; + private final static int MSG_UNPLUG_DISPLAY = 8; + + private class EventHandler extends Handler { + public EventHandler(RemoteControlClient rcc, Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch(msg.what) { + case MSG_REQUEST_PLAYBACK_STATE: + synchronized (mCacheLock) { + sendPlaybackState_syncCacheLock(); + } + break; + case MSG_REQUEST_METADATA: + synchronized (mCacheLock) { + sendMetadata_syncCacheLock(); + } + break; + case MSG_REQUEST_TRANSPORTCONTROL: + synchronized (mCacheLock) { + sendTransportControlFlags_syncCacheLock(); + } + break; + case MSG_REQUEST_ARTWORK: + synchronized (mCacheLock) { + sendArtwork_syncCacheLock(); + } + break; + case MSG_NEW_INTERNAL_CLIENT_GEN: + onNewInternalClientGen((Integer)msg.obj, msg.arg1, msg.arg2); + break; + case MSG_NEW_CURRENT_CLIENT_GEN: + onNewCurrentClientGen(msg.arg1); + break; + case MSG_PLUG_DISPLAY: + onPlugDisplay((IRemoteControlDisplay)msg.obj); + break; + case MSG_UNPLUG_DISPLAY: + onUnplugDisplay((IRemoteControlDisplay)msg.obj); + break; + default: + Log.e(TAG, "Unknown event " + msg.what + " in RemoteControlClient handler"); + } + } + } + + private void sendPlaybackState_syncCacheLock() { + if ((mCurrentClientGenId == mInternalClientGenId) && (mRcDisplay != null)) { + try { + mRcDisplay.setPlaybackState(mInternalClientGenId, mPlaybackState); + } catch (RemoteException e) { + Log.e(TAG, "Error in setPlaybackState(), dead display "+e); + mRcDisplay = null; + mArtworkExpectedWidth = -1; + mArtworkExpectedHeight = -1; + } + } + } + + private void sendMetadata_syncCacheLock() { + if ((mCurrentClientGenId == mInternalClientGenId) && (mRcDisplay != null)) { + try { + mRcDisplay.setMetadata(mInternalClientGenId, mMetadata); + } catch (RemoteException e) { + Log.e(TAG, "Error in sendPlaybackState(), dead display "+e); + mRcDisplay = null; + mArtworkExpectedWidth = -1; + mArtworkExpectedHeight = -1; + } + } + } + + private void sendTransportControlFlags_syncCacheLock() { + if ((mCurrentClientGenId == mInternalClientGenId) && (mRcDisplay != null)) { + try { + mRcDisplay.setTransportControlFlags(mInternalClientGenId, + mTransportControlFlags); + } catch (RemoteException e) { + Log.e(TAG, "Error in sendTransportControlFlags(), dead display "+e); + mRcDisplay = null; + mArtworkExpectedWidth = -1; + mArtworkExpectedHeight = -1; + } + } + } + + private void sendArtwork_syncCacheLock() { + if ((mCurrentClientGenId == mInternalClientGenId) && (mRcDisplay != null)) { + // even though we have already scaled in setArtwork(), when this client needs to + // send the bitmap, there might be newer and smaller expected dimensions, so we have + // to check again. + mArtwork = scaleBitmapIfTooBig(mArtwork, mArtworkExpectedWidth, mArtworkExpectedHeight); + try { + mRcDisplay.setArtwork(mInternalClientGenId, mArtwork); + } catch (RemoteException e) { + Log.e(TAG, "Error in sendArtwork(), dead display "+e); + mRcDisplay = null; + mArtworkExpectedWidth = -1; + mArtworkExpectedHeight = -1; + } + } + } + + private void onNewInternalClientGen(Integer clientGeneration, int artWidth, int artHeight) { + synchronized (mCacheLock) { + // this remote control client is told it is the "focused" one: + // it implies that now (mCurrentClientGenId == mInternalClientGenId) is true + mInternalClientGenId = clientGeneration.intValue(); + if (artWidth > 0) { + mArtworkExpectedWidth = artWidth; + mArtworkExpectedHeight = artHeight; + } + } + } + + private void onNewCurrentClientGen(int clientGeneration) { + synchronized (mCacheLock) { + mCurrentClientGenId = clientGeneration; + } + } + + private void onPlugDisplay(IRemoteControlDisplay rcd) { + synchronized(mCacheLock) { + mRcDisplay = rcd; + } + } + + private void onUnplugDisplay(IRemoteControlDisplay rcd) { + synchronized(mCacheLock) { + if ((mRcDisplay != null) && (mRcDisplay.equals(rcd))) { + mRcDisplay = null; + mArtworkExpectedWidth = ARTWORK_DEFAULT_SIZE; + mArtworkExpectedHeight = ARTWORK_DEFAULT_SIZE; + } + } + } + + /** + * Scale a bitmap to fit the smallest dimension by uniformly scaling the incoming bitmap. + * If the bitmap fits, then do nothing and return the original. + * + * @param bitmap + * @param maxWidth + * @param maxHeight + * @return + */ + + private Bitmap scaleBitmapIfTooBig(Bitmap bitmap, int maxWidth, int maxHeight) { + final int width = bitmap.getWidth(); + final int height = bitmap.getHeight(); + if (width > maxWidth || height > maxHeight) { + float scale = Math.min((float) maxWidth / width, (float) maxHeight / height); + int newWidth = Math.round(scale * width); + int newHeight = Math.round(scale * height); + Bitmap outBitmap = Bitmap.createBitmap(newWidth, newHeight, bitmap.getConfig()); + Canvas canvas = new Canvas(outBitmap); + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setFilterBitmap(true); + canvas.drawBitmap(bitmap, null, + new RectF(0, 0, outBitmap.getWidth(), outBitmap.getHeight()), paint); + bitmap = outBitmap; + } + return bitmap; + + } } diff --git a/media/java/android/media/videoeditor/MediaImageItem.java b/media/java/android/media/videoeditor/MediaImageItem.java index 4ca6fad2a69b..b2a279a47287 100755 --- a/media/java/android/media/videoeditor/MediaImageItem.java +++ b/media/java/android/media/videoeditor/MediaImageItem.java @@ -626,7 +626,7 @@ public class MediaImageItem extends MediaItem { if (getGeneratedImageClip() == null) { final Bitmap thumbnail = scaleImage(mFilename, width, height); for (int i = 0; i < indices.length; i++) { - callback.onThumbnail(thumbnail, i); + callback.onThumbnail(thumbnail, indices[i]); } } else { if (startMs > endMs) { diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.cpp b/media/libstagefright/codecs/aacdec/SoftAAC.cpp index f0a330f3e156..2abdb560f30f 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC.cpp +++ b/media/libstagefright/codecs/aacdec/SoftAAC.cpp @@ -378,23 +378,35 @@ void SoftAAC::onQueueFilled(OMX_U32 portIndex) { // fall through } - if (mUpsamplingFactor == 2) { - if (mConfig->desiredChannels == 1) { - memcpy(&mConfig->pOutputBuffer[1024], - &mConfig->pOutputBuffer[2048], - numOutBytes * 2); + if (decoderErr == MP4AUDEC_SUCCESS || mNumSamplesOutput > 0) { + // We'll only output data if we successfully decoded it or + // we've previously decoded valid data, in the latter case + // (decode failed) we'll output a silent frame. + + if (mUpsamplingFactor == 2) { + if (mConfig->desiredChannels == 1) { + memcpy(&mConfig->pOutputBuffer[1024], + &mConfig->pOutputBuffer[2048], + numOutBytes * 2); + } + numOutBytes *= 2; } - numOutBytes *= 2; - } - outHeader->nFilledLen = numOutBytes; - outHeader->nFlags = 0; + outHeader->nFilledLen = numOutBytes; + outHeader->nFlags = 0; - outHeader->nTimeStamp = - mAnchorTimeUs - + (mNumSamplesOutput * 1000000ll) / mConfig->samplingRate; + outHeader->nTimeStamp = + mAnchorTimeUs + + (mNumSamplesOutput * 1000000ll) / mConfig->samplingRate; - mNumSamplesOutput += mConfig->frameLength * mUpsamplingFactor; + mNumSamplesOutput += mConfig->frameLength * mUpsamplingFactor; + + outInfo->mOwnedByUs = false; + outQueue.erase(outQueue.begin()); + outInfo = NULL; + notifyFillBufferDone(outHeader); + outHeader = NULL; + } if (inHeader->nFilledLen == 0) { inInfo->mOwnedByUs = false; @@ -404,12 +416,6 @@ void SoftAAC::onQueueFilled(OMX_U32 portIndex) { inHeader = NULL; } - outInfo->mOwnedByUs = false; - outQueue.erase(outQueue.begin()); - outInfo = NULL; - notifyFillBufferDone(outHeader); - outHeader = NULL; - if (decoderErr == MP4AUDEC_SUCCESS) { ++mInputBufferCount; } diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp index e1b9991983a5..3ef7b71e248d 100644 --- a/media/libstagefright/matroska/MatroskaExtractor.cpp +++ b/media/libstagefright/matroska/MatroskaExtractor.cpp @@ -493,7 +493,8 @@ MatroskaExtractor::MatroskaExtractor(const sp<DataSource> &source) : mDataSource(source), mReader(new DataSourceReader(mDataSource)), mSegment(NULL), - mExtractedThumbnails(false) { + mExtractedThumbnails(false), + mIsWebm(false) { off64_t size; mIsLiveStreaming = (mDataSource->flags() @@ -507,6 +508,10 @@ MatroskaExtractor::MatroskaExtractor(const sp<DataSource> &source) return; } + if (ebmlHeader.m_docType && !strcmp("webm", ebmlHeader.m_docType)) { + mIsWebm = true; + } + long long ret = mkvparser::Segment::CreateInstance(mReader, pos, mSegment); @@ -757,7 +762,10 @@ void MatroskaExtractor::findThumbnails() { sp<MetaData> MatroskaExtractor::getMetaData() { sp<MetaData> meta = new MetaData; - meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MATROSKA); + + meta->setCString( + kKeyMIMEType, + mIsWebm ? "video/webm" : MEDIA_MIMETYPE_CONTAINER_MATROSKA); return meta; } diff --git a/media/libstagefright/matroska/MatroskaExtractor.h b/media/libstagefright/matroska/MatroskaExtractor.h index 38ebd6121ba9..1294b4fe415a 100644 --- a/media/libstagefright/matroska/MatroskaExtractor.h +++ b/media/libstagefright/matroska/MatroskaExtractor.h @@ -68,6 +68,7 @@ private: mkvparser::Segment *mSegment; bool mExtractedThumbnails; bool mIsLiveStreaming; + bool mIsWebm; void addTracks(); void findThumbnails(); diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java index 36f16596b653..8da2db641867 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java @@ -120,6 +120,9 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView } public void onBeginDrag(View v) { + // We do this so the underlying ScrollView knows that it won't get + // the chance to intercept events anymore + requestDisallowInterceptTouchEvent(true); } public View getChildAtPosition(MotionEvent ev) { diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java index 959328f7d4ee..b1a30d9e0407 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java @@ -135,6 +135,9 @@ public class RecentsVerticalScrollView extends ScrollView implements SwipeHelper } public void onBeginDrag(View v) { + // We do this so the underlying ScrollView knows that it won't get + // the chance to intercept events anymore + requestDisallowInterceptTouchEvent(true); } public View getChildAtPosition(MotionEvent ev) { diff --git a/policy/src/com/android/internal/policy/impl/AccountUnlockScreen.java b/policy/src/com/android/internal/policy/impl/AccountUnlockScreen.java index 044cf4ae28a5..6ff9a608ad44 100644 --- a/policy/src/com/android/internal/policy/impl/AccountUnlockScreen.java +++ b/policy/src/com/android/internal/policy/impl/AccountUnlockScreen.java @@ -311,12 +311,6 @@ public class AccountUnlockScreen extends RelativeLayout implements KeyguardScree mCheckingDialog.setCancelable(false); mCheckingDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - if (!mContext.getResources().getBoolean( - com.android.internal.R.bool.config_sf_slowBlur)) { - mCheckingDialog.getWindow().setFlags( - WindowManager.LayoutParams.FLAG_BLUR_BEHIND, - WindowManager.LayoutParams.FLAG_BLUR_BEHIND); - } } return mCheckingDialog; } diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java index c47383a9a241..ec3102867f67 100644 --- a/policy/src/com/android/internal/policy/impl/GlobalActions.java +++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java @@ -226,11 +226,6 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac final AlertDialog dialog = ab.create(); dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); - if (!mContext.getResources().getBoolean( - com.android.internal.R.bool.config_sf_slowBlur)) { - dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND, - WindowManager.LayoutParams.FLAG_BLUR_BEHIND); - } dialog.setOnDismissListener(this); diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java index 86de5588593b..431f8e0bf115 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java @@ -292,7 +292,8 @@ public class KeyguardViewMediator implements KeyguardViewCallback, mKeyguardViewProperties, mUpdateMonitor); mUserPresentIntent = new Intent(Intent.ACTION_USER_PRESENT); - mUserPresentIntent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + mUserPresentIntent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING + | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); final ContentResolver cr = mContext.getContentResolver(); mShowLockIcon = (Settings.System.getInt(cr, "show_status_bar_lock", 0) == 1); diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java index b60bae7d49eb..2d90727a0e02 100644 --- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java +++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java @@ -42,6 +42,7 @@ import android.os.SystemProperties; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; +import android.util.Slog; import android.view.KeyEvent; import android.view.View; import android.view.WindowManager; @@ -294,22 +295,47 @@ public class LockPatternKeyguardView extends KeyguardViewBase { public void reportFailedUnlockAttempt() { mUpdateMonitor.reportFailedAttempt(); final int failedAttempts = mUpdateMonitor.getFailedAttempts(); - if (DEBUG) Log.d(TAG, - "reportFailedPatternAttempt: #" + failedAttempts + + if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts + " (enableFallback=" + mEnableFallback + ")"); - final boolean usingLockPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality() + + final boolean usingPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality() == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; - if (usingLockPattern && mEnableFallback && failedAttempts == - (LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET - - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) { - showAlmostAtAccountLoginDialog(); - } else if (usingLockPattern && mEnableFallback - && failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) { - mLockPatternUtils.setPermanentlyLocked(true); - updateScreen(mMode); - } else if ((failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) - == 0) { - showTimeoutDialog(); + + final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager() + .getMaximumFailedPasswordsForWipe(null); + + final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET + - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; + + final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ? + (failedAttemptsBeforeWipe - failedAttempts) + : Integer.MAX_VALUE; // because DPM returns 0 if no restriction + + if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) { + // If we reach this code, it means the user has installed a DevicePolicyManager + // that requests device wipe after N attempts. Once we get below the grace + // period, we'll post this dialog every time as a clear warning until the + // bombshell hits and the device is wiped. + if (remainingBeforeWipe > 0) { + showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe); + } else { + // Too many attempts. The device will be wiped shortly. + Slog.i(TAG, "Too many unlock attempts; device will be wiped!"); + showWipeDialog(failedAttempts); + } + } else if (usingPattern && mEnableFallback) { + if (failedAttempts == failedAttemptWarning) { + showAlmostAtAccountLoginDialog(); + } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) { + mLockPatternUtils.setPermanentlyLocked(true); + updateScreen(mMode); + } + } else { + final boolean showTimeout = + (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0; + if (showTimeout) { + showTimeoutDialog(); + } } mLockPatternUtils.reportFailedPasswordAttempt(); } @@ -727,10 +753,20 @@ public class LockPatternKeyguardView extends KeyguardViewBase { return currentMode; } + private void showDialog(String title, String message) { + final AlertDialog dialog = new AlertDialog.Builder(mContext) + .setTitle(title) + .setMessage(message) + .setNeutralButton(R.string.ok, null) + .create(); + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + dialog.show(); + } + private void showTimeoutDialog() { int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; int messageId = R.string.lockscreen_too_many_failed_attempts_dialog_message; - if(getUnlockMode() == UnlockMode.Password) { + if (getUnlockMode() == UnlockMode.Password) { if(mLockPatternUtils.getKeyguardStoredPasswordQuality() == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) { messageId = R.string.lockscreen_too_many_failed_pin_attempts_dialog_message; @@ -738,46 +774,32 @@ public class LockPatternKeyguardView extends KeyguardViewBase { messageId = R.string.lockscreen_too_many_failed_password_attempts_dialog_message; } } - String message = mContext.getString( - messageId, - mUpdateMonitor.getFailedAttempts(), + String message = mContext.getString(messageId, mUpdateMonitor.getFailedAttempts(), timeoutInSeconds); - final AlertDialog dialog = new AlertDialog.Builder(mContext) - .setTitle(null) - .setMessage(message) - .setNeutralButton(R.string.ok, null) - .create(); - dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - if (!mContext.getResources().getBoolean( - com.android.internal.R.bool.config_sf_slowBlur)) { - dialog.getWindow().setFlags( - WindowManager.LayoutParams.FLAG_BLUR_BEHIND, - WindowManager.LayoutParams.FLAG_BLUR_BEHIND); - } - dialog.show(); + + showDialog(null, message); } private void showAlmostAtAccountLoginDialog() { + final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; + final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET + - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; + String message = mContext.getString(R.string.lockscreen_failed_attempts_almost_glogin, + count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds); + showDialog(null, message); + } + + private void showAlmostAtWipeDialog(int attempts, int remaining) { int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; String message = mContext.getString( - R.string.lockscreen_failed_attempts_almost_glogin, - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET - - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, - timeoutInSeconds); - final AlertDialog dialog = new AlertDialog.Builder(mContext) - .setTitle(null) - .setMessage(message) - .setNeutralButton(R.string.ok, null) - .create(); - dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - if (!mContext.getResources().getBoolean( - com.android.internal.R.bool.config_sf_slowBlur)) { - dialog.getWindow().setFlags( - WindowManager.LayoutParams.FLAG_BLUR_BEHIND, - WindowManager.LayoutParams.FLAG_BLUR_BEHIND); - } - dialog.show(); + R.string.lockscreen_failed_attempts_almost_at_wipe, attempts, remaining); + showDialog(null, message); + } + + private void showWipeDialog(int attempts) { + String message = mContext.getString( + R.string.lockscreen_failed_attempts_now_wiping, attempts); + showDialog(null, message); } /** diff --git a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java index d70b3bb2ba90..ee0a6e9e3b20 100644 --- a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java +++ b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java @@ -41,7 +41,6 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; -import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index c929bbc92b32..431a6bb6b9e8 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -2540,8 +2540,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) { params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND; } - params.dimAmount = a.getFloat( - android.R.styleable.Window_backgroundDimAmount, 0.5f); + if (!haveDimAmount()) { + params.dimAmount = a.getFloat( + android.R.styleable.Window_backgroundDimAmount, 0.5f); + } } if (params.windowAnimations == 0) { diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index ebadb5e0758d..1d5fbc0acec7 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -20,6 +20,7 @@ import android.app.Activity; import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.app.IUiModeManager; +import android.app.ProgressDialog; import android.app.UiModeManager; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; @@ -3078,7 +3079,44 @@ public class PhoneWindowManager implements WindowManagerPolicy { }); } } - + + ProgressDialog mBootMsgDialog = null; + + /** {@inheritDoc} */ + public void showBootMessage(final CharSequence msg, final boolean always) { + mHandler.post(new Runnable() { + @Override public void run() { + if (mBootMsgDialog == null) { + mBootMsgDialog = new ProgressDialog(mContext); + mBootMsgDialog.setTitle(R.string.android_upgrading_title); + mBootMsgDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); + mBootMsgDialog.setIndeterminate(true); + mBootMsgDialog.getWindow().setType( + WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY); + mBootMsgDialog.getWindow().addFlags( + WindowManager.LayoutParams.FLAG_DIM_BEHIND + | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN); + mBootMsgDialog.getWindow().setDimAmount(1); + mBootMsgDialog.setCancelable(false); + mBootMsgDialog.show(); + } + mBootMsgDialog.setMessage(msg); + } + }); + } + + /** {@inheritDoc} */ + public void hideBootMessages() { + mHandler.post(new Runnable() { + @Override public void run() { + if (mBootMsgDialog != null) { + mBootMsgDialog.dismiss(); + mBootMsgDialog = null; + } + } + }); + } + /** {@inheritDoc} */ public void userActivity() { synchronized (mScreenLockTimeout) { diff --git a/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java b/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java index f968beea9168..520d3021476d 100644 --- a/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java +++ b/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java @@ -226,12 +226,6 @@ public class SimPukUnlockScreen extends LinearLayout implements KeyguardScreen, mSimUnlockProgressDialog.setCancelable(false); mSimUnlockProgressDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - if (!mContext.getResources().getBoolean( - com.android.internal.R.bool.config_sf_slowBlur)) { - mSimUnlockProgressDialog.getWindow().setFlags( - WindowManager.LayoutParams.FLAG_BLUR_BEHIND, - WindowManager.LayoutParams.FLAG_BLUR_BEHIND); - } } return mSimUnlockProgressDialog; } diff --git a/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java b/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java index 8bac969b2316..1acf68172241 100644 --- a/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java +++ b/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java @@ -196,12 +196,6 @@ public class SimUnlockScreen extends LinearLayout implements KeyguardScreen, Vie mSimUnlockProgressDialog.setCancelable(false); mSimUnlockProgressDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - if (!mContext.getResources().getBoolean( - com.android.internal.R.bool.config_sf_slowBlur)) { - mSimUnlockProgressDialog.getWindow().setFlags( - WindowManager.LayoutParams.FLAG_BLUR_BEHIND, - WindowManager.LayoutParams.FLAG_BLUR_BEHIND); - } } return mSimUnlockProgressDialog; } diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 941c9c808757..744fa50879e0 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -522,6 +522,11 @@ uint32_t AudioFlinger::latency(int output) const status_t AudioFlinger::setMasterVolume(float value) { + status_t ret = initCheck(); + if (ret != NO_ERROR) { + return ret; + } + // check calling permissions if (!settingsAllowed()) { return PERMISSION_DENIED; @@ -547,7 +552,10 @@ status_t AudioFlinger::setMasterVolume(float value) status_t AudioFlinger::setMode(int mode) { - status_t ret; + status_t ret = initCheck(); + if (ret != NO_ERROR) { + return ret; + } // check calling permissions if (!settingsAllowed()) { @@ -577,6 +585,11 @@ status_t AudioFlinger::setMode(int mode) status_t AudioFlinger::setMicMute(bool state) { + status_t ret = initCheck(); + if (ret != NO_ERROR) { + return ret; + } + // check calling permissions if (!settingsAllowed()) { return PERMISSION_DENIED; @@ -584,13 +597,18 @@ status_t AudioFlinger::setMicMute(bool state) AutoMutex lock(mHardwareLock); mHardwareStatus = AUDIO_HW_SET_MIC_MUTE; - status_t ret = mPrimaryHardwareDev->set_mic_mute(mPrimaryHardwareDev, state); + ret = mPrimaryHardwareDev->set_mic_mute(mPrimaryHardwareDev, state); mHardwareStatus = AUDIO_HW_IDLE; return ret; } bool AudioFlinger::getMicMute() const { + status_t ret = initCheck(); + if (ret != NO_ERROR) { + return false; + } + bool state = AUDIO_MODE_INVALID; mHardwareStatus = AUDIO_HW_GET_MIC_MUTE; mPrimaryHardwareDev->get_mic_mute(mPrimaryHardwareDev, &state); @@ -814,6 +832,11 @@ String8 AudioFlinger::getParameters(int ioHandle, const String8& keys) size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) { + status_t ret = initCheck(); + if (ret != NO_ERROR) { + return 0; + } + return mPrimaryHardwareDev->get_input_buffer_size(mPrimaryHardwareDev, sampleRate, format, channelCount); } @@ -834,6 +857,11 @@ unsigned int AudioFlinger::getInputFramesLost(int ioHandle) status_t AudioFlinger::setVoiceVolume(float value) { + status_t ret = initCheck(); + if (ret != NO_ERROR) { + return ret; + } + // check calling permissions if (!settingsAllowed()) { return PERMISSION_DENIED; @@ -841,7 +869,7 @@ status_t AudioFlinger::setVoiceVolume(float value) AutoMutex lock(mHardwareLock); mHardwareStatus = AUDIO_SET_VOICE_VOLUME; - status_t ret = mPrimaryHardwareDev->set_voice_volume(mPrimaryHardwareDev, value); + ret = mPrimaryHardwareDev->set_voice_volume(mPrimaryHardwareDev, value); mHardwareStatus = AUDIO_HW_IDLE; return ret; diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index d54930870564..f1b8bae47fcb 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -1556,13 +1556,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return false; } } - - LockPatternUtils utils = new LockPatternUtils(mContext); - if(utils.checkPasswordHistory(password)) { - Slog.w(TAG, "resetPassword: password is the same as one of the last " - + getPasswordHistoryLength(null) + " passwords"); - return false; - } } int callingUid = Binder.getCallingUid(); diff --git a/services/java/com/android/server/DropBoxManagerService.java b/services/java/com/android/server/DropBoxManagerService.java index 5c878c946231..d37c9ab14b77 100644 --- a/services/java/com/android/server/DropBoxManagerService.java +++ b/services/java/com/android/server/DropBoxManagerService.java @@ -28,7 +28,6 @@ import android.os.Debug; import android.os.DropBoxManager; import android.os.FileUtils; import android.os.Handler; -import android.os.ParcelFileDescriptor; import android.os.StatFs; import android.os.SystemClock; import android.provider.Settings; @@ -45,14 +44,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; -import java.io.OutputStreamWriter; import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; import java.util.ArrayList; -import java.util.Comparator; import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; import java.util.zip.GZIPOutputStream; diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 73d790a073c0..6b64dd0fc0ee 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -1,5 +1,4 @@ /* - * Copyright (C) 2006-2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -48,7 +47,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; @@ -57,7 +55,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.database.ContentObserver; -import android.graphics.BitmapFactory; import android.inputmethodservice.InputMethodService; import android.os.Binder; import android.os.Environment; @@ -98,12 +95,10 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.TreeMap; /** @@ -147,7 +142,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final Handler mHandler; final InputMethodSettings mSettings; final SettingsObserver mSettingsObserver; - final StatusBarManagerService mStatusBar; final IWindowManager mIWindowManager; final HandlerCaller mCaller; private final InputMethodFileManager mFileManager; @@ -162,10 +156,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub new LruCache<SuggestionSpan, InputMethodInfo>(SECURE_SUGGESTION_SPANS_MAX_SIZE); // Ongoing notification - private final NotificationManager mNotificationManager; - private final KeyguardManager mKeyguardManager; - private final Notification mImeSwitcherNotification; - private final PendingIntent mImeSwitchPendingIntent; + private NotificationManager mNotificationManager; + private KeyguardManager mKeyguardManager; + private StatusBarManagerService mStatusBar; + private Notification mImeSwitcherNotification; + private PendingIntent mImeSwitchPendingIntent; private boolean mShowOngoingImeSwitcherForPhones; private boolean mNotificationShown; @@ -469,8 +464,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // Pick another one... Slog.i(TAG, "Current input method removed: " + curInputMethodId); mImeWindowVis = 0; - mStatusBar.setImeWindowStatus(mCurToken, mImeWindowVis, - mBackDisposition); + updateImeWindowStatusLocked(); if (!chooseNewDefaultIMELocked()) { changed = true; curIm = null; @@ -511,7 +505,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - public InputMethodManagerService(Context context, StatusBarManagerService statusBar) { + public InputMethodManagerService(Context context) { mContext = context; mRes = context.getResources(); mHandler = new Handler(this); @@ -524,10 +518,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } }); - mKeyguardManager = (KeyguardManager) - mContext.getSystemService(Context.KEYGUARD_SERVICE); - mNotificationManager = (NotificationManager) - mContext.getSystemService(Context.NOTIFICATION_SERVICE); mImeSwitcherNotification = new Notification(); mImeSwitcherNotification.icon = com.android.internal.R.drawable.ic_notification_ime_default; mImeSwitcherNotification.when = 0; @@ -553,8 +543,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub screenOnOffFilt.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); mContext.registerReceiver(new ScreenOnOffReceiver(), screenOnOffFilt); - mStatusBar = statusBar; - statusBar.setIconVisibility("ime", false); mNotificationShown = false; // mSettings should be created before buildInputMethodListLocked @@ -608,10 +596,17 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - public void systemReady() { + public void systemReady(StatusBarManagerService statusBar) { synchronized (mMethodMap) { if (!mSystemReady) { mSystemReady = true; + mKeyguardManager = (KeyguardManager) + mContext.getSystemService(Context.KEYGUARD_SERVICE); + mNotificationManager = (NotificationManager) + mContext.getSystemService(Context.NOTIFICATION_SERVICE); + mStatusBar = statusBar; + statusBar.setIconVisibility("ime", false); + updateImeWindowStatusLocked(); mShowOngoingImeSwitcherForPhones = mRes.getBoolean( com.android.internal.R.bool.show_ongoing_ime_switcher); try { @@ -623,6 +618,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } + void updateImeWindowStatusLocked() { + if (mStatusBar != null) { + mStatusBar.setImeWindowStatus(mCurToken, mImeWindowVis, + mBackDisposition); + } + } + @Override public List<InputMethodInfo> getInputMethodList() { synchronized (mMethodMap) { @@ -1009,7 +1011,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mEnabledSession = null; mCurMethod = null; } - mStatusBar.setIconVisibility("ime", false); + if (mStatusBar != null) { + mStatusBar.setIconVisibility("ime", false); + } } @Override @@ -1046,7 +1050,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub synchronized (mMethodMap) { if (iconId == 0) { if (DEBUG) Slog.d(TAG, "hide the small icon for the input method"); - mStatusBar.setIconVisibility("ime", false); + if (mStatusBar != null) { + mStatusBar.setIconVisibility("ime", false); + } } else if (packageName != null) { if (DEBUG) Slog.d(TAG, "show a small icon for the input method"); CharSequence contentDescription = null; @@ -1057,9 +1063,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } catch (NameNotFoundException nnfe) { /* ignore */ } - mStatusBar.setIcon("ime", packageName, iconId, 0, - contentDescription != null ? contentDescription.toString() : null); - mStatusBar.setIconVisibility("ime", true); + if (mStatusBar != null) { + mStatusBar.setIcon("ime", packageName, iconId, 0, + contentDescription != null + ? contentDescription.toString() : null); + mStatusBar.setIconVisibility("ime", true); + } } } } finally { @@ -1125,7 +1134,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub synchronized (mMethodMap) { mImeWindowVis = vis; mBackDisposition = backDisposition; - mStatusBar.setImeWindowStatus(token, vis, backDisposition); + if (mStatusBar != null) { + mStatusBar.setImeWindowStatus(token, vis, backDisposition); + } final boolean iconVisibility = (vis & InputMethodService.IME_ACTIVE) != 0; final InputMethodInfo imi = mMethodMap.get(mCurMethodId); if (imi != null && iconVisibility && needsToShowImeSwitchOngoingNotification()) { @@ -1142,12 +1153,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mImeSwitcherNotification.setLatestEventInfo( mContext, title, summary, mImeSwitchPendingIntent); - mNotificationManager.notify( - com.android.internal.R.string.select_input_method, - mImeSwitcherNotification); - mNotificationShown = true; + if (mNotificationManager != null) { + mNotificationManager.notify( + com.android.internal.R.string.select_input_method, + mImeSwitcherNotification); + mNotificationShown = true; + } } else { - if (mNotificationShown) { + if (mNotificationShown && mNotificationManager != null) { mNotificationManager.cancel( com.android.internal.R.string.select_input_method); mNotificationShown = false; @@ -1252,8 +1265,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mImeWindowVis = (mInputShown || hardKeyShown) ? ( InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE) : 0; - mStatusBar.setImeWindowStatus(mCurToken, mImeWindowVis, - mBackDisposition); + updateImeWindowStatusLocked(); // If subtype is null, try to find the most applicable one from // getCurrentInputMethodSubtype. if (subtype == null) { @@ -1374,13 +1386,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (DEBUG) Slog.w(TAG, "Ignoring hideSoftInput of uid " + uid + ": " + client); mImeWindowVis = 0; - mStatusBar.setImeWindowStatus(mCurToken, mImeWindowVis, - mBackDisposition); + updateImeWindowStatusLocked(); return false; } } catch (RemoteException e) { mImeWindowVis = 0; - mStatusBar.setImeWindowStatus(mCurToken, mImeWindowVis, mBackDisposition); + updateImeWindowStatusLocked(); return false; } } @@ -2151,7 +2162,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } }); - if (showSubtypes && !(mKeyguardManager.isKeyguardLocked() + if (showSubtypes && mKeyguardManager != null && !(mKeyguardManager.isKeyguardLocked() && mKeyguardManager.isKeyguardSecure())) { mDialogBuilder.setPositiveButton( com.android.internal.R.string.configure_input_methods, diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 5dd3c5b117ba..e6f92a5baad2 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -40,6 +40,7 @@ import android.server.BluetoothService; import android.server.search.SearchManagerService; import android.util.DisplayMetrics; import android.util.EventLog; +import android.util.Log; import android.util.Slog; import android.view.WindowManager; @@ -66,6 +67,11 @@ class ServerThread extends Thread { ContentResolver mContentResolver; + void reportWtf(String msg, Throwable e) { + Slog.w(TAG, "***********************************************"); + Log.wtf(TAG, "BOOT FAILURE " + msg, e); + } + @Override public void run() { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, @@ -116,7 +122,6 @@ class ServerThread extends Thread { WindowManagerService wm = null; BluetoothService bluetooth = null; BluetoothA2dpService bluetoothA2dp = null; - WiredAccessoryObserver wiredAccessory = null; DockObserver dock = null; UsbService usb = null; UiModeManagerService uiMode = null; @@ -222,7 +227,8 @@ class ServerThread extends Thread { } } catch (RuntimeException e) { - Slog.e("System", "Failure starting core service", e); + Slog.e("System", "******************************************"); + Slog.e("System", "************ Failure starting core service", e); } DevicePolicyManagerService devicePolicy = null; @@ -235,13 +241,52 @@ class ServerThread extends Thread { CountryDetectorService countryDetector = null; TextServicesManagerService tsms = null; + // Bring up services needed for UI. + if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { + try { + Slog.i(TAG, "Input Method Service"); + imm = new InputMethodManagerService(context); + ServiceManager.addService(Context.INPUT_METHOD_SERVICE, imm); + } catch (Throwable e) { + reportWtf("starting Input Manager Service", e); + } + + try { + Slog.i(TAG, "Accessibility Manager"); + ServiceManager.addService(Context.ACCESSIBILITY_SERVICE, + new AccessibilityManagerService(context)); + } catch (Throwable e) { + reportWtf("starting Accessibility Manager", e); + } + } + + try { + wm.displayReady(); + } catch (Throwable e) { + reportWtf("making display ready", e); + } + + try { + pm.performBootDexOpt(); + } catch (Throwable e) { + reportWtf("performing boot dexopt", e); + } + + try { + ActivityManagerNative.getDefault().showBootMessage( + context.getResources().getText( + com.android.internal.R.string.android_upgrading_starting_apps), + false); + } catch (RemoteException e) { + } + if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { try { Slog.i(TAG, "Device Policy"); devicePolicy = new DevicePolicyManagerService(context); ServiceManager.addService(Context.DEVICE_POLICY_SERVICE, devicePolicy); } catch (Throwable e) { - Slog.e(TAG, "Failure starting DevicePolicyService", e); + reportWtf("starting DevicePolicyService", e); } try { @@ -249,7 +294,7 @@ class ServerThread extends Thread { statusBar = new StatusBarManagerService(context, wm); ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar); } catch (Throwable e) { - Slog.e(TAG, "Failure starting StatusBarManagerService", e); + reportWtf("starting StatusBarManagerService", e); } try { @@ -257,15 +302,7 @@ class ServerThread extends Thread { ServiceManager.addService(Context.CLIPBOARD_SERVICE, new ClipboardService(context)); } catch (Throwable e) { - Slog.e(TAG, "Failure starting Clipboard Service", e); - } - - try { - Slog.i(TAG, "Input Method Service"); - imm = new InputMethodManagerService(context, statusBar); - ServiceManager.addService(Context.INPUT_METHOD_SERVICE, imm); - } catch (Throwable e) { - Slog.e(TAG, "Failure starting Input Manager Service", e); + reportWtf("starting Clipboard Service", e); } try { @@ -273,7 +310,7 @@ class ServerThread extends Thread { networkManagement = NetworkManagementService.create(context); ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement); } catch (Throwable e) { - Slog.e(TAG, "Failure starting NetworkManagement Service", e); + reportWtf("starting NetworkManagement Service", e); } try { @@ -281,7 +318,7 @@ class ServerThread extends Thread { tsms = new TextServicesManagerService(context); ServiceManager.addService(Context.TEXT_SERVICES_MANAGER_SERVICE, tsms); } catch (Throwable e) { - Slog.e(TAG, "Failure starting Text Service Manager Service", e); + reportWtf("starting Text Service Manager Service", e); } try { @@ -289,7 +326,7 @@ class ServerThread extends Thread { networkStats = new NetworkStatsService(context, networkManagement, alarm); ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats); } catch (Throwable e) { - Slog.e(TAG, "Failure starting NetworkStats Service", e); + reportWtf("starting NetworkStats Service", e); } try { @@ -299,7 +336,7 @@ class ServerThread extends Thread { networkStats, networkManagement); ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy); } catch (Throwable e) { - Slog.e(TAG, "Failure starting NetworkPolicy Service", e); + reportWtf("starting NetworkPolicy Service", e); } try { @@ -307,7 +344,7 @@ class ServerThread extends Thread { wifiP2p = new WifiP2pService(context); ServiceManager.addService(Context.WIFI_P2P_SERVICE, wifiP2p); } catch (Throwable e) { - Slog.e(TAG, "Failure starting Wi-Fi P2pService", e); + reportWtf("starting Wi-Fi P2pService", e); } try { @@ -316,7 +353,7 @@ class ServerThread extends Thread { ServiceManager.addService(Context.WIFI_SERVICE, wifi); wifi.checkAndStartWifi(); } catch (Throwable e) { - Slog.e(TAG, "Failure starting Wi-Fi Service", e); + reportWtf("starting Wi-Fi Service", e); } try { @@ -327,7 +364,7 @@ class ServerThread extends Thread { networkPolicy.bindConnectivityManager(connectivity); wifiP2p.connectivityServiceReady(); } catch (Throwable e) { - Slog.e(TAG, "Failure starting Connectivity Service", e); + reportWtf("starting Connectivity Service", e); } try { @@ -336,15 +373,7 @@ class ServerThread extends Thread { ServiceManager.addService( Context.THROTTLE_SERVICE, throttle); } catch (Throwable e) { - Slog.e(TAG, "Failure starting ThrottleService", e); - } - - try { - Slog.i(TAG, "Accessibility Manager"); - ServiceManager.addService(Context.ACCESSIBILITY_SERVICE, - new AccessibilityManagerService(context)); - } catch (Throwable e) { - Slog.e(TAG, "Failure starting Accessibility Manager", e); + reportWtf("starting ThrottleService", e); } try { @@ -355,7 +384,7 @@ class ServerThread extends Thread { Slog.i(TAG, "Mount Service"); ServiceManager.addService("mount", new MountService(context)); } catch (Throwable e) { - Slog.e(TAG, "Failure starting Mount Service", e); + reportWtf("starting Mount Service", e); } try { @@ -364,7 +393,7 @@ class ServerThread extends Thread { ServiceManager.addService(Context.NOTIFICATION_SERVICE, notification); networkPolicy.bindNotificationManager(notification); } catch (Throwable e) { - Slog.e(TAG, "Failure starting Notification Manager", e); + reportWtf("starting Notification Manager", e); } try { @@ -372,7 +401,7 @@ class ServerThread extends Thread { ServiceManager.addService(DeviceStorageMonitorService.SERVICE, new DeviceStorageMonitorService(context)); } catch (Throwable e) { - Slog.e(TAG, "Failure starting DeviceStorageMonitor service", e); + reportWtf("starting DeviceStorageMonitor service", e); } try { @@ -380,7 +409,7 @@ class ServerThread extends Thread { location = new LocationManagerService(context); ServiceManager.addService(Context.LOCATION_SERVICE, location); } catch (Throwable e) { - Slog.e(TAG, "Failure starting Location Manager", e); + reportWtf("starting Location Manager", e); } try { @@ -388,7 +417,7 @@ class ServerThread extends Thread { countryDetector = new CountryDetectorService(context); ServiceManager.addService(Context.COUNTRY_DETECTOR, countryDetector); } catch (Throwable e) { - Slog.e(TAG, "Failure starting Country Detector", e); + reportWtf("starting Country Detector", e); } try { @@ -396,7 +425,7 @@ class ServerThread extends Thread { ServiceManager.addService(Context.SEARCH_SERVICE, new SearchManagerService(context)); } catch (Throwable e) { - Slog.e(TAG, "Failure starting Search Service", e); + reportWtf("starting Search Service", e); } try { @@ -404,7 +433,7 @@ class ServerThread extends Thread { ServiceManager.addService(Context.DROPBOX_SERVICE, new DropBoxManagerService(context, new File("/data/system/dropbox"))); } catch (Throwable e) { - Slog.e(TAG, "Failure starting DropBoxManagerService", e); + reportWtf("starting DropBoxManagerService", e); } try { @@ -412,14 +441,14 @@ class ServerThread extends Thread { wallpaper = new WallpaperManagerService(context); ServiceManager.addService(Context.WALLPAPER_SERVICE, wallpaper); } catch (Throwable e) { - Slog.e(TAG, "Failure starting Wallpaper Service", e); + reportWtf("starting Wallpaper Service", e); } try { Slog.i(TAG, "Audio Service"); ServiceManager.addService(Context.AUDIO_SERVICE, new AudioService(context)); } catch (Throwable e) { - Slog.e(TAG, "Failure starting Audio Service", e); + reportWtf("starting Audio Service", e); } try { @@ -427,15 +456,15 @@ class ServerThread extends Thread { // Listen for dock station changes dock = new DockObserver(context, power); } catch (Throwable e) { - Slog.e(TAG, "Failure starting DockObserver", e); + reportWtf("starting DockObserver", e); } try { Slog.i(TAG, "Wired Accessory Observer"); // Listen for wired headset changes - wiredAccessory = new WiredAccessoryObserver(context); + new WiredAccessoryObserver(context); } catch (Throwable e) { - Slog.e(TAG, "Failure starting WiredAccessoryObserver", e); + reportWtf("starting WiredAccessoryObserver", e); } try { @@ -444,7 +473,7 @@ class ServerThread extends Thread { usb = new UsbService(context); ServiceManager.addService(Context.USB_SERVICE, usb); } catch (Throwable e) { - Slog.e(TAG, "Failure starting UsbService", e); + reportWtf("starting UsbService", e); } try { @@ -452,7 +481,7 @@ class ServerThread extends Thread { // Listen for UI mode changes uiMode = new UiModeManagerService(context); } catch (Throwable e) { - Slog.e(TAG, "Failure starting UiModeManagerService", e); + reportWtf("starting UiModeManagerService", e); } try { @@ -468,21 +497,21 @@ class ServerThread extends Thread { appWidget = new AppWidgetService(context); ServiceManager.addService(Context.APPWIDGET_SERVICE, appWidget); } catch (Throwable e) { - Slog.e(TAG, "Failure starting AppWidget Service", e); + reportWtf("starting AppWidget Service", e); } try { Slog.i(TAG, "Recognition Service"); recognition = new RecognitionManagerService(context); } catch (Throwable e) { - Slog.e(TAG, "Failure starting Recognition Service", e); + reportWtf("starting Recognition Service", e); } try { Slog.i(TAG, "DiskStats Service"); ServiceManager.addService("diskstats", new DiskStatsService(context)); } catch (Throwable e) { - Slog.e(TAG, "Failure starting DiskStats Service", e); + reportWtf("starting DiskStats Service", e); } try { @@ -494,14 +523,14 @@ class ServerThread extends Thread { ServiceManager.addService("samplingprofiler", new SamplingProfilerService(context)); } catch (Throwable e) { - Slog.e(TAG, "Failure starting SamplingProfiler Service", e); + reportWtf("starting SamplingProfiler Service", e); } try { Slog.i(TAG, "NetworkTimeUpdateService"); networkTimeUpdater = new NetworkTimeUpdateService(context); } catch (Throwable e) { - Slog.e(TAG, "Failure starting NetworkTimeUpdate service"); + reportWtf("starting NetworkTimeUpdate service", e); } } @@ -522,14 +551,26 @@ class ServerThread extends Thread { // It is now time to start up the app processes... if (devicePolicy != null) { - devicePolicy.systemReady(); + try { + devicePolicy.systemReady(); + } catch (Throwable e) { + reportWtf("making Device Policy Service ready", e); + } } if (notification != null) { - notification.systemReady(); + try { + notification.systemReady(); + } catch (Throwable e) { + reportWtf("making Notification Service ready", e); + } } - wm.systemReady(); + try { + wm.systemReady(); + } catch (Throwable e) { + reportWtf("making Window Manager Service ready", e); + } if (safeMode) { ActivityManagerService.self().showSafeModeOverlay(); @@ -547,7 +588,8 @@ class ServerThread extends Thread { power.systemReady(); try { pm.systemReady(); - } catch (RemoteException e) { + } catch (Throwable e) { + reportWtf("making Package Manager Service ready", e); } // These are needed to propagate to the runnable below. @@ -569,6 +611,7 @@ class ServerThread extends Thread { final CountryDetectorService countryDetectorF = countryDetector; final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater; final TextServicesManagerService textServiceManagerServiceF = tsms; + final StatusBarManagerService statusBarF = statusBar; // We now tell the activity manager it is okay to run third party // code. It will call back into us once it has gotten to the state @@ -581,28 +624,96 @@ class ServerThread extends Thread { Slog.i(TAG, "Making services ready"); startSystemUi(contextF); - if (batteryF != null) batteryF.systemReady(); - if (networkManagementF != null) networkManagementF.systemReady(); - if (networkStatsF != null) networkStatsF.systemReady(); - if (networkPolicyF != null) networkPolicyF.systemReady(); - if (connectivityF != null) connectivityF.systemReady(); - if (dockF != null) dockF.systemReady(); - if (usbF != null) usbF.systemReady(); - if (uiModeF != null) uiModeF.systemReady(); - if (recognitionF != null) recognitionF.systemReady(); + try { + if (batteryF != null) batteryF.systemReady(); + } catch (Throwable e) { + reportWtf("making Battery Service ready", e); + } + try { + if (networkManagementF != null) networkManagementF.systemReady(); + } catch (Throwable e) { + reportWtf("making Network Managment Service ready", e); + } + try { + if (networkStatsF != null) networkStatsF.systemReady(); + } catch (Throwable e) { + reportWtf("making Network Stats Service ready", e); + } + try { + if (networkPolicyF != null) networkPolicyF.systemReady(); + } catch (Throwable e) { + reportWtf("making Network Policy Service ready", e); + } + try { + if (connectivityF != null) connectivityF.systemReady(); + } catch (Throwable e) { + reportWtf("making Connectivity Service ready", e); + } + try { + if (dockF != null) dockF.systemReady(); + } catch (Throwable e) { + reportWtf("making Dock Service ready", e); + } + try { + if (usbF != null) usbF.systemReady(); + } catch (Throwable e) { + reportWtf("making USB Service ready", e); + } + try { + if (uiModeF != null) uiModeF.systemReady(); + } catch (Throwable e) { + reportWtf("making UI Mode Service ready", e); + } + try { + if (recognitionF != null) recognitionF.systemReady(); + } catch (Throwable e) { + reportWtf("making Recognition Service ready", e); + } Watchdog.getInstance().start(); // It is now okay to let the various system services start their // third party code... - if (appWidgetF != null) appWidgetF.systemReady(safeMode); - if (wallpaperF != null) wallpaperF.systemReady(); - if (immF != null) immF.systemReady(); - if (locationF != null) locationF.systemReady(); - if (countryDetectorF != null) countryDetectorF.systemReady(); - if (throttleF != null) throttleF.systemReady(); - if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemReady(); - if (textServiceManagerServiceF != null) textServiceManagerServiceF.systemReady(); + try { + if (appWidgetF != null) appWidgetF.systemReady(safeMode); + } catch (Throwable e) { + reportWtf("making App Widget Service ready", e); + } + try { + if (wallpaperF != null) wallpaperF.systemReady(); + } catch (Throwable e) { + reportWtf("making Wallpaper Service ready", e); + } + try { + if (immF != null) immF.systemReady(statusBarF); + } catch (Throwable e) { + reportWtf("making Input Method Service ready", e); + } + try { + if (locationF != null) locationF.systemReady(); + } catch (Throwable e) { + reportWtf("making Location Service ready", e); + } + try { + if (countryDetectorF != null) countryDetectorF.systemReady(); + } catch (Throwable e) { + reportWtf("making Country Detector Service ready", e); + } + try { + if (throttleF != null) throttleF.systemReady(); + } catch (Throwable e) { + reportWtf("making Throttle Service ready", e); + } + try { + if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemReady(); + } catch (Throwable e) { + reportWtf("making Network Time Service ready", e); + } + try { + if (textServiceManagerServiceF != null) textServiceManagerServiceF.systemReady(); + } catch (Throwable e) { + reportWtf("making Text Services Manager Service ready", e); + } } }); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 800c4fc32598..b817598a3fc4 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -91,6 +91,7 @@ import android.os.Environment; import android.os.FileObserver; import android.os.FileUtils; import android.os.Handler; +import android.os.HandlerThread; import android.os.IBinder; import android.os.IPermissionController; import android.os.Looper; @@ -3748,6 +3749,10 @@ public final class ActivityManagerService extends ActivityManagerNative mWindowManager.enableScreenAfterBoot(); } + public void showBootMessage(final CharSequence msg, final boolean always) { + mWindowManager.showBootMessage(msg, always); + } + final void finishBooting() { IntentFilter pkgFilter = new IntentFilter(); pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); @@ -6446,7 +6451,9 @@ public final class ActivityManagerService extends ActivityManagerNative File fname = new File(systemDir, "called_pre_boots.dat"); return fname; } - + + static final int LAST_DONE_VERSION = 10000; + private static ArrayList<ComponentName> readLastDonePreBootReceivers() { ArrayList<ComponentName> lastDoneReceivers = new ArrayList<ComponentName>(); File file = getCalledPreBootReceiversFile(); @@ -6454,16 +6461,21 @@ public final class ActivityManagerService extends ActivityManagerNative try { fis = new FileInputStream(file); DataInputStream dis = new DataInputStream(new BufferedInputStream(fis, 2048)); - int vers = dis.readInt(); - String codename = dis.readUTF(); - if (vers == android.os.Build.VERSION.SDK_INT - && codename.equals(android.os.Build.VERSION.CODENAME)) { - int num = dis.readInt(); - while (num > 0) { - num--; - String pkg = dis.readUTF(); - String cls = dis.readUTF(); - lastDoneReceivers.add(new ComponentName(pkg, cls)); + int fvers = dis.readInt(); + if (fvers == LAST_DONE_VERSION) { + String vers = dis.readUTF(); + String codename = dis.readUTF(); + String build = dis.readUTF(); + if (android.os.Build.VERSION.RELEASE.equals(vers) + && android.os.Build.VERSION.CODENAME.equals(codename) + && android.os.Build.VERSION.INCREMENTAL.equals(build)) { + int num = dis.readInt(); + while (num > 0) { + num--; + String pkg = dis.readUTF(); + String cls = dis.readUTF(); + lastDoneReceivers.add(new ComponentName(pkg, cls)); + } } } } catch (FileNotFoundException e) { @@ -6488,8 +6500,10 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.i(TAG, "Writing new set of last done pre-boot receivers..."); fos = new FileOutputStream(file); dos = new DataOutputStream(new BufferedOutputStream(fos, 2048)); - dos.writeInt(android.os.Build.VERSION.SDK_INT); + dos.writeInt(LAST_DONE_VERSION); + dos.writeUTF(android.os.Build.VERSION.RELEASE); dos.writeUTF(android.os.Build.VERSION.CODENAME); + dos.writeUTF(android.os.Build.VERSION.INCREMENTAL); dos.writeInt(list.size()); for (int i=0; i<list.size(); i++) { dos.writeUTF(list.get(i).getPackageName()); @@ -6571,6 +6585,9 @@ public final class ActivityManagerService extends ActivityManagerNative mDidUpdate = true; } writeLastDonePreBootReceivers(doneReceivers); + showBootMessage(mContext.getText( + R.string.android_upgrading_complete), + false); systemReady(goingCallback); } }); diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index dbb8164dbdaa..2f19a46203b9 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -187,6 +187,7 @@ public class PackageManagerService extends IPackageManager.Stub { static final int SCAN_NEW_INSTALL = 1<<4; static final int SCAN_NO_PATHS = 1<<5; static final int SCAN_UPDATE_TIME = 1<<6; + static final int SCAN_DEFER_DEX = 1<<7; static final int REMOVE_CHATTY = 1<<16; @@ -349,6 +350,9 @@ public class PackageManagerService extends IPackageManager.Stub { /** List of packages waiting for verification. */ final SparseArray<InstallArgs> mPendingVerification = new SparseArray<InstallArgs>(); + final ArrayList<PackageParser.Package> mDeferredDexOpt = + new ArrayList<PackageParser.Package>(); + /** Token for keys in mPendingVerification. */ private int mPendingVerificationToken = 0; @@ -907,7 +911,7 @@ public class PackageManagerService extends IPackageManager.Stub { // Set flag to monitor and not change apk file paths when // scanning install directories. - int scanMode = SCAN_MONITOR | SCAN_NO_PATHS; + int scanMode = SCAN_MONITOR | SCAN_NO_PATHS | SCAN_DEFER_DEX; if (mNoDexOpt) { Slog.w(TAG, "Running ENG build: no pre-dexopt!"); scanMode |= SCAN_NO_DEX; @@ -2899,6 +2903,33 @@ public class PackageManagerService extends IPackageManager.Stub { } } + public void performBootDexOpt() { + ArrayList<PackageParser.Package> pkgs = null; + synchronized (mPackages) { + if (mDeferredDexOpt.size() > 0) { + pkgs = new ArrayList<PackageParser.Package>(mDeferredDexOpt); + mDeferredDexOpt.clear(); + } + } + if (pkgs != null) { + for (int i=0; i<pkgs.size(); i++) { + try { + ActivityManagerNative.getDefault().showBootMessage( + mContext.getResources().getString( + com.android.internal.R.string.android_upgrading_apk, + i+1, pkgs.size()), true); + } catch (RemoteException e) { + } + PackageParser.Package p = pkgs.get(i); + synchronized (mInstallLock) { + if (!p.mDidDexOpt) { + performDexOptLI(p, false, false); + } + } + } + } + } + public boolean performDexOpt(String packageName) { enforceSystemOrRoot("Only the system can request dexopt be performed"); @@ -2914,25 +2945,32 @@ public class PackageManagerService extends IPackageManager.Stub { } } synchronized (mInstallLock) { - return performDexOptLI(p, false) == DEX_OPT_PERFORMED; + return performDexOptLI(p, false, false) == DEX_OPT_PERFORMED; } } static final int DEX_OPT_SKIPPED = 0; static final int DEX_OPT_PERFORMED = 1; + static final int DEX_OPT_DEFERRED = 2; static final int DEX_OPT_FAILED = -1; - private int performDexOptLI(PackageParser.Package pkg, boolean forceDex) { + private int performDexOptLI(PackageParser.Package pkg, boolean forceDex, boolean defer) { boolean performed = false; if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { String path = pkg.mScanPath; int ret = 0; try { if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) { - ret = mInstaller.dexopt(path, pkg.applicationInfo.uid, - !isForwardLocked(pkg)); - pkg.mDidDexOpt = true; - performed = true; + if (!forceDex && defer) { + mDeferredDexOpt.add(pkg); + return DEX_OPT_DEFERRED; + } else { + Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName); + ret = mInstaller.dexopt(path, pkg.applicationInfo.uid, + !isForwardLocked(pkg)); + pkg.mDidDexOpt = true; + performed = true; + } } } catch (FileNotFoundException e) { Slog.w(TAG, "Apk not found for dexopt: " + path); @@ -3487,7 +3525,8 @@ public class PackageManagerService extends IPackageManager.Stub { pkg.mScanPath = path; if ((scanMode&SCAN_NO_DEX) == 0) { - if (performDexOptLI(pkg, forceDex) == DEX_OPT_FAILED) { + if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0) + == DEX_OPT_FAILED) { mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT; return null; } diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 8fd4f9589df0..dc5555ef808c 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -50,6 +50,7 @@ import com.android.server.am.BatteryStatsService; import android.Manifest; import android.app.ActivityManagerNative; import android.app.IActivityManager; +import android.app.ProgressDialog; import android.app.StatusBarManager; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; @@ -407,6 +408,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean mSafeMode; boolean mDisplayEnabled = false; boolean mSystemBooted = false; + boolean mShowingBootMessages = false; int mInitialDisplayWidth = 0; int mInitialDisplayHeight = 0; int mBaseDisplayWidth = 0; @@ -4685,16 +4687,17 @@ public class WindowManagerService extends IWindowManager.Stub return; } mSystemBooted = true; + hideBootMessagesLocked(); } performEnableScreen(); } - public void enableScreenIfNeededLocked() { + void enableScreenIfNeededLocked() { if (mDisplayEnabled) { return; } - if (!mSystemBooted) { + if (!mSystemBooted && !mShowingBootMessages) { return; } mH.sendMessage(mH.obtainMessage(H.ENABLE_SCREEN)); @@ -4705,7 +4708,7 @@ public class WindowManagerService extends IWindowManager.Stub if (mDisplayEnabled) { return; } - if (!mSystemBooted) { + if (!mSystemBooted && !mShowingBootMessages) { return; } @@ -4749,6 +4752,33 @@ public class WindowManagerService extends IWindowManager.Stub mLastRotationFlags | Surface.FLAGS_ORIENTATION_ANIMATION_DISABLE); } + public void showBootMessage(final CharSequence msg, final boolean always) { + boolean first = false; + synchronized(mWindowMap) { + if (!mShowingBootMessages) { + if (!always) { + return; + } + first = true; + } + if (mSystemBooted) { + return; + } + mShowingBootMessages = true; + mPolicy.showBootMessage(msg, always); + } + if (first) { + performEnableScreen(); + } + } + + public void hideBootMessagesLocked() { + if (mShowingBootMessages) { + mShowingBootMessages = false; + mPolicy.hideBootMessages(); + } + } + public void setInTouchMode(boolean mode) { synchronized(mWindowMap) { mInTouchMode = mode; @@ -6136,7 +6166,7 @@ public class WindowManagerService extends IWindowManager.Stub return mSafeMode; } - public void systemReady() { + public void displayReady() { synchronized(mWindowMap) { if (mDisplay != null) { throw new IllegalStateException("Display already initialized"); @@ -6165,14 +6195,16 @@ public class WindowManagerService extends IWindowManager.Stub mActivityManager.updateConfiguration(null); } catch (RemoteException e) { } - - mPolicy.systemReady(); - + synchronized (mWindowMap) { readForcedDisplaySizeLocked(); } } + public void systemReady() { + mPolicy.systemReady(); + } + // This is an animation that does nothing: it just immediately finishes // itself every time it is called. It is used as a stub animation in cases // where we want to synchronize multiple things that may be animating. diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 50afb3d8bd6e..1f27a70f696f 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -822,7 +822,7 @@ void SurfaceFlinger::handleWorkList() hwc_layer_t* const cur(hwc.getLayers()); for (size_t i=0 ; cur && i<count ; i++) { currentLayers[i]->setGeometry(&cur[i]); - if (mDebugDisableHWC) { + if (mDebugDisableHWC || mDebugRegion) { cur[i].compositionType = HWC_FRAMEBUFFER; cur[i].flags |= HWC_SKIP_LAYER; } @@ -974,6 +974,10 @@ void SurfaceFlinger::debugFlashRegions() { const DisplayHardware& hw(graphicPlane(0).displayHardware()); const uint32_t flags = hw.getFlags(); + const int32_t height = hw.getHeight(); + if (mInvalidRegion.isEmpty()) { + return; + } if (!((flags & DisplayHardware::SWAP_RECTANGLE) || (flags & DisplayHardware::BUFFER_PRESERVED))) { @@ -999,26 +1003,21 @@ void SurfaceFlinger::debugFlashRegions() while (it != end) { const Rect& r = *it++; GLfloat vertices[][2] = { - { r.left, r.top }, - { r.left, r.bottom }, - { r.right, r.bottom }, - { r.right, r.top } + { r.left, height - r.top }, + { r.left, height - r.bottom }, + { r.right, height - r.bottom }, + { r.right, height - r.top } }; glVertexPointer(2, GL_FLOAT, 0, vertices); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } - if (mInvalidRegion.isEmpty()) { - mDirtyRegion.dump("mDirtyRegion"); - mInvalidRegion.dump("mInvalidRegion"); - } hw.flip(mInvalidRegion); if (mDebugRegion > 1) usleep(mDebugRegion * 1000); glEnable(GL_SCISSOR_TEST); - //mDirtyRegion.dump("mDirtyRegion"); } void SurfaceFlinger::drawWormhole() const @@ -1581,7 +1580,7 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) HWComposer& hwc(hw.getHwComposer()); snprintf(buffer, SIZE, " h/w composer %s and %s\n", hwc.initCheck()==NO_ERROR ? "present" : "not present", - mDebugDisableHWC ? "disabled" : "enabled"); + (mDebugDisableHWC || mDebugRegion) ? "disabled" : "enabled"); result.append(buffer); hwc.dump(result, buffer, SIZE); @@ -1660,21 +1659,15 @@ status_t SurfaceFlinger::onTransact( case 1002: // SHOW_UPDATES n = data.readInt32(); mDebugRegion = n ? n : (mDebugRegion ? 0 : 1); + invalidateHwcGeometry(); + repaintEverything(); return NO_ERROR; case 1003: // SHOW_BACKGROUND n = data.readInt32(); mDebugBackground = n ? 1 : 0; return NO_ERROR; - case 1008: // toggle use of hw composer - n = data.readInt32(); - mDebugDisableHWC = n ? 1 : 0; - invalidateHwcGeometry(); - // fall-through... case 1004:{ // repaint everything - Mutex::Autolock _l(mStateLock); - const DisplayHardware& hw(graphicPlane(0).displayHardware()); - mDirtyRegion.set(hw.bounds()); // careful that's not thread-safe - signalEvent(); + repaintEverything(); return NO_ERROR; } case 1005:{ // force transaction @@ -1690,6 +1683,12 @@ status_t SurfaceFlinger::onTransact( mFreezeCount = data.readInt32(); mFreezeDisplayTime = 0; return NO_ERROR; + case 1008: // toggle use of hw composer + n = data.readInt32(); + mDebugDisableHWC = n ? 1 : 0; + invalidateHwcGeometry(); + repaintEverything(); + return NO_ERROR; case 1010: // interrogate. reply->writeInt32(0); reply->writeInt32(0); @@ -1707,6 +1706,13 @@ status_t SurfaceFlinger::onTransact( return err; } +void SurfaceFlinger::repaintEverything() { + Mutex::Autolock _l(mStateLock); + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + mDirtyRegion.set(hw.bounds()); // careful that's not thread-safe + signalEvent(); +} + // --------------------------------------------------------------------------- status_t SurfaceFlinger::renderScreenToTextureLocked(DisplayID dpy, diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 1738238abea4..d68e4840199f 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -278,6 +278,7 @@ private: void handleRepaint(); void postFramebuffer(); void composeSurfaces(const Region& dirty); + void repaintEverything(); ssize_t addClientLayer(const sp<Client>& client, diff --git a/tests/TileBenchmark/res/values/strings.xml b/tests/TileBenchmark/res/values/strings.xml index c4fd189c8ac3..5af52dcbe4b9 100644 --- a/tests/TileBenchmark/res/values/strings.xml +++ b/tests/TileBenchmark/res/values/strings.xml @@ -73,6 +73,8 @@ <string name="viewport_coverage">Coverage</string> <!-- Milliseconds taken to inval, and re-render the page [CHAR LIMIT=15] --> <string name="render_millis">RenderMillis</string> + <!-- Number of rendering stalls while running the test [CHAR LIMIT=15] --> + <string name="render_stalls">Stalls</string> <!-- Format string for stat value overlay [CHAR LIMIT=15] --> <string name="format_stat">%4.4f</string> diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java index 82a7e8226415..e7a21ad8437d 100644 --- a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java +++ b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfileActivity.java @@ -22,7 +22,6 @@ import android.content.Context; import android.graphics.Bitmap; import android.os.AsyncTask; import android.os.Bundle; -import android.os.CountDownTimer; import android.util.Pair; import android.view.KeyEvent; import android.view.View; @@ -55,8 +54,6 @@ public class ProfileActivity extends Activity { } public static final String TEMP_FILENAME = "profile.tiles"; - private static final int LOAD_TEST_DELAY = 1000; // nr of millis after load, - // before test Button mInspectButton; ToggleButton mCaptureButton; @@ -136,16 +133,7 @@ public class ProfileActivity extends Activity { super.onPageFinished(view, url); view.requestFocus(); - new CountDownTimer(LOAD_TEST_DELAY, LOAD_TEST_DELAY) { - @Override - public void onTick(long millisUntilFinished) { - } - - @Override - public void onFinish() { - startViewProfiling(true); - } - }.start(); + startViewProfiling(true); } @Override diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java index 3fc4665e9238..b1cef15ce639 100644 --- a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java +++ b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java @@ -17,6 +17,7 @@ package com.test.tilebenchmark; import android.content.Context; +import android.os.CountDownTimer; import android.util.AttributeSet; import android.util.Log; import android.webkit.WebView; @@ -27,10 +28,14 @@ import com.test.tilebenchmark.RunData.TileData; public class ProfiledWebView extends WebView { private int mSpeed; - private boolean isTesting = false; - private boolean isScrolling = false; + private boolean mIsTesting = false; + private boolean mIsScrolling = false; private ProfileCallback mCallback; private long mContentInvalMillis; + private boolean mHadToBeForced = false; + private int mTestCount = 0; + private static final int LOAD_STALL_MILLIS = 5000; // nr of millis after load, + // before test is forced public ProfiledWebView(Context context) { super(context); @@ -51,12 +56,12 @@ public class ProfiledWebView extends WebView { @Override protected void onDraw(android.graphics.Canvas canvas) { - if (isTesting && isScrolling) { + if (mIsTesting && mIsScrolling) { if (canScrollVertically(1)) { scrollBy(0, mSpeed); } else { stopScrollTest(); - isScrolling = false; + mIsScrolling = false; } } super.onDraw(canvas); @@ -68,13 +73,36 @@ public class ProfiledWebView extends WebView { * scrolling, invalidate all content and redraw it, measuring time taken. */ public void startScrollTest(ProfileCallback callback, boolean autoScrolling) { - isScrolling = autoScrolling; + mIsScrolling = autoScrolling; mCallback = callback; - isTesting = false; + mIsTesting = false; mContentInvalMillis = System.currentTimeMillis(); registerPageSwapCallback(); contentInvalidateAll(); invalidate(); + + mTestCount++; + final int testCount = mTestCount; + + if (autoScrolling) { + // after a while, force it to start even if the pages haven't swapped + new CountDownTimer(LOAD_STALL_MILLIS, LOAD_STALL_MILLIS) { + @Override + public void onTick(long millisUntilFinished) { + } + + @Override + public void onFinish() { + if (testCount == mTestCount && !mIsTesting) { + mHadToBeForced = true; + Log.d("ProfiledWebView", "num " + testCount + + " forcing a page swap with a scroll..."); + scrollBy(0, 1); + invalidate(); // ensure a redraw so that auto-scrolling can occur + } + } + }.start(); + } } /* @@ -87,7 +115,7 @@ public class ProfiledWebView extends WebView { super.pageSwapCallback(); Log.d("ProfiledWebView", "REDRAW TOOK " + mContentInvalMillis + "millis"); - isTesting = true; + mIsTesting = true; invalidate(); // ensure a redraw so that auto-scrolling can occur tileProfilingStart(); } @@ -97,7 +125,7 @@ public class ProfiledWebView extends WebView { */ public void stopScrollTest() { tileProfilingStop(); - isTesting = false; + mIsTesting = false; if (mCallback == null) { tileProfilingClear(); @@ -105,8 +133,15 @@ public class ProfiledWebView extends WebView { } RunData data = new RunData(super.tileProfilingNumFrames()); + // record the time spent (before scrolling) rendering the page data.singleStats.put(getResources().getString(R.string.render_millis), (double)mContentInvalMillis); + // record if the page render timed out + Log.d("ProfiledWebView", "hadtobeforced = " + mHadToBeForced); + data.singleStats.put(getResources().getString(R.string.render_stalls), + mHadToBeForced ? 1.0 : 0.0); + mHadToBeForced = false; + for (int frame = 0; frame < data.frames.length; frame++) { data.frames[frame] = new TileData[ tileProfilingNumTilesInFrame(frame)]; diff --git a/tests/TileBenchmark/tests/src/com/test/tilebenchmark/PerformanceTest.java b/tests/TileBenchmark/tests/src/com/test/tilebenchmark/PerformanceTest.java index 0f02239c1c36..6bf6f6b46c86 100644 --- a/tests/TileBenchmark/tests/src/com/test/tilebenchmark/PerformanceTest.java +++ b/tests/TileBenchmark/tests/src/com/test/tilebenchmark/PerformanceTest.java @@ -80,7 +80,7 @@ public class PerformanceTest extends private static final String URL_POSTFIX = "/index.html?skip=true"; private static final int MAX_ITERATIONS = 4; private static final String TEST_DIRS[] = { - "alexa_us"//, "android", "dom", "intl1", "intl2", "moz", "moz2" + "intl1"//, "alexa_us", "android", "dom", "intl2", "moz", "moz2" }; public PerformanceTest() { |