diff options
85 files changed, 1536 insertions, 603 deletions
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java index 5cde65c..03e0c0f 100644 --- a/core/java/android/accounts/AccountManagerService.java +++ b/core/java/android/accounts/AccountManagerService.java @@ -247,7 +247,6 @@ public class AccountManagerService } public void systemReady() { - initUser(UserHandle.USER_OWNER); } private UserManager getUserManager() { diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 7492629..67d3930 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -2809,6 +2809,15 @@ class ActivityManagerProxy implements IActivityManager return success; } + public void clearPendingBackup() throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + mRemote.transact(CLEAR_PENDING_BACKUP_TRANSACTION, data, reply, 0); + reply.recycle(); + data.recycle(); + } + public void backupAgentCreated(String packageName, IBinder agent) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 5f65f08..456d757 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -29,6 +29,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.InstrumentationInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ProviderInfo; @@ -2396,12 +2397,31 @@ public final class ActivityThread { private void handleCreateBackupAgent(CreateBackupAgentData data) { if (DEBUG_BACKUP) Slog.v(TAG, "handleCreateBackupAgent: " + data); + // Sanity check the requested target package's uid against ours + try { + PackageInfo requestedPackage = getPackageManager().getPackageInfo( + data.appInfo.packageName, 0, UserHandle.myUserId()); + if (requestedPackage.applicationInfo.uid != Process.myUid()) { + Slog.w(TAG, "Asked to instantiate non-matching package " + + data.appInfo.packageName); + return; + } + } catch (RemoteException e) { + Slog.e(TAG, "Can't reach package manager", e); + return; + } + // no longer idle; we have backup work to do unscheduleGcIdler(); // instantiate the BackupAgent class named in the manifest LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo); String packageName = packageInfo.mPackageName; + if (packageName == null) { + Slog.d(TAG, "Asked to create backup agent for nonexistent package"); + return; + } + if (mBackupAgents.get(packageName) != null) { Slog.d(TAG, "BackupAgent " + " for " + packageName + " already exists"); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 97250e9..8fc1c86 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -152,6 +152,7 @@ public interface IActivityManager extends IInterface { public boolean bindBackupAgent(ApplicationInfo appInfo, int backupRestoreMode) throws RemoteException; + public void clearPendingBackup() throws RemoteException; public void backupAgentCreated(String packageName, IBinder agent) throws RemoteException; public void unbindBackupAgent(ApplicationInfo appInfo) throws RemoteException; public void killApplicationProcess(String processName, int uid) throws RemoteException; @@ -619,4 +620,5 @@ public interface IActivityManager extends IInterface { int GET_RUNNING_USER_IDS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+156; int REQUEST_BUG_REPORT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+157; int INPUT_DISPATCHING_TIMED_OUT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+158; + int CLEAR_PENDING_BACKUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+159; } diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index 58df167..bb0c686 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -55,6 +55,7 @@ import android.text.format.Time; import android.util.EventLog; import android.util.Log; import android.util.Pair; +import android.util.Slog; import com.android.internal.R; import com.android.internal.util.IndentingPrintWriter; @@ -311,13 +312,10 @@ public class SyncManager { if (userId == UserHandle.USER_NULL) return; if (Intent.ACTION_USER_REMOVED.equals(action)) { - Log.i(TAG, "User removed: u" + userId); onUserRemoved(userId); } else if (Intent.ACTION_USER_STARTING.equals(action)) { - Log.i(TAG, "User starting: u" + userId); onUserStarting(userId); } else if (Intent.ACTION_USER_STOPPING.equals(action)) { - Log.i(TAG, "User stopping: u" + userId); onUserStopping(userId); } } @@ -338,6 +336,10 @@ public class SyncManager { } } + /** + * Should only be created after {@link ContentService#systemReady()} so that + * {@link PackageManager} is ready to query. + */ public SyncManager(Context context, boolean factoryTest) { // Initialize the SyncStorageEngine first, before registering observers // and creating threads and so on; it may fail if the disk is full. @@ -441,9 +443,6 @@ public class SyncManager { UserHandle.ALL, new IntentFilter(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION), null, null); - - // do this synchronously to ensure we have the accounts before this call returns - onUserStarting(UserHandle.USER_OWNER); } // Pick a random second in a day to seed all periodic syncs diff --git a/core/java/android/content/SyncQueue.java b/core/java/android/content/SyncQueue.java index 14bfc5b..c9a325e 100644 --- a/core/java/android/content/SyncQueue.java +++ b/core/java/android/content/SyncQueue.java @@ -51,8 +51,6 @@ public class SyncQueue { public SyncQueue(SyncStorageEngine syncStorageEngine, final SyncAdaptersCache syncAdapters) { mSyncStorageEngine = syncStorageEngine; mSyncAdapters = syncAdapters; - - addPendingOperations(UserHandle.USER_OWNER); } public void addPendingOperations(int userId) { diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java index 69e1de9..ce5f163 100644 --- a/core/java/android/os/AsyncTask.java +++ b/core/java/android/os/AsyncTask.java @@ -110,7 +110,7 @@ import java.util.concurrent.atomic.AtomicInteger; * <h2>The 4 steps</h2> * <p>When an asynchronous task is executed, the task goes through 4 steps:</p> * <ol> - * <li>{@link #onPreExecute()}, invoked on the UI thread immediately after the task + * <li>{@link #onPreExecute()}, invoked on the UI thread before the task * is executed. This step is normally used to setup the task, for instance by * showing a progress bar in the user interface.</li> * <li>{@link #doInBackground}, invoked on the background thread diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index 4820c5e..6c9290b 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -51,12 +51,17 @@ import com.android.internal.policy.PolicyManager; * an exhibition/lean-back experience.</p> * * <p>The Dream lifecycle is as follows:</p> - * <ul> - * <li>onAttachedToWindow</li> - * <li>onDreamingStarted</li> - * <li>onDreamingStopped</li> - * <li>onDetachedFromWindow</li> - * </ul> + * <ol> + * <li>{@link #onAttachedToWindow} + * <p>Use this for initial setup, such as calling {@link #setContentView setContentView()}.</li> + * <li>{@link #onDreamingStarted} + * <p>Your dream has started, so you should begin animations or other behaviors here.</li> + * <li>{@link #onDreamingStopped} + * <p>Use this to stop the things you started in {@link #onDreamingStarted}.</li> + * <li>{@link #onDetachedFromWindow} + * <p>Use this to dismantle resources your dream set up. For example, detach from handlers + * and listeners.</li> + * </ol> * * <p>In addition, onCreate and onDestroy (from the Service interface) will also be called, but * initialization and teardown should be done by overriding the hooks above.</p> @@ -80,14 +85,40 @@ import com.android.internal.policy.PolicyManager; * android:resource="@xml/my_dream" /> * </service> * </pre> - * <p>If specified, additional information for the dream is defined using the - * <code><{@link android.R.styleable#Dream dream}></code> element. For example:</p> - * <pre> - * (in res/xml/my_dream.xml) * + * <p>If specified with the {@code <meta-data>} element, + * additional information for the dream is defined using the + * {@link android.R.styleable#Dream <dream>} element in a separate XML file. + * Currently, the only addtional + * information you can provide is for a settings activity that allows the user to configure + * the dream behavior. For example:</p> + * <p class="code-caption">res/xml/my_dream.xml</p> + * <pre> * <dream xmlns:android="http://schemas.android.com/apk/res/android" * android:settingsActivity="com.example.app/.MyDreamSettingsActivity" /> * </pre> + * <p>This makes a Settings button available alongside your dream's listing in the + * system settings, which when pressed opens the specified activity.</p> + * + * + * <p>To specify your dream layout, call {@link #setContentView}, typically during the + * {@link #onAttachedToWindow} callback. For example:</p> + * <pre> + * public class MyDream extends DreamService { + * + * @Override + * public void onAttachedToWindow() { + * super.onAttachedToWindow(); + * + * // Exit dream upon user touch + * setInteractive(false); + * // Hide system UI + * setFullscreen(true); + * // Set the dream layout + * setContentView(R.layout.dream); + * } + * } + * </pre> */ public class DreamService extends Service implements Window.Callback { private final String TAG = DreamService.class.getSimpleName() + "[" + getClass().getSimpleName() + "]"; @@ -323,11 +354,12 @@ public class DreamService extends Service implements Window.Callback { /** * Sets a view to be the content view for this Dream. - * Behaves similarly to {@link android.app.Activity#setContentView(android.view.View)}, + * Behaves similarly to {@link android.app.Activity#setContentView(android.view.View)} in an activity, * including using {@link ViewGroup.LayoutParams#MATCH_PARENT} as the layout height and width of the view. * - * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p> - * @param view The desired content to display. + * <p>Note: This requires a window, so you should usually call it during + * {@link #onAttachedToWindow()} and never earlier (you <strong>cannot</strong> call it + * during {@link #onCreate}).</p> * * @see #setContentView(int) * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) @@ -339,9 +371,12 @@ public class DreamService extends Service implements Window.Callback { /** * Sets a view to be the content view for this Dream. * Behaves similarly to - * {@link android.app.Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}. + * {@link android.app.Activity#setContentView(android.view.View, android.view.ViewGroup.LayoutParams)} + * in an activity. * - * <p>Note: Requires a window, do not call before {@link #onAttachedToWindow()}</p> + * <p>Note: This requires a window, so you should usually call it during + * {@link #onAttachedToWindow()} and never earlier (you <strong>cannot</strong> call it + * during {@link #onCreate}).</p> * * @param view The desired content to display. * @param params Layout parameters for the view. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 7ce3042..67452ec 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -887,11 +887,13 @@ public final class ViewRootImpl implements ViewParent, // Intersect with the bounds of the window to skip // updates that lie outside of the visible region final float appScale = mAttachInfo.mApplicationScale; - localDirty.intersect(0, 0, - (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f)); - - if (!mWillDrawSoon) { - scheduleTraversals(); + if (localDirty.intersect(0, 0, + (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f))) { + if (!mWillDrawSoon) { + scheduleTraversals(); + } + } else { + localDirty.setEmpty(); } return null; diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 1b71b43..5d306d2 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -829,7 +829,6 @@ static void android_view_GLES20Canvas_clearLayerTexture(JNIEnv* env, jobject cla static void android_view_GLES20Canvas_setTextureLayerTransform(JNIEnv* env, jobject clazz, Layer* layer, SkMatrix* matrix) { - layer->getTransform().load(*matrix); } diff --git a/core/res/res/drawable-hdpi/ic_notify_wifidisplay.png b/core/res/res/drawable-hdpi/ic_notify_wifidisplay.png Binary files differnew file mode 100644 index 0000000..35f27df --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_notify_wifidisplay.png diff --git a/core/res/res/drawable-mdpi/ic_notify_wifidisplay.png b/core/res/res/drawable-mdpi/ic_notify_wifidisplay.png Binary files differnew file mode 100644 index 0000000..f9c8678 --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_notify_wifidisplay.png diff --git a/core/res/res/drawable-xhdpi/ic_notify_wifidisplay.png b/core/res/res/drawable-xhdpi/ic_notify_wifidisplay.png Binary files differnew file mode 100644 index 0000000..4cc0ee8 --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_notify_wifidisplay.png diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index c93008d..3e8892b 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -1418,7 +1418,7 @@ <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik geteken. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings, sal jy gevra word om jou tablet te ontsluit deur middel van \'n e-posrekening."\n\n" Probeer weer oor <xliff:g id="NUMBER_2">%d</xliff:g> sekondes."</string> <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik geteken. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings, sal jy gevra word om jou foon te ontsluit deur middel van \'n e-posrekening."\n\n" Probeer weer oor <xliff:g id="NUMBER_2">%d</xliff:g> sekondes."</string> <string name="safe_media_volume_warning" product="default" msgid="7382971871993371648">"Moet volume bo veilige vlak verhoog word?"\n"Deur vir lang tydperke op hoë volume te luister, kan jou gehoor beskadig."</string> - <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Hou twee vingers in om toeganklikheid te aktiveer."</string> + <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Hou aan met twee vingers inhou om toeganklikheid te aktiveer."</string> <string name="accessibility_enabled" msgid="1381972048564547685">"Toeganklikheid geaktiveer."</string> <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Toeganklikheid gekanselleer."</string> <string name="user_switched" msgid="3768006783166984410">"Huidige gebruiker <xliff:g id="NAME">%1$s</xliff:g> ."</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 4698002..afd847f 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -636,19 +636,12 @@ of new location providers at run-time. The new package does not have to be explicitly listed here, however it must have a signature that matches the signature of at least one package on this list. - Platforms should overlay additional packages in - config_overlay_locationProviderPackageNames, instead of overlaying - this config, if they only want to append packages and not replace - the entire array. --> <string-array name="config_locationProviderPackageNames" translatable="false"> + <!-- The standard AOSP fused location provider --> <item>com.android.location.fused</item> </string-array> - <!-- Pacakge name(s) supplied by overlay, and appended to - config_locationProviderPackageNames. --> - <string-array name="config_overlay_locationProviderPackageNames" translatable="false" /> - <!-- Boolean indicating if current platform supports bluetooth SCO for off call use cases --> <bool name="config_bluetooth_sco_off_call">true</bool> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 72de22c..f8dbd84 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3821,6 +3821,13 @@ <!-- Title text to show within the overlay. [CHAR LIMIT=50] --> <string name="display_manager_overlay_display_title"><xliff:g id="name">%1$s</xliff:g>: <xliff:g id="width">%2$d</xliff:g>x<xliff:g id="height">%3$d</xliff:g>, <xliff:g id="dpi">%4$d</xliff:g> dpi</string> + <!-- Title of the notification to indicate an active wifi display connection. [CHAR LIMIT=50] --> + <string name="wifi_display_notification_title">Wireless display is connected</string> + <!-- Message of the notification to indicate an active wifi display connection. [CHAR LIMIT=80] --> + <string name="wifi_display_notification_message">This screen is showing on another device</string> + <!-- Label of a button to disconnect an active wifi display connection. [CHAR LIMIT=25] --> + <string name="wifi_display_notification_disconnect">Disconnect</string> + <!-- Keyguard strings --> <!-- Label shown on emergency call button in keyguard --> <string name="kg_emergency_call_label">Emergency call</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e76b67b..7c0547e 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1475,7 +1475,6 @@ <java-symbol type="array" name="radioAttributes" /> <java-symbol type="array" name="config_oemUsbModeOverride" /> <java-symbol type="array" name="config_locationProviderPackageNames" /> - <java-symbol type="array" name="config_overlay_locationProviderPackageNames" /> <java-symbol type="bool" name="config_animateScreenLights" /> <java-symbol type="bool" name="config_automatic_brightness_available" /> <java-symbol type="bool" name="config_sf_limitedAlpha" /> @@ -1486,6 +1485,7 @@ <java-symbol type="bool" name="show_ongoing_ime_switcher" /> <java-symbol type="color" name="config_defaultNotificationColor" /> <java-symbol type="drawable" name="ic_notification_ime_default" /> + <java-symbol type="drawable" name="ic_notify_wifidisplay" /> <java-symbol type="drawable" name="stat_notify_car_mode" /> <java-symbol type="drawable" name="stat_notify_disabled" /> <java-symbol type="drawable" name="stat_notify_disk_full" /> @@ -1621,6 +1621,9 @@ <java-symbol type="string" name="vpn_lockdown_error" /> <java-symbol type="string" name="vpn_lockdown_reset" /> <java-symbol type="string" name="wallpaper_binding_label" /> + <java-symbol type="string" name="wifi_display_notification_title" /> + <java-symbol type="string" name="wifi_display_notification_message" /> + <java-symbol type="string" name="wifi_display_notification_disconnect" /> <java-symbol type="style" name="Theme.Dialog.AppError" /> <java-symbol type="style" name="Theme.Toast" /> <java-symbol type="xml" name="storage_list" /> diff --git a/core/tests/ConnectivityManagerTest/AndroidManifest.xml b/core/tests/ConnectivityManagerTest/AndroidManifest.xml index 05f8b39..1bbc7df 100644 --- a/core/tests/ConnectivityManagerTest/AndroidManifest.xml +++ b/core/tests/ConnectivityManagerTest/AndroidManifest.xml @@ -72,5 +72,6 @@ <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.DEVICE_POWER" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> </manifest> diff --git a/data/fonts/DroidNaskh-Bold.ttf b/data/fonts/DroidNaskh-Bold.ttf Binary files differindex 692b796..14d8768 100644 --- a/data/fonts/DroidNaskh-Bold.ttf +++ b/data/fonts/DroidNaskh-Bold.ttf diff --git a/data/fonts/DroidNaskh-Regular.ttf b/data/fonts/DroidNaskh-Regular.ttf Binary files differindex da9a45f..03662f2 100644 --- a/data/fonts/DroidNaskh-Regular.ttf +++ b/data/fonts/DroidNaskh-Regular.ttf diff --git a/docs/html/guide/topics/ui/dialogs.jd b/docs/html/guide/topics/ui/dialogs.jd index 62c054a..3cfed13 100644 --- a/docs/html/guide/topics/ui/dialogs.jd +++ b/docs/html/guide/topics/ui/dialogs.jd @@ -119,7 +119,7 @@ onCreateDialog()} callback method.</p> a {@link android.support.v4.app.DialogFragment}:</p> <pre> -public class FireMissilesDialog extends DialogFragment { +public class FireMissilesDialogFragment extends DialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // Use the Builder class for convenient dialog construction @@ -469,7 +469,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { }) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { - NoticeDialog.this.getDialog().cancel(); + LoginDialogFragment.this.getDialog().cancel(); } }); return builder.create(); @@ -497,15 +497,15 @@ in the <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <p>When the user touches one of the dialog's action buttons or selects an item from its list, your {@link android.support.v4.app.DialogFragment} might perform the necessary action itself, but often you'll want to deliver the event to the activity or fragment that -opened the dialog. To do this, define an interface with a method for each type of click event, -then implement that interface in the host component that will +opened the dialog. To do this, define an interface with a method for each type of click event. +Then implement that interface in the host component that will receive the action events from the dialog.</p> <p>For example, here's a {@link android.support.v4.app.DialogFragment} that defines an interface through which it delivers the events back to the host activity:</p> <pre> -public class NoticeDialog extends DialogFragment { +public class NoticeDialogFragment extends DialogFragment { /* The activity that creates an instance of this dialog fragment must * implement this interface in order to receive event callbacks. @@ -516,48 +516,44 @@ public class NoticeDialog extends DialogFragment { } // Use this instance of the interface to deliver action events - static NoticeDialogListener mListener; - - /* Call this to instantiate a new NoticeDialog. - * @param activity The activity hosting the dialog, which must implement the - * NoticeDialogListener to receive event callbacks. - * @returns A new instance of NoticeDialog. - * @throws ClassCastException if the host activity does not - * implement NoticeDialogListener - */ - public static NoticeDialog newInstance(Activity activity) { + NoticeDialogListener mListener; + + // Override the Fragment.onAttach() method to instantiate the NoticeDialogListener + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); // Verify that the host activity implements the callback interface try { - // Instantiate the NoticeDialogListener so we can send events with it + // Instantiate the NoticeDialogListener so we can send events to the host mListener = (NoticeDialogListener) activity; } catch (ClassCastException e) { // The activity doesn't implement the interface, throw exception throw new ClassCastException(activity.toString() + " must implement NoticeDialogListener"); } - NoticeDialog frag = new NoticeDialog(); - return frag; } - ... } </pre> -<p>The activity hosting the dialog creates and shows an instance of the dialog -by calling {@code NoticeDialog.newInstance()} and receives the dialog's +<p>The activity hosting the dialog creates an instance of the dialog +with the dialog fragment's constructor and receives the dialog's events through an implementation of the {@code NoticeDialogListener} interface:</p> <pre> public class MainActivity extends FragmentActivity - implements NoticeDialog.NoticeDialogListener{ + implements NoticeDialogFragment.NoticeDialogListener{ ... public void showNoticeDialog() { // Create an instance of the dialog fragment and show it - DialogFragment dialog = NoticeDialog.newInstance(this); - dialog.show(getSupportFragmentManager(), "NoticeDialog"); + DialogFragment dialog = new NoticeDialogFragment(); + dialog.show(getSupportFragmentManager(), "NoticeDialogFragment"); } + // The dialog fragment receives a reference to this Activity through the + // Fragment.onAttach() callback, which it uses to call the following methods + // defined by the NoticeDialogFragment.NoticeDialogListener interface @Override public void onDialogPositiveClick(DialogFragment dialog) { // User touched the dialog's positive button @@ -573,11 +569,12 @@ public class MainActivity extends FragmentActivity </pre> <p>Because the host activity implements the {@code NoticeDialogListener}—which is -enforced by the {@code newInstance()} method shown above—the dialog fragment can use the +enforced by the {@link android.support.v4.app.Fragment#onAttach onAttach()} +callback method shown above—the dialog fragment can use the interface callback methods to deliver click events to the activity:</p> <pre> -public class NoticeDialog extends DialogFragment { +public class NoticeDialogFragment extends DialogFragment { ... @Override @@ -588,13 +585,13 @@ public class NoticeDialog extends DialogFragment { .setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { // Send the positive button event back to the host activity - mListener.onDialogPositiveClick(NoticeDialog.this); + mListener.onDialogPositiveClick(NoticeDialogFragment.this); } }) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { // Send the negative button event back to the host activity - mListener.onDialogPositiveClick(NoticeDialog.this); + mListener.onDialogPositiveClick(NoticeDialogFragment.this); } }); return builder.create(); @@ -604,8 +601,6 @@ public class NoticeDialog extends DialogFragment { - - <h2 id="ShowingADialog">Showing a Dialog</h2> <p>When you want to show your dialog, create an instance of your {@link @@ -621,7 +616,7 @@ android.support.v4.app.Fragment}. For example:</p> <pre> public void confirmFireMissiles() { - DialogFragment newFragment = FireMissilesDialog.newInstance(this); + DialogFragment newFragment = new FireMissilesDialogFragment(); newFragment.show(getSupportFragmentManager(), "missiles"); } </pre> @@ -653,7 +648,7 @@ onCreateView()} callback.</p> dialog or an embeddable fragment (using a layout named <code>purchase_items.xml</code>):</p> <pre> -public class CustomLayoutDialog extends DialogFragment { +public class CustomDialogFragment extends DialogFragment { /** The system calls this to get the DialogFragment's layout, regardless of whether it's being displayed as a dialog or an embedded fragment. */ @Override @@ -683,7 +678,7 @@ or a fullscreen UI, based on the screen size:</p> <pre> public void showDialog() { FragmentManager fragmentManager = getSupportFragmentManager(); - CustomLayoutDialog newFragment = new CustomLayoutDialog(); + CustomDialogFragment newFragment = new CustomDialogFragment(); if (mIsLargeLayout) { // The device is using a large layout, so show the fragment as a dialog diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index 69be317..448e3da 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -209,6 +209,9 @@ struct Layer { } inline void allocateTexture(GLenum format, GLenum storage) { +#if DEBUG_LAYERS + ALOGD(" Allocate layer: %dx%d", getWidth(), getHeight()); +#endif glTexImage2D(renderTarget, 0, format, getWidth(), getHeight(), 0, format, storage, NULL); } diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 406d5e9..b6be5b3 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -895,12 +895,6 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) { float alpha = layer->getAlpha() / 255.0f; - mat4& transform = layer->getTransform(); - if (!transform.isIdentity()) { - save(0); - mSnapshot->transform->multiply(transform); - } - setupDraw(); if (layer->getRenderTarget() == GL_TEXTURE_2D) { setupDrawWithTexture(); @@ -937,10 +931,6 @@ void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) { glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); finishDrawTexture(); - - if (!transform.isIdentity()) { - restore(); - } } void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) { @@ -2792,12 +2782,24 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* pain return DrawGlInfo::kStatusDone; } + mat4* transform = NULL; + if (layer->isTextureLayer()) { + transform = &layer->getTransform(); + if (!transform->isIdentity()) { + save(0); + mSnapshot->transform->multiply(*transform); + } + } + Rect transformed; Rect clip; const bool rejected = quickRejectNoScissor(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(), transformed, clip); if (rejected) { + if (transform && !transform->isIdentity()) { + restore(); + } return DrawGlInfo::kStatusDone; } @@ -2858,6 +2860,10 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* pain } } + if (transform && !transform->isIdentity()) { + restore(); + } + return DrawGlInfo::kStatusDrew; } diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index 7fb86ee..10d112a 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -74,8 +74,6 @@ void TextureCache::init() { INIT_LOGD(" Maximum texture dimension is %d pixels", mMaxTextureSize); mDebugEnabled = readDebugLevel() & kDebugCaches; - - mHasNPot = false; //Caches::getInstance().extensions.hasNPot(); } /////////////////////////////////////////////////////////////////////////////// @@ -219,11 +217,15 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege return; } + // We could also enable mipmapping if both bitmap dimensions are powers + // of 2 but we'd have to deal with size changes. Let's keep this simple + const bool canMipMap = Caches::getInstance().extensions.hasNPot(); + // If the texture had mipmap enabled but not anymore, // force a glTexImage2D to discard the mipmap levels const bool resize = !regenerate || bitmap->width() != int(texture->width) || bitmap->height() != int(texture->height) || - (regenerate && mHasNPot && texture->mipMap && !bitmap->hasHardwareMipMap()); + (regenerate && canMipMap && texture->mipMap && !bitmap->hasHardwareMipMap()); if (!regenerate) { glGenTextures(1, &texture->id); @@ -267,7 +269,7 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege break; } - if (mHasNPot) { + if (canMipMap) { texture->mipMap = bitmap->hasHardwareMipMap(); if (texture->mipMap) { glGenerateMipmap(GL_TEXTURE_2D); diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index 8e19092..31a2e3d 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -138,7 +138,6 @@ private: float mFlushRate; - bool mHasNPot; bool mDebugEnabled; Vector<SkBitmap*> mGarbage; diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java index cb291ea..6871ee2 100644 --- a/location/java/android/location/LocationRequest.java +++ b/location/java/android/location/LocationRequest.java @@ -221,6 +221,18 @@ public final class LocationRequest implements Parcelable { /** @hide */ public LocationRequest() { } + /** @hide */ + public LocationRequest(LocationRequest src) { + mQuality = src.mQuality; + mInterval = src.mInterval; + mFastestInterval = src.mFastestInterval; + mExplicitFastestInterval = src.mExplicitFastestInterval; + mExpireAt = src.mExpireAt; + mNumUpdates = src.mNumUpdates; + mSmallestDisplacement = src.mSmallestDisplacement; + mProvider = src.mProvider; + } + /** * Set the quality of the request. * diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java index 3464924..1c60401 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java @@ -117,6 +117,7 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med } mMemWriter = new BufferedWriter(new FileWriter (new File(MEDIA_MEMORY_OUTPUT), true)); + mMemWriter.write(this.getName() + "\n"); } @Override diff --git a/packages/FusedLocation/AndroidManifest.xml b/packages/FusedLocation/AndroidManifest.xml index 4c57401..10b9064 100644 --- a/packages/FusedLocation/AndroidManifest.xml +++ b/packages/FusedLocation/AndroidManifest.xml @@ -18,7 +18,8 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.location.fused" - coreApp="true"> + coreApp="true" + android:sharedUserId="android.uid.system"> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> @@ -39,7 +40,7 @@ <intent-filter> <action android:name="com.android.location.service.FusedLocationProvider" /> </intent-filter> - <meta-data android:name="version" android:value="1" /> + <meta-data android:name="serviceVersion" android:value="0" /> </service> </application> </manifest> diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml index 5a5769b..f7b1d78 100644 --- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml +++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml @@ -21,6 +21,8 @@ <ImageView android:id="@+id/brightness_icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:paddingRight="10dp" android:src="@drawable/ic_qs_brightness_auto_off" diff --git a/packages/SystemUI/res/layout/system_bar_notification_panel_title.xml b/packages/SystemUI/res/layout/system_bar_notification_panel_title.xml index 2d1bda4..59544f4 100644 --- a/packages/SystemUI/res/layout/system_bar_notification_panel_title.xml +++ b/packages/SystemUI/res/layout/system_bar_notification_panel_title.xml @@ -170,7 +170,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_sysbar_quicksettings" - android:contentDescription="@string/accessibility_settings_button" + android:contentDescription="@string/accessibility_desc_quick_settings" /> <ImageView diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index 0958f70..0a7dd7c 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -109,7 +109,7 @@ public class ImageWallpaper extends WallpaperService { private WallpaperObserver mReceiver; Bitmap mBackground; - int mBackgroundWidth = -1, mBackgroundHeight = -1; + int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1; int mLastRotation = -1; float mXOffset; float mYOffset; @@ -156,7 +156,7 @@ public class ImageWallpaper extends WallpaperService { } synchronized (mLock) { - mBackgroundWidth = mBackgroundHeight = -1; + mLastSurfaceWidth = mLastSurfaceHeight = -1; mBackground = null; mRedrawNeeded = true; drawFrameLocked(); @@ -172,6 +172,9 @@ public class ImageWallpaper extends WallpaperService { public void trimMemory(int level) { if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW && mBackground != null && mIsHwAccelerated) { + if (DEBUG) { + Log.d(TAG, "trimMemory"); + } mBackground.recycle(); mBackground = null; mWallpaperManager.forgetLoadedWallpaper(); @@ -286,13 +289,13 @@ public class ImageWallpaper extends WallpaperService { @Override public void onSurfaceDestroyed(SurfaceHolder holder) { super.onSurfaceDestroyed(holder); - mBackgroundWidth = mBackgroundHeight = -1; + mLastSurfaceWidth = mLastSurfaceHeight = -1; } @Override public void onSurfaceCreated(SurfaceHolder holder) { super.onSurfaceCreated(holder); - mBackgroundWidth = mBackgroundHeight = -1; + mLastSurfaceWidth = mLastSurfaceHeight = -1; } @Override @@ -314,9 +317,9 @@ public class ImageWallpaper extends WallpaperService { final int dh = frame.height(); int newRotation = ((WindowManager) getSystemService(WINDOW_SERVICE)). getDefaultDisplay().getRotation(); + boolean surfaceDimensionsChanged = dw != mLastSurfaceWidth || dh != mLastSurfaceHeight; - boolean redrawNeeded = dw != mBackgroundWidth || dh != mBackgroundHeight || - newRotation != mLastRotation; + boolean redrawNeeded = surfaceDimensionsChanged || newRotation != mLastRotation; if (!redrawNeeded && !mOffsetsChanged) { if (DEBUG) { Log.d(TAG, "Suppressed drawFrame since redraw is not needed " @@ -327,21 +330,41 @@ public class ImageWallpaper extends WallpaperService { mLastRotation = newRotation; // Load bitmap if it is not yet loaded or if it was loaded at a different size - if (mBackground == null || dw != mBackgroundWidth || dw != mBackgroundHeight) { + if (mBackground == null || surfaceDimensionsChanged) { if (DEBUG) { - Log.d(TAG, "Reloading bitmap"); + Log.d(TAG, "Reloading bitmap: mBackground, bgw, bgh, dw, dh = " + + mBackground + ", " + + ((mBackground == null) ? 0 : mBackground.getWidth()) + ", " + + ((mBackground == null) ? 0 : mBackground.getHeight()) + ", " + + dw + ", " + dh); } - mWallpaperManager.forgetLoadedWallpaper(); updateWallpaperLocked(); + if (mBackground == null) { + if (DEBUG) { + Log.d(TAG, "Unable to load bitmap"); + } + return; + } + if (DEBUG) { + if (dw != mBackground.getWidth() || dh != mBackground.getHeight()) { + Log.d(TAG, "Surface != bitmap dimensions: surface w/h, bitmap w/h: " + + dw + ", " + dh + ", " + mBackground.getWidth() + ", " + + mBackground.getHeight()); + } + } } - final int availw = dw - mBackgroundWidth; - final int availh = dh - mBackgroundHeight; + final int availw = dw - mBackground.getWidth(); + final int availh = dh - mBackground.getHeight(); int xPixels = availw < 0 ? (int)(availw * mXOffset + .5f) : (availw / 2); int yPixels = availh < 0 ? (int)(availh * mYOffset + .5f) : (availh / 2); mOffsetsChanged = false; mRedrawNeeded = false; + if (surfaceDimensionsChanged) { + mLastSurfaceWidth = dw; + mLastSurfaceHeight = dh; + } mLastXTranslation = xPixels; mLastYTranslation = yPixels; if (!redrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) { @@ -374,9 +397,10 @@ public class ImageWallpaper extends WallpaperService { } - void updateWallpaperLocked() { + private void updateWallpaperLocked() { Throwable exception = null; try { + mWallpaperManager.forgetLoadedWallpaper(); // force reload mBackground = mWallpaperManager.getBitmap(); } catch (RuntimeException e) { exception = e; @@ -397,9 +421,6 @@ public class ImageWallpaper extends WallpaperService { Log.w(TAG, "Unable reset to default wallpaper!", ex); } } - - mBackgroundWidth = mBackground != null ? mBackground.getWidth() : 0; - mBackgroundHeight = mBackground != null ? mBackground.getHeight() : 0; } private void drawWallpaperWithCanvas(SurfaceHolder sh, int w, int h, int x, int y) { @@ -413,7 +434,8 @@ public class ImageWallpaper extends WallpaperService { c.translate(x, y); if (w < 0 || h < 0) { c.save(Canvas.CLIP_SAVE_FLAG); - c.clipRect(0, 0, mBackgroundWidth, mBackgroundHeight, Op.DIFFERENCE); + c.clipRect(0, 0, mBackground.getWidth(), mBackground.getHeight(), + Op.DIFFERENCE); c.drawColor(0xff000000); c.restore(); } @@ -429,8 +451,8 @@ public class ImageWallpaper extends WallpaperService { private boolean drawWallpaperWithOpenGL(SurfaceHolder sh, int w, int h, int left, int top) { if (!initGL(sh)) return false; - final float right = left + mBackgroundWidth; - final float bottom = top + mBackgroundHeight; + final float right = left + mBackground.getWidth(); + final float bottom = top + mBackground.getHeight(); final Rect frame = sh.getSurfaceFrame(); final Matrix4f ortho = new Matrix4f(); diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java index 57d2ed3..9f0bcf5 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java @@ -185,6 +185,16 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener final Activity activity = (Activity) RecentsPanelView.this.getContext(); final SystemUIApplication app = (SystemUIApplication) activity.getApplication(); if (app.isWaitingForWindowAnimationStart()) { + if (mItemToAnimateInWhenWindowAnimationIsFinished != null) { + for (View v : + new View[] { holder.iconView, holder.labelView, holder.calloutLine }) { + if (v != null) { + v.setAlpha(1f); + v.setTranslationX(0f); + v.setTranslationY(0f); + } + } + } mItemToAnimateInWhenWindowAnimationIsFinished = holder; final int translation = -getResources().getDimensionPixelSize( R.dimen.status_bar_recents_app_icon_translate_distance); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index c832fb8..248a516 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -21,6 +21,8 @@ import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.util.AttributeSet; +import android.util.Slog; +import android.view.MotionEvent; import android.view.View; import com.android.systemui.R; @@ -31,11 +33,18 @@ public class NotificationPanelView extends PanelView { Drawable mHandleBar; float mHandleBarHeight; View mHandleView; + int mFingers; + PhoneStatusBar mStatusBar; + boolean mOkToFlip; public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); } + public void setStatusBar(PhoneStatusBar bar) { + mStatusBar = bar; + } + @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -79,4 +88,35 @@ public class NotificationPanelView extends PanelView { mHandleBar.draw(canvas); canvas.translate(0, -off); } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (PhoneStatusBar.SETTINGS_DRAG_SHORTCUT && mStatusBar.mHasFlipSettings) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + mOkToFlip = getExpandedHeight() == 0; + break; + case MotionEvent.ACTION_POINTER_DOWN: + if (mOkToFlip) { + float miny = event.getY(0); + float maxy = miny; + for (int i=1; i<event.getPointerCount(); i++) { + final float y = event.getY(i); + if (y < miny) miny = y; + if (y > maxy) maxy = y; + } + if (maxy - miny < mHandleBarHeight) { + if (getMeasuredHeight() < mHandleBarHeight) { + mStatusBar.switchToSettings(); + } else { + mStatusBar.flipToSettings(); + } + mOkToFlip = false; + } + } + break; + } + } + return mHandleView.dispatchTouchEvent(event); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java index d0fc340..c9a137c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java @@ -102,7 +102,7 @@ public class PanelBar extends FrameLayout { startOpeningPanel(panel); } final boolean result = mTouchingPanel != null - ? mTouchingPanel.getHandle().dispatchTouchEvent(event) + ? mTouchingPanel.onTouchEvent(event) : true; return result; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index b2bca56..6184e30 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -250,6 +250,7 @@ public class PanelView extends FrameLayout { case MotionEvent.ACTION_DOWN: mTracking = true; mHandleView.setPressed(true); + postInvalidate(); // catch the press state change mInitialTouchY = y; mVelocityTracker = VelocityTracker.obtain(); trackMovement(event); @@ -283,6 +284,7 @@ public class PanelView extends FrameLayout { mFinalTouchY = y; mTracking = false; mHandleView.setPressed(false); + postInvalidate(); // catch the press state change mBar.onTrackingStopped(PanelView.this); trackMovement(event); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 5a9d12e..1c4dff8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -110,6 +110,8 @@ public class PhoneStatusBar extends BaseStatusBar { public static final boolean ENABLE_NOTIFICATION_PANEL_CLING = false; + public static final boolean SETTINGS_DRAG_SHORTCUT = true; + // additional instrumentation for testing purposes; intended to be left on during development public static final boolean CHATTY = DEBUG; @@ -180,7 +182,7 @@ public class PhoneStatusBar extends BaseStatusBar { View mMoreIcon; // expanded notifications - PanelView mNotificationPanel; // the sliding/resizing panel within the notification window + NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window ScrollView mScrollView; View mExpandedContents; int mNotificationPanelGravity; @@ -363,7 +365,8 @@ public class PhoneStatusBar extends BaseStatusBar { PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder); mStatusBarView.setPanelHolder(holder); - mNotificationPanel = (PanelView) mStatusBarWindow.findViewById(R.id.notification_panel); + mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(R.id.notification_panel); + mNotificationPanel.setStatusBar(this); mNotificationPanelIsFullScreenWidth = (mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT); @@ -1081,8 +1084,9 @@ public class PhoneStatusBar extends BaseStatusBar { if (mHasFlipSettings && mFlipSettingsView != null - && mFlipSettingsView.getVisibility() == View.VISIBLE) { - // the flip settings panel is showing; we should not be shown + && mFlipSettingsView.getVisibility() == View.VISIBLE + && mScrollView.getVisibility() != View.VISIBLE) { + // the flip settings panel is unequivocally showing; we should not be shown mClearButton.setVisibility(View.INVISIBLE); } else if (mClearButton.isShown()) { if (clearable != (mClearButton.getAlpha() == 1.0f)) { @@ -1285,6 +1289,10 @@ public class PhoneStatusBar extends BaseStatusBar { } } + public Handler getHandler() { + return mHandler; + } + View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() { public void onFocusChange(View v, boolean hasFocus) { // Because 'v' is a ViewGroup, all its children will be (un)selected @@ -1299,18 +1307,6 @@ public class PhoneStatusBar extends BaseStatusBar { return; } - if (mHasFlipSettings && !mExpandedVisible) { - // reset things to their proper state - mScrollView.setScaleX(1f); - mScrollView.setVisibility(View.VISIBLE); - mSettingsButton.setAlpha(1f); - mSettingsButton.setVisibility(View.VISIBLE); - mNotificationPanel.setVisibility(View.GONE); - mFlipSettingsView.setVisibility(View.GONE); - mNotificationButton.setVisibility(View.GONE); - setAreThereNotifications(); // show the clear button - } - mExpandedVisible = true; mPile.setLayoutTransitionsEnabled(true); if (mNavigationBarView != null) @@ -1420,51 +1416,53 @@ public class PhoneStatusBar extends BaseStatusBar { } mNotificationPanel.expand(); - if (mHasFlipSettings) { - if (mScrollView.getVisibility() != View.VISIBLE) { - if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel(); - if (mScrollViewAnim != null) mScrollViewAnim.cancel(); - if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel(); - if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel(); - if (mClearButtonAnim != null) mClearButtonAnim.cancel(); - - mScrollView.setVisibility(View.VISIBLE); - mScrollViewAnim = start( - startDelay(FLIP_DURATION_OUT, - interpolator(mDecelerateInterpolator, - ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 0f, 1f) - .setDuration(FLIP_DURATION_IN) - ))); - mFlipSettingsViewAnim = start( - setVisibilityWhenDone( - interpolator(mAccelerateInterpolator, - ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 1f, 0f) - ) - .setDuration(FLIP_DURATION_OUT), - mFlipSettingsView, View.INVISIBLE)); - mNotificationButtonAnim = start( - setVisibilityWhenDone( - ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 0f) - .setDuration(FLIP_DURATION), - mNotificationButton, View.INVISIBLE)); - mSettingsButton.setVisibility(View.VISIBLE); - mSettingsButtonAnim = start( - ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 1f) - .setDuration(FLIP_DURATION)); - mClearButton.setVisibility(View.VISIBLE); - mClearButton.setAlpha(0f); - setAreThereNotifications(); // this will show/hide the button as necessary - mNotificationPanel.postDelayed(new Runnable() { - public void run() { - updateCarrierLabelVisibility(false); - } - }, FLIP_DURATION - 150); - } + if (mHasFlipSettings && mScrollView.getVisibility() != View.VISIBLE) { + flipToNotifications(); } if (false) postStartTracing(); } + public void flipToNotifications() { + if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel(); + if (mScrollViewAnim != null) mScrollViewAnim.cancel(); + if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel(); + if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel(); + if (mClearButtonAnim != null) mClearButtonAnim.cancel(); + + mScrollView.setVisibility(View.VISIBLE); + mScrollViewAnim = start( + startDelay(FLIP_DURATION_OUT, + interpolator(mDecelerateInterpolator, + ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 0f, 1f) + .setDuration(FLIP_DURATION_IN) + ))); + mFlipSettingsViewAnim = start( + setVisibilityWhenDone( + interpolator(mAccelerateInterpolator, + ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 1f, 0f) + ) + .setDuration(FLIP_DURATION_OUT), + mFlipSettingsView, View.INVISIBLE)); + mNotificationButtonAnim = start( + setVisibilityWhenDone( + ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 0f) + .setDuration(FLIP_DURATION), + mNotificationButton, View.INVISIBLE)); + mSettingsButton.setVisibility(View.VISIBLE); + mSettingsButtonAnim = start( + ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 1f) + .setDuration(FLIP_DURATION)); + mClearButton.setVisibility(View.VISIBLE); + mClearButton.setAlpha(0f); + setAreThereNotifications(); // this will show/hide the button as necessary + mNotificationPanel.postDelayed(new Runnable() { + public void run() { + updateCarrierLabelVisibility(false); + } + }, FLIP_DURATION - 150); + } + @Override public void animateExpandSettingsPanel() { if (SPEW) Slog.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible); @@ -1475,46 +1473,7 @@ public class PhoneStatusBar extends BaseStatusBar { if (mHasFlipSettings) { mNotificationPanel.expand(); if (mFlipSettingsView.getVisibility() != View.VISIBLE) { - if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel(); - if (mScrollViewAnim != null) mScrollViewAnim.cancel(); - if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel(); - if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel(); - if (mClearButtonAnim != null) mClearButtonAnim.cancel(); - - mFlipSettingsView.setVisibility(View.VISIBLE); - mFlipSettingsView.setScaleX(0f); - mFlipSettingsViewAnim = start( - startDelay(FLIP_DURATION_OUT, - interpolator(mDecelerateInterpolator, - ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 0f, 1f) - .setDuration(FLIP_DURATION_IN) - ))); - mScrollViewAnim = start( - setVisibilityWhenDone( - interpolator(mAccelerateInterpolator, - ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 1f, 0f) - ) - .setDuration(FLIP_DURATION_OUT), - mScrollView, View.INVISIBLE)); - mSettingsButtonAnim = start( - setVisibilityWhenDone( - ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 0f) - .setDuration(FLIP_DURATION), - mScrollView, View.INVISIBLE)); - mNotificationButton.setVisibility(View.VISIBLE); - mNotificationButtonAnim = start( - ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 1f) - .setDuration(FLIP_DURATION)); - mClearButtonAnim = start( - setVisibilityWhenDone( - ObjectAnimator.ofFloat(mClearButton, View.ALPHA, 0f) - .setDuration(FLIP_DURATION), - mClearButton, View.INVISIBLE)); - mNotificationPanel.postDelayed(new Runnable() { - public void run() { - updateCarrierLabelVisibility(false); - } - }, FLIP_DURATION - 150); + flipToSettings(); } } else if (mSettingsPanel != null) { mSettingsPanel.expand(); @@ -1523,6 +1482,70 @@ public class PhoneStatusBar extends BaseStatusBar { if (false) postStartTracing(); } + public void switchToSettings() { + mFlipSettingsView.setScaleX(1f); + mFlipSettingsView.setVisibility(View.VISIBLE); + mSettingsButton.setVisibility(View.GONE); + mScrollView.setVisibility(View.GONE); + mScrollView.setScaleX(0f); + mNotificationButton.setVisibility(View.VISIBLE); + mNotificationButton.setAlpha(1f); + mClearButton.setVisibility(View.GONE); + } + + public void flipToSettings() { + if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel(); + if (mScrollViewAnim != null) mScrollViewAnim.cancel(); + if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel(); + if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel(); + if (mClearButtonAnim != null) mClearButtonAnim.cancel(); + + mFlipSettingsView.setVisibility(View.VISIBLE); + mFlipSettingsView.setScaleX(0f); + mFlipSettingsViewAnim = start( + startDelay(FLIP_DURATION_OUT, + interpolator(mDecelerateInterpolator, + ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 0f, 1f) + .setDuration(FLIP_DURATION_IN) + ))); + mScrollViewAnim = start( + setVisibilityWhenDone( + interpolator(mAccelerateInterpolator, + ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 1f, 0f) + ) + .setDuration(FLIP_DURATION_OUT), + mScrollView, View.INVISIBLE)); + mSettingsButtonAnim = start( + setVisibilityWhenDone( + ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 0f) + .setDuration(FLIP_DURATION), + mScrollView, View.INVISIBLE)); + mNotificationButton.setVisibility(View.VISIBLE); + mNotificationButtonAnim = start( + ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 1f) + .setDuration(FLIP_DURATION)); + mClearButtonAnim = start( + setVisibilityWhenDone( + ObjectAnimator.ofFloat(mClearButton, View.ALPHA, 0f) + .setDuration(FLIP_DURATION), + mClearButton, View.INVISIBLE)); + mNotificationPanel.postDelayed(new Runnable() { + public void run() { + updateCarrierLabelVisibility(false); + } + }, FLIP_DURATION - 150); + } + + public void flipPanels() { + if (mHasFlipSettings) { + if (mFlipSettingsView.getVisibility() != View.VISIBLE) { + flipToSettings(); + } else { + flipToNotifications(); + } + } + } + public void animateCollapseQuickSettings() { mStatusBarView.collapseAllPanels(true); } @@ -1542,6 +1565,18 @@ public class PhoneStatusBar extends BaseStatusBar { // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868) mStatusBarView.collapseAllPanels(/*animate=*/ false); + if (mHasFlipSettings) { + // reset things to their proper state + mScrollView.setScaleX(1f); + mScrollView.setVisibility(View.VISIBLE); + mSettingsButton.setAlpha(1f); + mSettingsButton.setVisibility(View.VISIBLE); + mNotificationPanel.setVisibility(View.GONE); + mFlipSettingsView.setVisibility(View.GONE); + mNotificationButton.setVisibility(View.GONE); + setAreThereNotifications(); // show the clear button + } + mExpandedVisible = false; mPile.setLayoutTransitionsEnabled(false); if (mNavigationBarView != null) diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 91d5eaa..24ce9bc 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -299,6 +299,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mCarDockRotation; int mDeskDockRotation; int mHdmiRotation; + boolean mHdmiRotationLock; int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE; int mUserRotation = Surface.ROTATION_0; @@ -1035,11 +1036,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { mCanHideNavigationBar = false; } + // For demo purposes, allow the rotation of the HDMI display to be controlled. + // By default, HDMI locks rotation to landscape. if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) { mHdmiRotation = mPortraitRotation; } else { mHdmiRotation = mLandscapeRotation; } + mHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", true); } public void updateSettings() { @@ -3873,7 +3877,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // enable 180 degree rotation while docked. preferredRotation = mDeskDockEnablesAccelerometer ? sensorRotation : mDeskDockRotation; - } else if (mHdmiPlugged) { + } else if (mHdmiPlugged && mHdmiRotationLock) { // Ignore sensor when plugged into HDMI. // Note that the dock orientation overrides the HDMI orientation. preferredRotation = mHdmiRotation; @@ -4538,5 +4542,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(" mSeascapeRotation="); pw.println(mSeascapeRotation); pw.print(prefix); pw.print("mPortraitRotation="); pw.print(mPortraitRotation); pw.print(" mUpsideDownRotation="); pw.println(mUpsideDownRotation); + pw.print(prefix); pw.print("mHdmiRotation="); pw.print(mHdmiRotation); + pw.print(" mHdmiRotationLock="); pw.println(mHdmiRotationLock); } } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java index 0ad2404..b66c883 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java @@ -43,7 +43,7 @@ import com.android.internal.widget.LockPatternUtils; /** * Manages creating, showing, hiding and resetting the keyguard. Calls back - * via {@link com.android.internal.policy.impl.KeyguardViewCallback} to poke + * via {@link KeyguardViewMediator.ViewMediatorCallback} to poke * the wake lock and report that the keyguard is done, which is in turn, * reported to this class by the current {@link KeyguardViewBase}. */ @@ -233,6 +233,7 @@ public class KeyguardViewManager { if (mScreenOn) { mKeyguardView.show(); + mKeyguardView.requestFocus(); } } @@ -314,22 +315,25 @@ public class KeyguardViewManager { // Caller should wait for this window to be shown before turning // on the screen. - if (mKeyguardHost.getVisibility() == View.VISIBLE) { - // Keyguard may be in the process of being shown, but not yet - // updated with the window manager... give it a chance to do so. - mKeyguardHost.post(new Runnable() { - public void run() { - if (mKeyguardHost.getVisibility() == View.VISIBLE) { - showListener.onShown(mKeyguardHost.getWindowToken()); - } else { - showListener.onShown(null); + if (showListener != null) { + if (mKeyguardHost.getVisibility() == View.VISIBLE) { + // Keyguard may be in the process of being shown, but not yet + // updated with the window manager... give it a chance to do so. + mKeyguardHost.post(new Runnable() { + @Override + public void run() { + if (mKeyguardHost.getVisibility() == View.VISIBLE) { + showListener.onShown(mKeyguardHost.getWindowToken()); + } else { + showListener.onShown(null); + } } - } - }); - } else { - showListener.onShown(null); + }); + } else { + showListener.onShown(null); + } } - } else { + } else if (showListener != null) { showListener.onShown(null); } } @@ -356,10 +360,9 @@ public class KeyguardViewManager { if (mKeyguardView != null) { mKeyguardView.wakeWhenReadyTq(keyCode); return true; - } else { - Log.w(TAG, "mKeyguardView is null in wakeWhenReadyTq"); - return false; } + Log.w(TAG, "mKeyguardView is null in wakeWhenReadyTq"); + return false; } /** @@ -382,6 +385,7 @@ public class KeyguardViewManager { final KeyguardViewBase lastView = mKeyguardView; mKeyguardView = null; mKeyguardHost.postDelayed(new Runnable() { + @Override public void run() { synchronized (KeyguardViewManager.this) { lastView.cleanUp(); diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java index 92f9dfd..ceb0325 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java @@ -629,9 +629,7 @@ public class KeyguardViewMediator { mScreenOn = true; cancelDoKeyguardLaterLocked(); if (DEBUG) Log.d(TAG, "onScreenTurnedOn, seq = " + mDelayedShowingSequence); - if (showListener != null) { - notifyScreenOnLocked(showListener); - } + notifyScreenOnLocked(showListener); } maybeSendUserPresentBroadcast(); } @@ -1365,7 +1363,7 @@ public class KeyguardViewMediator { /** * Handle message sent by {@link #verifyUnlock} - * @see #RESET + * @see #VERIFY_UNLOCK */ private void handleVerifyUnlock() { synchronized (KeyguardViewMediator.this) { diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 9f01eca..f241c80 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -1474,6 +1474,7 @@ class BackupManagerService extends IBackupManager.Stub { if (MORE_DEBUG) Slog.v(TAG, " removing participant " + packageName); removeEverBackedUp(packageName); set.remove(packageName); + mPendingBackups.remove(packageName); } } @@ -1625,6 +1626,7 @@ class BackupManagerService extends IBackupManager.Stub { } catch (InterruptedException e) { // just bail if (DEBUG) Slog.w(TAG, "Interrupted: " + e); + mActivityManager.clearPendingBackup(); return null; } } @@ -1632,6 +1634,7 @@ class BackupManagerService extends IBackupManager.Stub { // if we timed out with no connect, abort and move on if (mConnecting == true) { Slog.w(TAG, "Timeout waiting for agent " + app); + mActivityManager.clearPendingBackup(); return null; } if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent); diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index 63eeeb3..37dee19 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -23,8 +23,11 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; 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; +import android.content.pm.Signature; import android.content.res.Resources; import android.database.ContentObserver; import android.location.Address; @@ -90,10 +93,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run private static final String WAKELOCK_KEY = TAG; private static final String THREAD_NAME = TAG; - private static final String ACCESS_FINE_LOCATION = - android.Manifest.permission.ACCESS_FINE_LOCATION; - private static final String ACCESS_COARSE_LOCATION = - android.Manifest.permission.ACCESS_COARSE_LOCATION; + // Location resolution level: no location data whatsoever + private static final int RESOLUTION_LEVEL_NONE = 0; + // Location resolution level: coarse location data only + private static final int RESOLUTION_LEVEL_COARSE = 1; + // Location resolution level: fine location data + private static final int RESOLUTION_LEVEL_FINE = 2; + private static final String ACCESS_MOCK_LOCATION = android.Manifest.permission.ACCESS_MOCK_LOCATION; private static final String ACCESS_LOCATION_EXTRA_COMMANDS = @@ -246,6 +252,74 @@ public class LocationManagerService extends ILocationManager.Stub implements Run updateProvidersLocked(); } + private void ensureFallbackFusedProviderPresentLocked(ArrayList<String> pkgs) { + PackageManager pm = mContext.getPackageManager(); + String systemPackageName = mContext.getPackageName(); + ArrayList<HashSet<Signature>> sigSets = ServiceWatcher.getSignatureSets(mContext, pkgs); + + List<ResolveInfo> rInfos = pm.queryIntentServicesAsUser( + new Intent(FUSED_LOCATION_SERVICE_ACTION), + PackageManager.GET_META_DATA, mCurrentUserId); + for (ResolveInfo rInfo : rInfos) { + String packageName = rInfo.serviceInfo.packageName; + + // Check that the signature is in the list of supported sigs. If it's not in + // this list the standard provider binding logic won't bind to it. + try { + PackageInfo pInfo; + pInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + if (!ServiceWatcher.isSignatureMatch(pInfo.signatures, sigSets)) { + Log.w(TAG, packageName + " resolves service " + FUSED_LOCATION_SERVICE_ACTION + + ", but has wrong signature, ignoring"); + continue; + } + } catch (NameNotFoundException e) { + Log.e(TAG, "missing package: " + packageName); + continue; + } + + // Get the version info + if (rInfo.serviceInfo.metaData == null) { + Log.w(TAG, "Found fused provider without metadata: " + packageName); + continue; + } + + int version = rInfo.serviceInfo.metaData.getInt( + ServiceWatcher.EXTRA_SERVICE_VERSION, -1); + if (version == 0) { + // This should be the fallback fused location provider. + + // Make sure it's in the system partition. + if ((rInfo.serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { + if (D) Log.d(TAG, "Fallback candidate not in /system: " + packageName); + continue; + } + + // Check that the fallback is signed the same as the OS + // as a proxy for coreApp="true" + if (pm.checkSignatures(systemPackageName, packageName) + != PackageManager.SIGNATURE_MATCH) { + if (D) Log.d(TAG, "Fallback candidate not signed the same as system: " + + packageName); + continue; + } + + // Found a valid fallback. + if (D) Log.d(TAG, "Found fallback provider: " + packageName); + return; + } else { + if (D) Log.d(TAG, "Fallback candidate not version 0: " + packageName); + } + } + + throw new IllegalStateException("Unable to find a fused location provider that is in the " + + "system partition with version 0 and signed with the platform certificate. " + + "Such a package is needed to provide a default fused location provider in the " + + "event that no other fused location provider has been installed or is currently " + + "available. For example, coreOnly boot mode when decrypting the data " + + "partition. The fallback must also be marked coreApp=\"true\" in the manifest"); + } + private void loadProvidersLocked() { // create a passive location provider, which is always enabled PassiveProvider passiveProvider = new PassiveProvider(this); @@ -275,14 +349,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run */ Resources resources = mContext.getResources(); ArrayList<String> providerPackageNames = new ArrayList<String>(); - String[] pkgs1 = resources.getStringArray( + String[] pkgs = resources.getStringArray( com.android.internal.R.array.config_locationProviderPackageNames); - String[] pkgs2 = resources.getStringArray( - com.android.internal.R.array.config_overlay_locationProviderPackageNames); - if (D) Log.d(TAG, "built-in location providers: " + Arrays.toString(pkgs1)); - if (D) Log.d(TAG, "overlay location providers: " + Arrays.toString(pkgs2)); - if (pkgs1 != null) providerPackageNames.addAll(Arrays.asList(pkgs1)); - if (pkgs2 != null) providerPackageNames.addAll(Arrays.asList(pkgs2)); + if (D) Log.d(TAG, "certificates for location providers pulled from: " + + Arrays.toString(pkgs)); + if (pkgs != null) providerPackageNames.addAll(Arrays.asList(pkgs)); + + ensureFallbackFusedProviderPresentLocked(providerPackageNames); // bind to network provider LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind( @@ -347,7 +420,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run final int mUid; // uid of receiver final int mPid; // pid of receiver final String mPackageName; // package name of receiver - final String mPermission; // best permission that receiver has + final int mAllowedResolutionLevel; // resolution level allowed to receiver final ILocationListener mListener; final PendingIntent mPendingIntent; @@ -366,7 +439,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } else { mKey = intent; } - mPermission = checkPermission(); + mAllowedResolutionLevel = getAllowedResolutionLevel(pid, uid); mUid = uid; mPid = pid; mPackageName = packageName; @@ -440,7 +513,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler, - mPermission); + getResolutionPermission(mAllowedResolutionLevel)); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcastsLocked(); @@ -474,7 +547,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler, - mPermission); + getResolutionPermission(mAllowedResolutionLevel)); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcastsLocked(); @@ -512,7 +585,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // synchronize to ensure incrementPendingBroadcastsLocked() // is called before decrementPendingBroadcasts() mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler, - mPermission); + getResolutionPermission(mAllowedResolutionLevel)); // call this after broadcasting so we do not increment // if we throw an exeption. incrementPendingBroadcastsLocked(); @@ -609,51 +682,76 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } /** - * Returns the best permission available to the caller. + * Returns the permission string associated with the specified resolution level. + * + * @param resolutionLevel the resolution level + * @return the permission string */ - private String getBestCallingPermission() { - if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) == - PackageManager.PERMISSION_GRANTED) { - return ACCESS_FINE_LOCATION; - } else if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) == - PackageManager.PERMISSION_GRANTED) { - return ACCESS_COARSE_LOCATION; + private String getResolutionPermission(int resolutionLevel) { + switch (resolutionLevel) { + case RESOLUTION_LEVEL_FINE: + return android.Manifest.permission.ACCESS_FINE_LOCATION; + case RESOLUTION_LEVEL_COARSE: + return android.Manifest.permission.ACCESS_COARSE_LOCATION; + default: + return null; } - return null; } /** - * Throw SecurityException if caller has neither COARSE or FINE. - * Otherwise, return the best permission. + * Returns the resolution level allowed to the given PID/UID pair. + * + * @param pid the PID + * @param uid the UID + * @return resolution level allowed to the pid/uid pair */ - private String checkPermission() { - String perm = getBestCallingPermission(); - if (perm == null) { - throw new SecurityException("Location requires either ACCESS_COARSE_LOCATION or" + - " ACCESS_FINE_LOCATION permission"); + private int getAllowedResolutionLevel(int pid, int uid) { + if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, + pid, uid) == PackageManager.PERMISSION_GRANTED) { + return RESOLUTION_LEVEL_FINE; + } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION, + pid, uid) == PackageManager.PERMISSION_GRANTED) { + return RESOLUTION_LEVEL_COARSE; + } else { + return RESOLUTION_LEVEL_NONE; } - return perm; } /** - * Throw SecurityException if caller lacks permission to use Geofences. + * Returns the resolution level allowed to the caller + * + * @return resolution level allowed to caller + */ + private int getCallerAllowedResolutionLevel() { + return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid()); + } + + /** + * Throw SecurityException if specified resolution level is insufficient to use geofences. + * + * @param allowedResolutionLevel resolution level allowed to caller */ - private void checkGeofencePermission() { - if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) != - PackageManager.PERMISSION_GRANTED) { + private void checkResolutionLevelIsSufficientForGeofenceUse(int allowedResolutionLevel) { + if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) { throw new SecurityException("Geofence usage requires ACCESS_FINE_LOCATION permission"); } } - private String getMinimumPermissionForProvider(String provider) { + /** + * Return the minimum resolution level required to use the specified location provider. + * + * @param provider the name of the location provider + * @return minimum resolution level required for provider + */ + private int getMinimumResolutionLevelForProviderUse(String provider) { if (LocationManager.GPS_PROVIDER.equals(provider) || LocationManager.PASSIVE_PROVIDER.equals(provider)) { // gps and passive providers require FINE permission - return ACCESS_FINE_LOCATION; + return RESOLUTION_LEVEL_FINE; } else if (LocationManager.NETWORK_PROVIDER.equals(provider) || LocationManager.FUSED_PROVIDER.equals(provider)) { // network and fused providers are ok with COARSE or FINE - return ACCESS_COARSE_LOCATION; + return RESOLUTION_LEVEL_COARSE; } else { // mock providers LocationProviderInterface lp = mMockProviders.get(provider); @@ -662,41 +760,38 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if (properties != null) { if (properties.mRequiresSatellite) { // provider requiring satellites require FINE permission - return ACCESS_FINE_LOCATION; + return RESOLUTION_LEVEL_FINE; } else if (properties.mRequiresNetwork || properties.mRequiresCell) { // provider requiring network and or cell require COARSE or FINE - return ACCESS_COARSE_LOCATION; + return RESOLUTION_LEVEL_COARSE; } } } } - - return null; + return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE } - private boolean isPermissionSufficient(String perm, String minPerm) { - if (ACCESS_FINE_LOCATION.equals(minPerm)) { - return ACCESS_FINE_LOCATION.equals(perm); - } else if (ACCESS_COARSE_LOCATION.equals(minPerm)) { - return ACCESS_FINE_LOCATION.equals(perm) || - ACCESS_COARSE_LOCATION.equals(perm); - } else { - return false; - } - } - - private void checkPermissionForProvider(String perm, String provider) { - String minPerm = getMinimumPermissionForProvider(provider); - if (!isPermissionSufficient(perm, minPerm)) { - if (ACCESS_FINE_LOCATION.equals(minPerm)) { - throw new SecurityException("Location provider \"" + provider + - "\" requires ACCESS_FINE_LOCATION permission."); - } else if (ACCESS_COARSE_LOCATION.equals(minPerm)) { - throw new SecurityException("Location provider \"" + provider + - "\" requires ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission."); - } else { - throw new SecurityException("Insufficient permission for location provider \"" + - provider + "\"."); + /** + * Throw SecurityException if specified resolution level is insufficient to use the named + * location provider. + * + * @param allowedResolutionLevel resolution level allowed to caller + * @param providerName the name of the location provider + */ + private void checkResolutionLevelIsSufficientForProviderUse(int allowedResolutionLevel, + String providerName) { + int requiredResolutionLevel = getMinimumResolutionLevelForProviderUse(providerName); + if (allowedResolutionLevel < requiredResolutionLevel) { + switch (requiredResolutionLevel) { + case RESOLUTION_LEVEL_FINE: + throw new SecurityException("\"" + providerName + "\" location provider " + + "requires ACCESS_FINE_LOCATION permission."); + case RESOLUTION_LEVEL_COARSE: + throw new SecurityException("\"" + providerName + "\" location provider " + + "requires ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission."); + default: + throw new SecurityException("Insufficient permission for \"" + providerName + + "\" location provider."); } } } @@ -731,8 +826,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run */ @Override public List<String> getProviders(Criteria criteria, boolean enabledOnly) { + int allowedResolutionLevel = getCallerAllowedResolutionLevel(); ArrayList<String> out; - String perm = getBestCallingPermission(); int callingUserId = UserHandle.getCallingUserId(); long identity = Binder.clearCallingIdentity(); try { @@ -743,7 +838,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if (LocationManager.FUSED_PROVIDER.equals(name)) { continue; } - if (isPermissionSufficient(perm, getMinimumPermissionForProvider(name))) { + if (allowedResolutionLevel >= getMinimumResolutionLevelForProviderUse(name)) { if (enabledOnly && !isAllowedBySettingsLocked(name, callingUserId)) { continue; } @@ -803,8 +898,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run @Override public boolean providerMeetsCriteria(String provider, Criteria criteria) { - checkPermission(); - LocationProviderInterface p = mProvidersByName.get(provider); if (p == null) { throw new IllegalArgumentException("provider=" + provider); @@ -1010,33 +1103,41 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return receiver; } - private String checkPermissionAndRequest(LocationRequest request) { - String perm = getBestCallingPermission(); - String provider = request.getProvider(); - checkPermissionForProvider(perm, provider); - - if (ACCESS_COARSE_LOCATION.equals(perm)) { - switch (request.getQuality()) { + /** + * Creates a LocationRequest based upon the supplied LocationRequest that to meets resolution + * and consistency requirements. + * + * @param request the LocationRequest from which to create a sanitized version + * @param shouldBeCoarse whether the sanitized version should be held to coarse resolution + * constraints + * @param fastestCoarseIntervalMS minimum interval allowed for coarse resolution + * @return a version of request that meets the given resolution and consistency requirements + * @hide + */ + private LocationRequest createSanitizedRequest(LocationRequest request, int resolutionLevel) { + LocationRequest sanitizedRequest = new LocationRequest(request); + if (resolutionLevel < RESOLUTION_LEVEL_FINE) { + switch (sanitizedRequest.getQuality()) { case LocationRequest.ACCURACY_FINE: - request.setQuality(LocationRequest.ACCURACY_BLOCK); + sanitizedRequest.setQuality(LocationRequest.ACCURACY_BLOCK); break; case LocationRequest.POWER_HIGH: - request.setQuality(LocationRequest.POWER_LOW); + sanitizedRequest.setQuality(LocationRequest.POWER_LOW); break; } // throttle - if (request.getInterval() < LocationFudger.FASTEST_INTERVAL_MS) { - request.setInterval(LocationFudger.FASTEST_INTERVAL_MS); + if (sanitizedRequest.getInterval() < LocationFudger.FASTEST_INTERVAL_MS) { + sanitizedRequest.setInterval(LocationFudger.FASTEST_INTERVAL_MS); } - if (request.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) { - request.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS); + if (sanitizedRequest.getFastestInterval() < LocationFudger.FASTEST_INTERVAL_MS) { + sanitizedRequest.setFastestInterval(LocationFudger.FASTEST_INTERVAL_MS); } } // make getFastestInterval() the minimum of interval and fastest interval - if (request.getFastestInterval() > request.getInterval()) { + if (sanitizedRequest.getFastestInterval() > sanitizedRequest.getInterval()) { request.setFastestInterval(request.getInterval()); } - return perm; + return sanitizedRequest; } private void checkPackageName(String packageName) { @@ -1079,7 +1180,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run PendingIntent intent, String packageName) { if (request == null) request = DEFAULT_LOCATION_REQUEST; checkPackageName(packageName); - checkPermissionAndRequest(request); + int allowedResolutionLevel = getCallerAllowedResolutionLevel(); + checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel, + request.getProvider()); + LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel); final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); @@ -1089,7 +1193,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { - requestLocationUpdatesLocked(request, recevier, pid, uid, packageName); + requestLocationUpdatesLocked(sanitizedRequest, recevier, pid, uid, packageName); } } finally { Binder.restoreCallingIdentity(identity); @@ -1132,7 +1236,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run public void removeUpdates(ILocationListener listener, PendingIntent intent, String packageName) { checkPackageName(packageName); - checkPermission(); + final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); Receiver receiver = checkListenerOrIntent(listener, intent, pid, uid, packageName); @@ -1188,8 +1292,11 @@ public class LocationManagerService extends ILocationManager.Stub implements Run public Location getLastLocation(LocationRequest request, String packageName) { if (D) Log.d(TAG, "getLastLocation: " + request); if (request == null) request = DEFAULT_LOCATION_REQUEST; - String perm = checkPermissionAndRequest(request); + int allowedResolutionLevel = getCallerAllowedResolutionLevel(); checkPackageName(packageName); + checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel, + request.getProvider()); + // no need to sanitize this request, as only the provider name is used long identity = Binder.clearCallingIdentity(); try { @@ -1213,13 +1320,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if (location == null) { return null; } - if (ACCESS_FINE_LOCATION.equals(perm)) { - return location; - } else { + if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) { Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION); if (noGPSLocation != null) { return mLocationFudger.getOrCreate(noGPSLocation); } + } else { + return location; } } return null; @@ -1232,18 +1339,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent, String packageName) { if (request == null) request = DEFAULT_LOCATION_REQUEST; - checkGeofencePermission(); - checkPermissionAndRequest(request); + int allowedResolutionLevel = getCallerAllowedResolutionLevel(); + checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel); checkPendingIntent(intent); checkPackageName(packageName); + checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel, + request.getProvider()); + LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel); - if (D) Log.d(TAG, "requestGeofence: " + request + " " + geofence + " " + intent); + if (D) Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " " + intent); // geo-fence manager uses the public location API, need to clear identity int uid = Binder.getCallingUid(); long identity = Binder.clearCallingIdentity(); try { - mGeofenceManager.addFence(request, geofence, intent, uid, packageName); + mGeofenceManager.addFence(sanitizedRequest, geofence, intent, uid, packageName); } finally { Binder.restoreCallingIdentity(identity); } @@ -1251,7 +1361,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run @Override public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) { - checkGeofencePermission(); + checkResolutionLevelIsSufficientForGeofenceUse(getCallerAllowedResolutionLevel()); checkPendingIntent(intent); checkPackageName(packageName); @@ -1272,10 +1382,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if (mGpsStatusProvider == null) { return false; } - if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires ACCESS_FINE_LOCATION permission"); - } + checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(), + LocationManager.GPS_PROVIDER); try { mGpsStatusProvider.addGpsStatusListener(listener); @@ -1303,8 +1411,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run // throw NullPointerException to remain compatible with previous implementation throw new NullPointerException(); } + checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(), + provider); - checkPermission(); // and check for ACCESS_LOCATION_EXTRA_COMMANDS if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS) != PackageManager.PERMISSION_GRANTED)) { @@ -1344,7 +1453,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run return null; } - checkPermissionForProvider(getBestCallingPermission(), provider); + checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(), + provider); LocationProviderInterface p; synchronized (mLock) { @@ -1357,7 +1467,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run @Override public boolean isProviderEnabled(String provider) { - checkPermissionForProvider(getBestCallingPermission(), provider); + checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(), + provider); if (LocationManager.FUSED_PROVIDER.equals(provider)) return false; long identity = Binder.clearCallingIdentity(); @@ -1522,10 +1633,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run } Location notifyLocation = null; - if (ACCESS_FINE_LOCATION.equals(receiver.mPermission)) { - notifyLocation = lastLocation; // use fine location + if (receiver.mAllowedResolutionLevel < RESOLUTION_LEVEL_FINE) { + notifyLocation = coarseLocation; // use coarse location } else { - notifyLocation = coarseLocation; // use coarse location if available + notifyLocation = lastLocation; // use fine location } if (notifyLocation != null) { Location lastLoc = r.mLastFixBroadcast; diff --git a/services/java/com/android/server/ServiceWatcher.java b/services/java/com/android/server/ServiceWatcher.java index 5598b0a..2e7c6d1 100644 --- a/services/java/com/android/server/ServiceWatcher.java +++ b/services/java/com/android/server/ServiceWatcher.java @@ -43,7 +43,7 @@ import java.util.List; */ public class ServiceWatcher implements ServiceConnection { private static final boolean D = false; - private static final String EXTRA_VERSION = "version"; + public static final String EXTRA_SERVICE_VERSION = "serviceVersion"; private final String mTag; private final Context mContext; @@ -58,9 +58,27 @@ public class ServiceWatcher implements ServiceConnection { // all fields below synchronized on mLock private IBinder mBinder; // connected service private String mPackageName; // current best package - private int mVersion; // current best version + private int mVersion = Integer.MIN_VALUE; // current best version private int mCurrentUserId; + public static ArrayList<HashSet<Signature>> getSignatureSets(Context context, + List<String> initialPackageNames) { + PackageManager pm = context.getPackageManager(); + ArrayList<HashSet<Signature>> sigSets = new ArrayList<HashSet<Signature>>(); + for (int i = 0, size = initialPackageNames.size(); i < size; i++) { + String pkg = initialPackageNames.get(i); + try { + HashSet<Signature> set = new HashSet<Signature>(); + Signature[] sigs = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures; + set.addAll(Arrays.asList(sigs)); + sigSets.add(set); + } catch (NameNotFoundException e) { + Log.w("ServiceWatcher", pkg + " not found"); + } + } + return sigSets; + } + public ServiceWatcher(Context context, String logTag, String action, List<String> initialPackageNames, Runnable newServiceWork, Handler handler, int userId) { mContext = context; @@ -71,20 +89,7 @@ public class ServiceWatcher implements ServiceConnection { mHandler = handler; mCurrentUserId = userId; - mSignatureSets = new ArrayList<HashSet<Signature>>(); - for (int i=0; i < initialPackageNames.size(); i++) { - String pkg = initialPackageNames.get(i); - HashSet<Signature> set = new HashSet<Signature>(); - try { - Signature[] sigs = - mPm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures; - set.addAll(Arrays.asList(sigs)); - mSignatureSets.add(set); - } catch (NameNotFoundException e) { - Log.w(logTag, pkg + " not found"); - } - } - + mSignatureSets = getSignatureSets(context, initialPackageNames); } public boolean start() { @@ -132,15 +137,16 @@ public class ServiceWatcher implements ServiceConnection { // check version int version = 0; if (rInfo.serviceInfo.metaData != null) { - version = rInfo.serviceInfo.metaData.getInt(EXTRA_VERSION, 0); + version = rInfo.serviceInfo.metaData.getInt(EXTRA_SERVICE_VERSION, 0); } + if (version > mVersion) { bestVersion = version; bestPackage = packageName; } } - if (D) Log.d(mTag, String.format("bindBestPackage %s found %d, %s", + if (D) Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction, (justCheckThisPackage == null ? "" : "(" + justCheckThisPackage + ") "), rInfos.size(), (bestPackage == null ? "no new best package" : "new best packge: " + bestPackage))); @@ -174,7 +180,8 @@ public class ServiceWatcher implements ServiceConnection { | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_VISIBLE, mCurrentUserId); } - private boolean isSignatureMatch(Signature[] signatures) { + public static boolean isSignatureMatch(Signature[] signatures, + List<HashSet<Signature>> sigSets) { if (signatures == null) return false; // build hashset of input to test against @@ -184,7 +191,7 @@ public class ServiceWatcher implements ServiceConnection { } // test input against each of the signature sets - for (HashSet<Signature> referenceSet : mSignatureSets) { + for (HashSet<Signature> referenceSet : sigSets) { if (referenceSet.equals(inputSet)) { return true; } @@ -192,6 +199,10 @@ public class ServiceWatcher implements ServiceConnection { return false; } + private boolean isSignatureMatch(Signature[] signatures) { + return isSignatureMatch(signatures, mSignatureSets); + } + private final PackageMonitor mPackageMonitor = new PackageMonitor() { /** * Called when package has been reinstalled diff --git a/services/java/com/android/server/accessibility/ScreenMagnifier.java b/services/java/com/android/server/accessibility/ScreenMagnifier.java index caf37b7..0f04b44 100644 --- a/services/java/com/android/server/accessibility/ScreenMagnifier.java +++ b/services/java/com/android/server/accessibility/ScreenMagnifier.java @@ -40,6 +40,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.provider.Settings; +import android.text.TextUtils; import android.util.Property; import android.util.Slog; import android.view.Display; @@ -71,6 +72,7 @@ import com.android.internal.os.SomeArgs; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.Locale; /** * This class handles the screen magnification when accessibility is enabled. @@ -1000,45 +1002,44 @@ public final class ScreenMagnifier implements EventStreamTransformation { mViewport.recomputeBounds(mMagnificationController.isMagnifying()); } break; } - } else { - switch (transition) { - case WindowManagerPolicy.TRANSIT_ENTER: - case WindowManagerPolicy.TRANSIT_SHOW: { - if (!magnifying || !isScreenMagnificationAutoUpdateEnabled(mContext)) { - break; - } - final int type = info.type; - switch (type) { - // TODO: Are these all the windows we want to make - // visible when they appear on the screen? - // Do we need to take some of them out? - case WindowManager.LayoutParams.TYPE_APPLICATION: - case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL: - case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA: - case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL: - case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: - case WindowManager.LayoutParams.TYPE_SEARCH_BAR: - case WindowManager.LayoutParams.TYPE_PHONE: - case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT: - case WindowManager.LayoutParams.TYPE_TOAST: - case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: - case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE: - case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG: - case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG: - case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: - case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY: - case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: - case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: { - Rect magnifiedRegionBounds = mMagnificationController - .getMagnifiedRegionBounds(); - Rect touchableRegion = info.touchableRegion; - if (!magnifiedRegionBounds.intersect(touchableRegion)) { - ensureRectangleInMagnifiedRegionBounds( - magnifiedRegionBounds, touchableRegion); - } - } break; - } break; + } + switch (transition) { + case WindowManagerPolicy.TRANSIT_ENTER: + case WindowManagerPolicy.TRANSIT_SHOW: { + if (!magnifying || !isScreenMagnificationAutoUpdateEnabled(mContext)) { + break; } + final int type = info.type; + switch (type) { + // TODO: Are these all the windows we want to make + // visible when they appear on the screen? + // Do we need to take some of them out? + case WindowManager.LayoutParams.TYPE_APPLICATION: + case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL: + case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA: + case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL: + case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: + case WindowManager.LayoutParams.TYPE_SEARCH_BAR: + case WindowManager.LayoutParams.TYPE_PHONE: + case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT: + case WindowManager.LayoutParams.TYPE_TOAST: + case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: + case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE: + case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG: + case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG: + case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: + case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY: + case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: + case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: { + Rect magnifiedRegionBounds = mMagnificationController + .getMagnifiedRegionBounds(); + Rect touchableRegion = info.touchableRegion; + if (!magnifiedRegionBounds.intersect(touchableRegion)) { + ensureRectangleInMagnifiedRegionBounds( + magnifiedRegionBounds, touchableRegion); + } + } break; + } break; } } } finally { @@ -1067,7 +1068,12 @@ public final class ScreenMagnifier implements EventStreamTransformation { final float scrollX; final float scrollY; if (rectangle.width() > magnifiedRegionBounds.width()) { - scrollX = rectangle.left - magnifiedRegionBounds.left; + final int direction = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()); + if (direction == View.LAYOUT_DIRECTION_LTR) { + scrollX = rectangle.left - magnifiedRegionBounds.left; + } else { + scrollX = rectangle.right - magnifiedRegionBounds.right; + } } else if (rectangle.left < magnifiedRegionBounds.left) { scrollX = rectangle.left - magnifiedRegionBounds.left; } else if (rectangle.right > magnifiedRegionBounds.right) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 7132e1e..1737876 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -11119,8 +11119,8 @@ public final class ActivityManagerService extends ActivityManagerNative // instantiated. The backup agent will invoke backupAgentCreated() on the // activity manager to announce its creation. public boolean bindBackupAgent(ApplicationInfo app, int backupMode) { - if (DEBUG_BACKUP) Slog.v(TAG, "startBackupAgent: app=" + app + " mode=" + backupMode); - enforceCallingPermission("android.permission.BACKUP", "startBackupAgent"); + if (DEBUG_BACKUP) Slog.v(TAG, "bindBackupAgent: app=" + app + " mode=" + backupMode); + enforceCallingPermission("android.permission.BACKUP", "bindBackupAgent"); synchronized(this) { // !!! TODO: currently no check here that we're already bound @@ -11181,6 +11181,17 @@ public final class ActivityManagerService extends ActivityManagerNative return true; } + @Override + public void clearPendingBackup() { + if (DEBUG_BACKUP) Slog.v(TAG, "clearPendingBackup"); + enforceCallingPermission("android.permission.BACKUP", "clearPendingBackup"); + + synchronized (this) { + mBackupTarget = null; + mBackupAppName = null; + } + } + // A backup agent has just come up public void backupAgentCreated(String agentPackageName, IBinder agent) { if (DEBUG_BACKUP) Slog.v(TAG, "backupAgentCreated: " + agentPackageName @@ -11217,32 +11228,34 @@ public final class ActivityManagerService extends ActivityManagerNative } synchronized(this) { - if (mBackupAppName == null) { - Slog.w(TAG, "Unbinding backup agent with no active backup"); - return; - } - - if (!mBackupAppName.equals(appInfo.packageName)) { - Slog.e(TAG, "Unbind of " + appInfo + " but is not the current backup target"); - return; - } + try { + if (mBackupAppName == null) { + Slog.w(TAG, "Unbinding backup agent with no active backup"); + return; + } - ProcessRecord proc = mBackupTarget.app; - mBackupTarget = null; - mBackupAppName = null; + if (!mBackupAppName.equals(appInfo.packageName)) { + Slog.e(TAG, "Unbind of " + appInfo + " but is not the current backup target"); + return; + } - // Not backing this app up any more; reset its OOM adjustment - updateOomAdjLocked(proc); + // Not backing this app up any more; reset its OOM adjustment + final ProcessRecord proc = mBackupTarget.app; + updateOomAdjLocked(proc); - // If the app crashed during backup, 'thread' will be null here - if (proc.thread != null) { - try { - proc.thread.scheduleDestroyBackupAgent(appInfo, - compatibilityInfoForPackageLocked(appInfo)); - } catch (Exception e) { - Slog.e(TAG, "Exception when unbinding backup agent:"); - e.printStackTrace(); + // If the app crashed during backup, 'thread' will be null here + if (proc.thread != null) { + try { + proc.thread.scheduleDestroyBackupAgent(appInfo, + compatibilityInfoForPackageLocked(appInfo)); + } catch (Exception e) { + Slog.e(TAG, "Exception when unbinding backup agent:"); + e.printStackTrace(); + } } + } finally { + mBackupTarget = null; + mBackupAppName = null; } } } diff --git a/services/java/com/android/server/display/DisplayDeviceInfo.java b/services/java/com/android/server/display/DisplayDeviceInfo.java index b4dab86..e76bf44 100644 --- a/services/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/java/com/android/server/display/DisplayDeviceInfo.java @@ -17,6 +17,7 @@ package com.android.server.display; import android.util.DisplayMetrics; +import android.view.Surface; import libcore.util.Objects; @@ -31,11 +32,21 @@ final class DisplayDeviceInfo { public static final int FLAG_DEFAULT_DISPLAY = 1 << 0; /** - * Flag: Indicates that this display device can rotate to show contents in a - * different orientation. Otherwise the rotation is assumed to be fixed in the - * natural orientation and the display manager should transform the content to fit. + * Flag: Indicates that the orientation of this display device is coupled to the + * rotation of its associated logical display. + * <p> + * This flag should be applied to the default display to indicate that the user + * physically rotates the display when content is presented in a different orientation. + * The display manager will apply a coordinate transformation assuming that the + * physical orientation of the display matches the logical orientation of its content. + * </p><p> + * The flag should not be set when the display device is mounted in a fixed orientation + * such as on a desk. The display manager will apply a coordinate transformation + * such as a scale and translation to letterbox or pillarbox format under the + * assumption that the physical orientation of the display is invariant. + * </p> */ - public static final int FLAG_SUPPORTS_ROTATION = 1 << 1; + public static final int FLAG_ROTATES_WITH_CONTENT = 1 << 1; /** * Flag: Indicates that this display device has secure video output, such as HDCP. @@ -116,6 +127,17 @@ final class DisplayDeviceInfo { */ public int touch; + /** + * The additional rotation to apply to all content presented on the display device + * relative to its physical coordinate system. Default is {@link Surface#ROTATION_0}. + * <p> + * This field can be used to compensate for the fact that the display has been + * physically rotated relative to its natural orientation such as an HDMI monitor + * that has been mounted sideways to appear to be portrait rather than landscape. + * </p> + */ + public int rotation = Surface.ROTATION_0; + public void setAssumedDensityForExternalDisplay(int width, int height) { densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_XHIGH / 1080; // Technically, these values should be smaller than the apparent density @@ -139,7 +161,8 @@ final class DisplayDeviceInfo { && xDpi == other.xDpi && yDpi == other.yDpi && flags == other.flags - && touch == other.touch; + && touch == other.touch + && rotation == other.rotation; } @Override @@ -157,14 +180,18 @@ final class DisplayDeviceInfo { yDpi = other.yDpi; flags = other.flags; touch = other.touch; + rotation = other.rotation; } // For debugging purposes @Override public String toString() { - return "DisplayDeviceInfo{\"" + name + "\": " + width + " x " + height + ", " + refreshRate + " fps, " + return "DisplayDeviceInfo{\"" + name + "\": " + width + " x " + height + ", " + + refreshRate + " fps, " + "density " + densityDpi + ", " + xDpi + " x " + yDpi + " dpi" - + ", touch " + touchToString(touch) + flagsToString(flags) + "}"; + + ", touch " + touchToString(touch) + flagsToString(flags) + + ", rotation " + rotation + + "}"; } private static String touchToString(int touch) { @@ -185,8 +212,8 @@ final class DisplayDeviceInfo { if ((flags & FLAG_DEFAULT_DISPLAY) != 0) { msg.append(", FLAG_DEFAULT_DISPLAY"); } - if ((flags & FLAG_SUPPORTS_ROTATION) != 0) { - msg.append(", FLAG_SUPPORTS_ROTATION"); + if ((flags & FLAG_ROTATES_WITH_CONTENT) != 0) { + msg.append(", FLAG_ROTATES_WITH_CONTENT"); } if ((flags & FLAG_SECURE) != 0) { msg.append(", FLAG_SECURE"); diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java index 93896af..e58a0a5 100644 --- a/services/java/com/android/server/display/DisplayManagerService.java +++ b/services/java/com/android/server/display/DisplayManagerService.java @@ -127,6 +127,13 @@ public final class DisplayManagerService extends IDisplayManager.Stub { // services should be started. This option may disable certain display adapters. public boolean mOnlyCore; + // True if the display manager service should pretend there is only one display + // and only tell applications about the existence of the default logical display. + // The display manager can still mirror content to secondary displays but applications + // cannot present unique content on those displays. + // Used for demonstration purposes only. + private final boolean mSingleDisplayDemoMode; + // All callback records indexed by calling process id. public final SparseArray<CallbackRecord> mCallbacks = new SparseArray<CallbackRecord>(); @@ -182,6 +189,7 @@ public final class DisplayManagerService extends IDisplayManager.Stub { mHandler = new DisplayManagerHandler(mainHandler.getLooper()); mUiHandler = uiHandler; mDisplayAdapterListener = new DisplayAdapterListener(); + mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false); mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER); } @@ -631,6 +639,12 @@ public final class DisplayManagerService extends IDisplayManager.Stub { isDefault = false; } + if (!isDefault && mSingleDisplayDemoMode) { + Slog.i(TAG, "Not creating a logical display for a secondary display " + + " because single display demo mode is enabled: " + deviceInfo); + return; + } + final int displayId = assignDisplayIdLocked(isDefault); final int layerStack = assignLayerStackLocked(displayId); @@ -857,6 +871,7 @@ public final class DisplayManagerService extends IDisplayManager.Stub { pw.println(" mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId); pw.println(" mDefaultViewport=" + mDefaultViewport); pw.println(" mExternalTouchViewport=" + mExternalTouchViewport); + pw.println(" mSingleDisplayDemoMode=" + mSingleDisplayDemoMode); IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.increaseIndent(); diff --git a/services/java/com/android/server/display/LocalDisplayAdapter.java b/services/java/com/android/server/display/LocalDisplayAdapter.java index d780006..fe38d7f 100644 --- a/services/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/java/com/android/server/display/LocalDisplayAdapter.java @@ -20,6 +20,7 @@ import android.content.Context; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.SystemProperties; import android.util.SparseArray; import android.view.DisplayEventReceiver; import android.view.Surface; @@ -135,7 +136,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { mInfo.name = getContext().getResources().getString( com.android.internal.R.string.display_manager_built_in_display_name); mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY - | DisplayDeviceInfo.FLAG_SUPPORTS_ROTATION; + | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; mInfo.densityDpi = (int)(mPhys.density * 160 + 0.5f); mInfo.xDpi = mPhys.xDpi; mInfo.yDpi = mPhys.yDpi; @@ -145,6 +146,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { com.android.internal.R.string.display_manager_hdmi_display_name); mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL; mInfo.setAssumedDensityForExternalDisplay(mPhys.width, mPhys.height); + + // For demonstration purposes, allow rotation of the external display. + // In the future we might allow the user to configure this directly. + if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) { + mInfo.rotation = Surface.ROTATION_270; + } } } return mInfo; diff --git a/services/java/com/android/server/display/LogicalDisplay.java b/services/java/com/android/server/display/LogicalDisplay.java index 680662e..aa7ea82 100644 --- a/services/java/com/android/server/display/LogicalDisplay.java +++ b/services/java/com/android/server/display/LogicalDisplay.java @@ -241,10 +241,13 @@ final class LogicalDisplay { // is rotated when the contents of the logical display are rendered. int orientation = Surface.ROTATION_0; if (device == mPrimaryDisplayDevice - && (displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_SUPPORTS_ROTATION) != 0) { + && (displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT) != 0) { orientation = displayInfo.rotation; } + // Apply the physical rotation of the display device itself. + orientation = (orientation + displayDeviceInfo.rotation) % 4; + // Set the frame. // The frame specifies the rotated physical coordinates into which the viewport // is mapped. We need to take care to preserve the aspect ratio of the viewport. diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java index c441b02..66eac88 100644 --- a/services/java/com/android/server/display/WifiDisplayAdapter.java +++ b/services/java/com/android/server/display/WifiDisplayAdapter.java @@ -16,17 +16,28 @@ package com.android.server.display; +import com.android.internal.R; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Resources; import android.hardware.display.DisplayManager; import android.hardware.display.WifiDisplay; import android.hardware.display.WifiDisplayStatus; import android.media.RemoteDisplay; import android.os.Handler; import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.UserHandle; +import android.provider.Settings; import android.util.Slog; import android.view.Surface; @@ -52,8 +63,18 @@ final class WifiDisplayAdapter extends DisplayAdapter { private static final boolean DEBUG = false; + private static final int MSG_SEND_STATUS_CHANGE_BROADCAST = 1; + private static final int MSG_UPDATE_NOTIFICATION = 2; + + private static final String ACTION_DISCONNECT = "android.server.display.wfd.DISCONNECT"; + + private final WifiDisplayHandler mHandler; private final PersistentDataStore mPersistentDataStore; private final boolean mSupportsProtectedBuffers; + private final NotificationManager mNotificationManager; + + private final PendingIntent mSettingsPendingIntent; + private final PendingIntent mDisconnectPendingIntent; private WifiDisplayController mDisplayController; private WifiDisplayDevice mDisplayDevice; @@ -67,14 +88,32 @@ final class WifiDisplayAdapter extends DisplayAdapter { private WifiDisplay[] mRememberedDisplays = WifiDisplay.EMPTY_ARRAY; private boolean mPendingStatusChangeBroadcast; + private boolean mPendingNotificationUpdate; public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener, PersistentDataStore persistentDataStore) { super(syncRoot, context, handler, listener, TAG); + mHandler = new WifiDisplayHandler(handler.getLooper()); mPersistentDataStore = persistentDataStore; mSupportsProtectedBuffers = context.getResources().getBoolean( com.android.internal.R.bool.config_wifiDisplaySupportsProtectedBuffers); + mNotificationManager = (NotificationManager)context.getSystemService( + Context.NOTIFICATION_SERVICE); + + Intent settingsIntent = new Intent(Settings.ACTION_WIFI_DISPLAY_SETTINGS); + settingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED + | Intent.FLAG_ACTIVITY_CLEAR_TOP); + mSettingsPendingIntent = PendingIntent.getActivityAsUser( + context, 0, settingsIntent, 0, null, UserHandle.CURRENT); + + Intent disconnectIntent = new Intent(ACTION_DISCONNECT); + mDisconnectPendingIntent = PendingIntent.getBroadcastAsUser( + context, 0, disconnectIntent, 0, UserHandle.CURRENT); + + context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, + new IntentFilter(ACTION_DISCONNECT), null, mHandler); } @Override @@ -89,6 +128,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { pw.println("mAvailableDisplays=" + Arrays.toString(mAvailableDisplays)); pw.println("mRememberedDisplays=" + Arrays.toString(mRememberedDisplays)); pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast); + pw.println("mPendingNotificationUpdate=" + mPendingNotificationUpdate); pw.println("mSupportsProtectedBuffers=" + mSupportsProtectedBuffers); // Try to dump the controller state. @@ -266,6 +306,8 @@ final class WifiDisplayAdapter extends DisplayAdapter { mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height, refreshRate, deviceFlags, surface); sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED); + + scheduleUpdateNotificationLocked(); } private void handleDisconnectLocked() { @@ -273,6 +315,8 @@ final class WifiDisplayAdapter extends DisplayAdapter { mDisplayDevice.clearSurfaceLocked(); sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED); mDisplayDevice = null; + + scheduleUpdateNotificationLocked(); } } @@ -280,28 +324,81 @@ final class WifiDisplayAdapter extends DisplayAdapter { mCurrentStatus = null; if (!mPendingStatusChangeBroadcast) { mPendingStatusChangeBroadcast = true; - getHandler().post(mStatusChangeBroadcast); + mHandler.sendEmptyMessage(MSG_SEND_STATUS_CHANGE_BROADCAST); } } - private final Runnable mStatusChangeBroadcast = new Runnable() { - @Override - public void run() { - final Intent intent; - synchronized (getSyncRoot()) { - if (!mPendingStatusChangeBroadcast) { - return; - } + private void scheduleUpdateNotificationLocked() { + if (!mPendingNotificationUpdate) { + mPendingNotificationUpdate = true; + mHandler.sendEmptyMessage(MSG_UPDATE_NOTIFICATION); + } + } + + // Runs on the handler. + private void handleSendStatusChangeBroadcast() { + final Intent intent; + synchronized (getSyncRoot()) { + if (!mPendingStatusChangeBroadcast) { + return; + } + + mPendingStatusChangeBroadcast = false; + intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS, + getWifiDisplayStatusLocked()); + } - mPendingStatusChangeBroadcast = false; - intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS, - getWifiDisplayStatusLocked()); + // Send protected broadcast about wifi display status to registered receivers. + getContext().sendBroadcastAsUser(intent, UserHandle.ALL); + } + + // Runs on the handler. + private void handleUpdateNotification() { + final boolean isConnected; + synchronized (getSyncRoot()) { + if (!mPendingNotificationUpdate) { + return; } - // Send protected broadcast about wifi display status to registered receivers. - getContext().sendBroadcast(intent); + mPendingNotificationUpdate = false; + isConnected = (mDisplayDevice != null); + } + + mNotificationManager.cancelAsUser(null, + R.string.wifi_display_notification_title, UserHandle.ALL); + + if (isConnected) { + Context context = getContext(); + + Resources r = context.getResources(); + Notification notification = new Notification.Builder(context) + .setContentTitle(r.getString( + R.string.wifi_display_notification_title)) + .setContentText(r.getString( + R.string.wifi_display_notification_message)) + .setContentIntent(mSettingsPendingIntent) + .setSmallIcon(R.drawable.ic_notify_wifidisplay) + .setOngoing(true) + .addAction(android.R.drawable.ic_menu_close_clear_cancel, + r.getString(R.string.wifi_display_notification_disconnect), + mDisconnectPendingIntent) + .build(); + mNotificationManager.notifyAsUser(null, + R.string.wifi_display_notification_title, + notification, UserHandle.ALL); + } + } + + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(ACTION_DISCONNECT)) { + synchronized (getSyncRoot()) { + requestDisconnectLocked(); + } + } } }; @@ -454,4 +551,23 @@ final class WifiDisplayAdapter extends DisplayAdapter { return mInfo; } } + + private final class WifiDisplayHandler extends Handler { + public WifiDisplayHandler(Looper looper) { + super(looper, null, true /*async*/); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SEND_STATUS_CHANGE_BROADCAST: + handleSendStatusChangeBroadcast(); + break; + + case MSG_UPDATE_NOTIFICATION: + handleUpdateNotification(); + break; + } + } + } } diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java index 77e6c03..072dd33 100644 --- a/services/java/com/android/server/pm/UserManagerService.java +++ b/services/java/com/android/server/pm/UserManagerService.java @@ -62,6 +62,8 @@ public class UserManagerService extends IUserManager.Stub { private static final String LOG_TAG = "UserManagerService"; + private static final boolean DBG = false; + private static final String TAG_NAME = "name"; private static final String ATTR_FLAGS = "flags"; private static final String ATTR_ICON_PATH = "icon"; @@ -97,6 +99,9 @@ public class UserManagerService extends IUserManager.Stub { private int[] mUserIds; private boolean mGuestEnabled; private int mNextSerialNumber; + // This resets on a reboot. Otherwise it keeps incrementing so that user ids are + // not reused in quick succession + private int mNextUserId = MIN_USER_ID; private static UserManagerService sInstance; @@ -199,7 +204,8 @@ public class UserManagerService extends IUserManager.Stub { */ private UserInfo getUserInfoLocked(int userId) { UserInfo ui = mUsers.get(userId); - if (ui != null && ui.partial) { + // If it is partial and not in the process of being removed, return as unknown user. + if (ui != null && ui.partial && !mRemovingUserIds.contains(userId)) { Slog.w(LOG_TAG, "getUserInfo: unknown user #" + userId); return null; } @@ -668,6 +674,7 @@ public class UserManagerService extends IUserManager.Stub { long now = System.currentTimeMillis(); userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0; userInfo.partial = true; + Environment.getUserSystemDirectory(userInfo.id).mkdirs(); mUsers.put(userId, userInfo); writeUserListLocked(); writeUserLocked(userInfo); @@ -709,7 +716,7 @@ public class UserManagerService extends IUserManager.Stub { user.partial = true; writeUserLocked(user); } - + if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle); int res; try { res = ActivityManagerNative.getDefault().stopUser(userHandle, @@ -730,12 +737,13 @@ public class UserManagerService extends IUserManager.Stub { } void finishRemoveUser(int userHandle) { + if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userHandle); synchronized (mInstallLock) { synchronized (mPackagesLock) { removeUserStateLocked(userHandle); } } - + if (DBG) Slog.i(LOG_TAG, "Removed user " + userHandle + ", sending broadcast"); // Let other services shutdown any activity long ident = Binder.clearCallingIdentity(); try { @@ -804,10 +812,11 @@ public class UserManagerService extends IUserManager.Stub { num++; } } - int[] newUsers = new int[num]; + final int[] newUsers = new int[num]; + int n = 0; for (int i = 0; i < mUsers.size(); i++) { if (!mUsers.valueAt(i).partial) { - newUsers[i] = mUsers.keyAt(i); + newUsers[n++] = mUsers.keyAt(i); } } mUserIds = newUsers; @@ -840,13 +849,14 @@ public class UserManagerService extends IUserManager.Stub { */ private int getNextAvailableIdLocked() { synchronized (mPackagesLock) { - int i = MIN_USER_ID; + int i = mNextUserId; while (i < Integer.MAX_VALUE) { if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.contains(i)) { break; } i++; } + mNextUserId = i + 1; return i; } } diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java index 4e692a2..b94bceb 100644 --- a/services/java/com/android/server/power/PowerManagerService.java +++ b/services/java/com/android/server/power/PowerManagerService.java @@ -107,6 +107,8 @@ public final class PowerManagerService extends IPowerManager.Stub private static final int DIRTY_PROXIMITY_POSITIVE = 1 << 9; // Dirty bit: screen on blocker state became held or unheld private static final int DIRTY_SCREEN_ON_BLOCKER_RELEASED = 1 << 10; + // Dirty bit: dock state changed + private static final int DIRTY_DOCK_STATE = 1 << 11; // Wakefulness: The device is asleep and can only be awoken by a call to wakeUp(). // The screen should be off or in the process of being turned off by the display controller. @@ -269,6 +271,9 @@ public final class PowerManagerService extends IPowerManager.Stub // draining faster than it is charging and the user activity timeout has expired. private int mBatteryLevelWhenDreamStarted; + // The current dock state. + private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + // True if the device should wake up when plugged or unplugged. private boolean mWakeUpWhenPluggedOrUnpluggedConfig; @@ -281,6 +286,9 @@ public final class PowerManagerService extends IPowerManager.Stub // True if dreams should be activated on sleep. private boolean mDreamsActivateOnSleepSetting; + // True if dreams should be activated on dock. + private boolean mDreamsActivateOnDockSetting; + // The screen off timeout setting value in milliseconds. private int mScreenOffTimeoutSetting; @@ -440,6 +448,10 @@ public final class PowerManagerService extends IPowerManager.Stub filter.addAction(Intent.ACTION_USER_SWITCHED); mContext.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler); + filter = new IntentFilter(); + filter.addAction(Intent.ACTION_DOCK_EVENT); + mContext.registerReceiver(new DockReceiver(), filter, null, mHandler); + // Register for settings changes. final ContentResolver resolver = mContext.getContentResolver(); resolver.registerContentObserver(Settings.Secure.getUriFor( @@ -448,6 +460,9 @@ public final class PowerManagerService extends IPowerManager.Stub resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP), false, mSettingsObserver, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK), + false, mSettingsObserver, UserHandle.USER_ALL); resolver.registerContentObserver(Settings.System.getUriFor( Settings.System.SCREEN_OFF_TIMEOUT), false, mSettingsObserver, UserHandle.USER_ALL); @@ -487,6 +502,9 @@ public final class PowerManagerService extends IPowerManager.Stub mDreamsActivateOnSleepSetting = (Settings.Secure.getIntForUser(resolver, Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 0, UserHandle.USER_CURRENT) != 0); + mDreamsActivateOnDockSetting = (Settings.Secure.getIntForUser(resolver, + Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK, 0, + UserHandle.USER_CURRENT) != 0); mScreenOffTimeoutSetting = Settings.System.getIntForUser(resolver, Settings.System.SCREEN_OFF_TIMEOUT, DEFAULT_SCREEN_OFF_TIMEOUT, UserHandle.USER_CURRENT); @@ -1339,13 +1357,14 @@ public final class PowerManagerService extends IPowerManager.Stub private boolean updateWakefulnessLocked(int dirty) { boolean changed = false; if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED - | DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE)) != 0) { + | DIRTY_WAKEFULNESS | DIRTY_STAY_ON | DIRTY_PROXIMITY_POSITIVE + | DIRTY_DOCK_STATE)) != 0) { if (mWakefulness == WAKEFULNESS_AWAKE && isItBedTimeYetLocked()) { if (DEBUG_SPEW) { Slog.d(TAG, "updateWakefulnessLocked: Bed time..."); } final long time = SystemClock.uptimeMillis(); - if (mDreamsActivateOnSleepSetting) { + if (shouldNapAtBedTimeLocked()) { changed = napNoUpdateLocked(time); } else { changed = goToSleepNoUpdateLocked(time, @@ -1357,6 +1376,16 @@ public final class PowerManagerService extends IPowerManager.Stub } /** + * Returns true if the device should automatically nap and start dreaming when the user + * activity timeout has expired and it's bedtime. + */ + private boolean shouldNapAtBedTimeLocked() { + return mDreamsActivateOnSleepSetting + || (mDreamsActivateOnDockSetting + && mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED); + } + + /** * Returns true if the device should go to sleep now. * Also used when exiting a dream to determine whether we should go back * to being fully awake or else go to sleep for good. @@ -2124,6 +2153,7 @@ public final class PowerManagerService extends IPowerManager.Stub pw.println(" mPlugType=" + mPlugType); pw.println(" mBatteryLevel=" + mBatteryLevel); pw.println(" mBatteryLevelWhenDreamStarted=" + mBatteryLevelWhenDreamStarted); + pw.println(" mDockState=" + mDockState); pw.println(" mStayOn=" + mStayOn); pw.println(" mProximityPositive=" + mProximityPositive); pw.println(" mBootCompleted=" + mBootCompleted); @@ -2149,6 +2179,7 @@ public final class PowerManagerService extends IPowerManager.Stub pw.println(" mDreamsSupportedConfig=" + mDreamsSupportedConfig); pw.println(" mDreamsEnabledSetting=" + mDreamsEnabledSetting); pw.println(" mDreamsActivateOnSleepSetting=" + mDreamsActivateOnSleepSetting); + pw.println(" mDreamsActivateOnDockSetting=" + mDreamsActivateOnDockSetting); pw.println(" mScreenOffTimeoutSetting=" + mScreenOffTimeoutSetting); pw.println(" mMaximumScreenOffTimeoutFromDeviceAdmin=" + mMaximumScreenOffTimeoutFromDeviceAdmin + " (enforced=" @@ -2267,6 +2298,21 @@ public final class PowerManagerService extends IPowerManager.Stub } } + private final class DockReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + synchronized (mLock) { + int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, + Intent.EXTRA_DOCK_STATE_UNDOCKED); + if (mDockState != dockState) { + mDockState = dockState; + mDirty |= DIRTY_DOCK_STATE; + updatePowerStateLocked(); + } + } + } + } + private final class SettingsObserver extends ContentObserver { public SettingsObserver(Handler handler) { super(handler); diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index fa450ae..e74b6db 100755 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -2420,6 +2420,7 @@ public class WindowManagerService extends IWindowManager.Stub final WindowList windows = win.getWindowList(); windows.remove(win); mPendingRemove.remove(win); + mResizingWindows.remove(win); mWindowsChanged = true; if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Final remove of window: " + win); diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_back.png Binary files differnew file mode 100644 index 0000000..84e6bc8 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_back.png diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_back_default.png b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_back_default.png Binary files differdeleted file mode 100644 index ac5a97b..0000000 --- a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_back_default.png +++ /dev/null diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_home.png b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_home.png Binary files differnew file mode 100644 index 0000000..38e4f45 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_home.png diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_home_default.png b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_home_default.png Binary files differdeleted file mode 100644 index a90dc9b..0000000 --- a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_home_default.png +++ /dev/null diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_recent.png Binary files differnew file mode 100644 index 0000000..bf9f300 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_recent.png diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_recent_default.png b/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_recent_default.png Binary files differdeleted file mode 100644 index cb3c433..0000000 --- a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_recent_default.png +++ /dev/null diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back.png Binary files differnew file mode 100644 index 0000000..a00bc5b --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back.png diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.png Binary files differdeleted file mode 100644 index 5ab09f0..0000000 --- a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back_default.png +++ /dev/null diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home.png Binary files differnew file mode 100644 index 0000000..dc3183b --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home.png diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.png Binary files differdeleted file mode 100644 index 62ca427..0000000 --- a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home_default.png +++ /dev/null diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent.png Binary files differnew file mode 100644 index 0000000..b07f611 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent.png diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.png b/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.png Binary files differdeleted file mode 100644 index ff698fb..0000000 --- a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent_default.png +++ /dev/null diff --git a/tools/layoutlib/bridge/resources/bars/tablet_system_bar.xml b/tools/layoutlib/bridge/resources/bars/navigation_bar.xml index c5acddb..599ca08 100644 --- a/tools/layoutlib/bridge/resources/bars/tablet_system_bar.xml +++ b/tools/layoutlib/bridge/resources/bars/navigation_bar.xml @@ -1,5 +1,9 @@ <?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1"/> <ImageView android:layout_height="wrap_content" android:layout_width="wrap_content"/> @@ -13,12 +17,4 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1"/> - <ImageView - android:layout_height="wrap_content" - android:layout_width="wrap_content"/> - <ImageView - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:layout_marginLeft="3dip" - android:layout_marginRight="15dip"/> </merge> diff --git a/tools/layoutlib/bridge/resources/bars/phone_system_bar.xml b/tools/layoutlib/bridge/resources/bars/status_bar.xml index d3c492e..d3c492e 100644 --- a/tools/layoutlib/bridge/resources/bars/phone_system_bar.xml +++ b/tools/layoutlib/bridge/resources/bars/status_bar.xml diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_back.png Binary files differnew file mode 100644 index 0000000..bd60cd6 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_back.png diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_back_default.png b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_back_default.png Binary files differdeleted file mode 100644 index 4cb305d..0000000 --- a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_back_default.png +++ /dev/null diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_home.png b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_home.png Binary files differnew file mode 100644 index 0000000..c5bc5c9 --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_home.png diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_home_default.png b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_home_default.png Binary files differdeleted file mode 100644 index 31d35c8..0000000 --- a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_home_default.png +++ /dev/null diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_recent.png Binary files differnew file mode 100644 index 0000000..f621d9c --- /dev/null +++ b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_recent.png diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_recent_default.png b/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_recent_default.png Binary files differdeleted file mode 100644 index f0cc341..0000000 --- a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_recent_default.png +++ /dev/null diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index daf520b..bf8658e 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -210,7 +210,8 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { Capability.PLAY_ANIMATION, Capability.ANIMATED_VIEW_MANIPULATION, Capability.ADAPTER_BINDING, - Capability.EXTENDED_VIEWINFO); + Capability.EXTENDED_VIEWINFO, + Capability.FIXED_SCALABLE_NINE_PATCH); BridgeAssetManager.initSystem(); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java index 62c886b..ea9d8d9 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java @@ -60,11 +60,15 @@ abstract class CustomBar extends LinearLayout { protected abstract TextView getStyleableTextView(); - protected CustomBar(Context context, Density density, String layoutPath, String name) - throws XmlPullParserException { + protected CustomBar(Context context, Density density, int orientation, String layoutPath, + String name) throws XmlPullParserException { super(context); - setOrientation(LinearLayout.HORIZONTAL); - setGravity(Gravity.CENTER_VERTICAL); + setOrientation(orientation); + if (orientation == LinearLayout.HORIZONTAL) { + setGravity(Gravity.CENTER_VERTICAL); + } else { + setGravity(Gravity.CENTER_HORIZONTAL); + } LayoutInflater inflater = (LayoutInflater) getContext().getSystemService( Context.LAYOUT_INFLATER_SERVICE); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java index 68f5aba..226649d 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java @@ -21,6 +21,7 @@ import com.android.resources.Density; import org.xmlpull.v1.XmlPullParserException; import android.content.Context; +import android.widget.LinearLayout; import android.widget.TextView; public class FakeActionBar extends CustomBar { @@ -29,7 +30,7 @@ public class FakeActionBar extends CustomBar { public FakeActionBar(Context context, Density density, String label, String icon) throws XmlPullParserException { - super(context, density, "/bars/action_bar.xml", "action_bar.xml"); + super(context, density, LinearLayout.HORIZONTAL, "/bars/action_bar.xml", "action_bar.xml"); // Cannot access the inside items through id because no R.id values have been // created for them. diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TabletSystemBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java index 456ddb4..cc90d6b 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TabletSystemBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java @@ -17,34 +17,34 @@ package com.android.layoutlib.bridge.bars; import com.android.resources.Density; -import com.android.resources.ResourceType; import org.xmlpull.v1.XmlPullParserException; import android.content.Context; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.LevelListDrawable; +import android.widget.LinearLayout; import android.widget.TextView; -public class TabletSystemBar extends CustomBar { +public class NavigationBar extends CustomBar { - public TabletSystemBar(Context context, Density density) throws XmlPullParserException { - super(context, density, "/bars/tablet_system_bar.xml", "tablet_system_bar.xml"); + public NavigationBar(Context context, Density density, int orientation) throws XmlPullParserException { + super(context, density, orientation, "/bars/navigation_bar.xml", "navigation_bar.xml"); setBackgroundColor(0xFF000000); // Cannot access the inside items through id because no R.id values have been // created for them. // We do know the order though. - loadIcon(0, "ic_sysbar_back_default.png", density); - loadIcon(1, "ic_sysbar_home_default.png", density); - loadIcon(2, "ic_sysbar_recent_default.png", density); - // 3 is the spacer - loadIcon(4, "stat_sys_wifi_signal_4_fully.png", density); - Drawable drawable = loadIcon(5, ResourceType.DRAWABLE, "stat_sys_battery_charge"); - if (drawable instanceof LevelListDrawable) { - ((LevelListDrawable) drawable).setLevel(100); + // 0 is a spacer. + int back = 1; + int recent = 3; + if (orientation == LinearLayout.VERTICAL) { + back = 3; + recent = 1; } + + loadIcon(back, "ic_sysbar_back.png", density); + loadIcon(2, "ic_sysbar_home.png", density); + loadIcon(recent, "ic_sysbar_recent.png", density); } @Override diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java index 7521011..5c08412 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/PhoneSystemBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java @@ -25,12 +25,13 @@ import android.content.Context; import android.graphics.drawable.Drawable; import android.graphics.drawable.LevelListDrawable; import android.view.Gravity; +import android.widget.LinearLayout; import android.widget.TextView; -public class PhoneSystemBar extends CustomBar { +public class StatusBar extends CustomBar { - public PhoneSystemBar(Context context, Density density) throws XmlPullParserException { - super(context, density, "/bars/phone_system_bar.xml", "phone_system_bar.xml"); + public StatusBar(Context context, Density density) throws XmlPullParserException { + super(context, density, LinearLayout.HORIZONTAL, "/bars/status_bar.xml", "status_bar.xml"); // FIXME: use FILL_H? setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java index 5f5ebc4..c27859f 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java @@ -21,6 +21,7 @@ import com.android.resources.Density; import org.xmlpull.v1.XmlPullParserException; import android.content.Context; +import android.widget.LinearLayout; import android.widget.TextView; public class TitleBar extends CustomBar { @@ -29,7 +30,7 @@ public class TitleBar extends CustomBar { public TitleBar(Context context, Density density, String label) throws XmlPullParserException { - super(context, density, "/bars/title_bar.xml", "title_bar.xml"); + super(context, density, LinearLayout.HORIZONTAL, "/bars/title_bar.xml", "title_bar.xml"); // Cannot access the inside items through id because no R.id values have been // created for them. diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java index de65fd4..f109e39 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java @@ -20,11 +20,12 @@ import static com.android.ide.common.rendering.api.Result.Status.ERROR_LOCK_INTE import static com.android.ide.common.rendering.api.Result.Status.ERROR_TIMEOUT; import static com.android.ide.common.rendering.api.Result.Status.SUCCESS; +import com.android.ide.common.rendering.api.HardwareConfig; import com.android.ide.common.rendering.api.LayoutLog; import com.android.ide.common.rendering.api.RenderParams; import com.android.ide.common.rendering.api.RenderResources; -import com.android.ide.common.rendering.api.Result; import com.android.ide.common.rendering.api.RenderResources.FrameworkResourceIdProvider; +import com.android.ide.common.rendering.api.Result; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.resources.Density; @@ -98,19 +99,22 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso return result; } + HardwareConfig hardwareConfig = mParams.getHardwareConfig(); + // setup the display Metrics. DisplayMetrics metrics = new DisplayMetrics(); - metrics.densityDpi = metrics.noncompatDensityDpi = mParams.getDensity().getDpiValue(); + metrics.densityDpi = metrics.noncompatDensityDpi = + hardwareConfig.getDensity().getDpiValue(); metrics.density = metrics.noncompatDensity = metrics.densityDpi / (float) DisplayMetrics.DENSITY_DEFAULT; metrics.scaledDensity = metrics.noncompatScaledDensity = metrics.density; - metrics.widthPixels = metrics.noncompatWidthPixels = mParams.getScreenWidth(); - metrics.heightPixels = metrics.noncompatHeightPixels = mParams.getScreenHeight(); - metrics.xdpi = metrics.noncompatXdpi = mParams.getXdpi(); - metrics.ydpi = metrics.noncompatYdpi = mParams.getYdpi(); + metrics.widthPixels = metrics.noncompatWidthPixels = hardwareConfig.getScreenWidth(); + metrics.heightPixels = metrics.noncompatHeightPixels = hardwareConfig.getScreenHeight(); + metrics.xdpi = metrics.noncompatXdpi = hardwareConfig.getXdpi(); + metrics.ydpi = metrics.noncompatYdpi = hardwareConfig.getYdpi(); RenderResources resources = mParams.getResources(); @@ -305,7 +309,9 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso private Configuration getConfiguration() { Configuration config = new Configuration(); - ScreenSize screenSize = mParams.getConfigScreenSize(); + HardwareConfig hardwareConfig = mParams.getHardwareConfig(); + + ScreenSize screenSize = hardwareConfig.getScreenSize(); if (screenSize != null) { switch (screenSize) { case SMALL: @@ -323,13 +329,13 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso } } - Density density = mParams.getDensity(); + Density density = hardwareConfig.getDensity(); if (density == null) { density = Density.MEDIUM; } - config.screenWidthDp = mParams.getScreenWidth() / density.getDpiValue(); - config.screenHeightDp = mParams.getScreenHeight() / density.getDpiValue(); + config.screenWidthDp = hardwareConfig.getScreenWidth() / density.getDpiValue(); + config.screenHeightDp = hardwareConfig.getScreenHeight() / density.getDpiValue(); if (config.screenHeightDp < config.screenWidthDp) { config.smallestScreenWidthDp = config.screenHeightDp; } else { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java index 8133210..b677131 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java @@ -19,6 +19,7 @@ package com.android.layoutlib.bridge.impl; import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN; import com.android.ide.common.rendering.api.DrawableParams; +import com.android.ide.common.rendering.api.HardwareConfig; import com.android.ide.common.rendering.api.ResourceValue; import com.android.ide.common.rendering.api.Result; import com.android.ide.common.rendering.api.Result.Status; @@ -59,6 +60,7 @@ public class RenderDrawable extends RenderAction<DrawableParams> { try { // get the drawable resource value DrawableParams params = getParams(); + HardwareConfig hardwareConfig = params.getHardwareConfig(); ResourceValue drawableResource = params.getDrawable(); // resolve it @@ -75,15 +77,15 @@ public class RenderDrawable extends RenderAction<DrawableParams> { // get the actual Drawable object to draw Drawable d = ResourceHelper.getDrawable(drawableResource, context); - content.setBackgroundDrawable(d); + content.setBackground(d); // set the AttachInfo on the root view. AttachInfo_Accessor.setAttachInfo(content); // measure - int w = params.getScreenWidth(); - int h = params.getScreenHeight(); + int w = hardwareConfig.getScreenWidth(); + int h = hardwareConfig.getScreenHeight(); int w_spec = MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY); int h_spec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY); content.measure(w_spec, h_spec); @@ -99,11 +101,11 @@ public class RenderDrawable extends RenderAction<DrawableParams> { // create an Android bitmap around the BufferedImage Bitmap bitmap = Bitmap_Delegate.createBitmap(image, - true /*isMutable*/, params.getDensity()); + true /*isMutable*/, hardwareConfig.getDensity()); // create a Canvas around the Android bitmap Canvas canvas = new Canvas(bitmap); - canvas.setDensity(params.getDensity().getDpiValue()); + canvas.setDensity(hardwareConfig.getDensity().getDpiValue()); // and draw content.draw(canvas); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index cc0f077..c14af4a 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -24,6 +24,7 @@ import static com.android.ide.common.rendering.api.Result.Status.ERROR_VIEWGROUP import static com.android.ide.common.rendering.api.Result.Status.SUCCESS; import com.android.ide.common.rendering.api.AdapterBinding; +import com.android.ide.common.rendering.api.HardwareConfig; import com.android.ide.common.rendering.api.IAnimationListener; import com.android.ide.common.rendering.api.ILayoutPullParser; import com.android.ide.common.rendering.api.IProjectCallback; @@ -43,13 +44,13 @@ import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import com.android.layoutlib.bridge.bars.FakeActionBar; -import com.android.layoutlib.bridge.bars.PhoneSystemBar; -import com.android.layoutlib.bridge.bars.TabletSystemBar; +import com.android.layoutlib.bridge.bars.NavigationBar; +import com.android.layoutlib.bridge.bars.StatusBar; import com.android.layoutlib.bridge.bars.TitleBar; import com.android.layoutlib.bridge.impl.binding.FakeAdapter; import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter; import com.android.resources.ResourceType; -import com.android.resources.ScreenSize; +import com.android.resources.ScreenOrientation; import com.android.util.Pair; import org.xmlpull.v1.XmlPullParserException; @@ -68,8 +69,8 @@ import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.AttachInfo_Accessor; import android.view.BridgeInflater; -import android.view.IWindowManagerImpl; import android.view.IWindowManager; +import android.view.IWindowManagerImpl; import android.view.Surface; import android.view.View; import android.view.View.MeasureSpec; @@ -124,7 +125,8 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { private boolean mWindowIsFloating; private int mStatusBarSize; - private int mSystemBarSize; + private int mNavigationBarSize; + private int mNavigationBarOrientation = LinearLayout.HORIZONTAL; private int mTitleBarSize; private int mActionBarSize; @@ -187,7 +189,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { findBackground(resources); findStatusBar(resources, metrics); findActionBar(resources, metrics); - findSystemBar(resources, metrics); + findNavigationBar(resources, metrics); // FIXME: find those out, and possibly add them to the render params boolean hasSystemNavBar = true; @@ -221,19 +223,57 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { try { SessionParams params = getParams(); + HardwareConfig hardwareConfig = params.getHardwareConfig(); BridgeContext context = getContext(); + // the view group that receives the window background. ViewGroup backgroundView = null; if (mWindowIsFloating || params.isForceNoDecor()) { backgroundView = mViewRoot = mContentRoot = new FrameLayout(context); } else { + if (hasSoftwareButtons() && mNavigationBarOrientation == LinearLayout.VERTICAL) { + /* + * This is a special case where the navigation bar is on the right. + +-------------------------------------------------+---+ + | Status bar (always) | | + +-------------------------------------------------+ | + | (Layout with background drawable) | | + | +---------------------------------------------+ | | + | | Title/Action bar (optional) | | | + | +---------------------------------------------+ | | + | | Content, vertical extending | | | + | | | | | + | +---------------------------------------------+ | | + +-------------------------------------------------+---+ + + So we create a horizontal layout, with the nav bar on the right, + and the left part is the normal layout below without the nav bar at + the bottom + */ + LinearLayout topLayout = new LinearLayout(context); + mViewRoot = topLayout; + topLayout.setOrientation(LinearLayout.HORIZONTAL); + + try { + NavigationBar navigationBar = new NavigationBar(context, + hardwareConfig.getDensity(), LinearLayout.VERTICAL); + navigationBar.setLayoutParams( + new LinearLayout.LayoutParams( + mNavigationBarSize, + LayoutParams.MATCH_PARENT)); + topLayout.addView(navigationBar); + } catch (XmlPullParserException e) { + + } + } + /* * we're creating the following layout * +-------------------------------------------------+ - | System bar (only in phone UI) | + | Status bar (always) | +-------------------------------------------------+ | (Layout with background drawable) | | +---------------------------------------------+ | @@ -243,20 +283,31 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { | | | | | +---------------------------------------------+ | +-------------------------------------------------+ - | System bar (only in tablet UI) | + | Navigation bar for soft buttons, maybe see above| +-------------------------------------------------+ */ LinearLayout topLayout = new LinearLayout(context); - mViewRoot = topLayout; topLayout.setOrientation(LinearLayout.VERTICAL); + // if we don't already have a view root this is it + if (mViewRoot == null) { + mViewRoot = topLayout; + } else { + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( + LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); + layoutParams.weight = 1; + topLayout.setLayoutParams(layoutParams); + + // this is the case of soft buttons + vertical bar. + // this top layout is the first layout in the horizontal layout. see above) + mViewRoot.addView(topLayout, 0); + } if (mStatusBarSize > 0) { // system bar try { - PhoneSystemBar systemBar = new PhoneSystemBar(context, - params.getDensity()); + StatusBar systemBar = new StatusBar(context, hardwareConfig.getDensity()); systemBar.setLayoutParams( new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, mStatusBarSize)); @@ -280,7 +331,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { if (mActionBarSize > 0) { try { FakeActionBar actionBar = new FakeActionBar(context, - params.getDensity(), + hardwareConfig.getDensity(), params.getAppLabel(), params.getAppIcon()); actionBar.setLayoutParams( new LinearLayout.LayoutParams( @@ -292,7 +343,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } else if (mTitleBarSize > 0) { try { TitleBar titleBar = new TitleBar(context, - params.getDensity(), params.getAppLabel()); + hardwareConfig.getDensity(), params.getAppLabel()); titleBar.setLayoutParams( new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, mTitleBarSize)); @@ -310,15 +361,16 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { mContentRoot.setLayoutParams(layoutParams); backgroundLayout.addView(mContentRoot); - if (mSystemBarSize > 0) { + if (mNavigationBarOrientation == LinearLayout.HORIZONTAL && + mNavigationBarSize > 0) { // system bar try { - TabletSystemBar systemBar = new TabletSystemBar(context, - params.getDensity()); - systemBar.setLayoutParams( + NavigationBar navigationBar = new NavigationBar(context, + hardwareConfig.getDensity(), LinearLayout.HORIZONTAL); + navigationBar.setLayoutParams( new LinearLayout.LayoutParams( - LayoutParams.MATCH_PARENT, mSystemBarSize)); - topLayout.addView(systemBar); + LayoutParams.MATCH_PARENT, mNavigationBarSize)); + topLayout.addView(navigationBar); } catch (XmlPullParserException e) { } @@ -346,7 +398,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { // get the background drawable if (mWindowBackground != null && backgroundView != null) { Drawable d = ResourceHelper.getDrawable(mWindowBackground, context); - backgroundView.setBackgroundDrawable(d); + backgroundView.setBackground(d); } return SUCCESS.createResult(); @@ -389,13 +441,14 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } RenderingMode renderingMode = params.getRenderingMode(); + HardwareConfig hardwareConfig = params.getHardwareConfig(); // only do the screen measure when needed. boolean newRenderSize = false; if (mMeasuredScreenWidth == -1) { newRenderSize = true; - mMeasuredScreenWidth = params.getScreenWidth(); - mMeasuredScreenHeight = params.getScreenHeight(); + mMeasuredScreenWidth = hardwareConfig.getScreenWidth(); + mMeasuredScreenHeight = hardwareConfig.getScreenHeight(); if (renderingMode != RenderingMode.NORMAL) { int widthMeasureSpecMode = renderingMode.isHorizExpand() ? @@ -495,11 +548,11 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { // create an Android bitmap around the BufferedImage Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage, - true /*isMutable*/, params.getDensity()); + true /*isMutable*/, hardwareConfig.getDensity()); // create a Canvas around the Android bitmap mCanvas = new Canvas(bitmap); - mCanvas.setDensity(params.getDensity().getDpiValue()); + mCanvas.setDensity(hardwareConfig.getDensity().getDpiValue()); } if (freshRender && newImage == false) { @@ -972,30 +1025,28 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } } - private boolean isTabletUi() { - return getParams().getConfigScreenSize() == ScreenSize.XLARGE; + private boolean hasSoftwareButtons() { + return getParams().getHardwareConfig().hasSoftwareButtons(); } private void findStatusBar(RenderResources resources, DisplayMetrics metrics) { - if (isTabletUi() == false) { - boolean windowFullscreen = getBooleanThemeValue(resources, - "windowFullscreen", false /*defaultValue*/); + boolean windowFullscreen = getBooleanThemeValue(resources, + "windowFullscreen", false /*defaultValue*/); - if (windowFullscreen == false && mWindowIsFloating == false) { - // default value - mStatusBarSize = DEFAULT_STATUS_BAR_HEIGHT; + if (windowFullscreen == false && mWindowIsFloating == false) { + // default value + mStatusBarSize = DEFAULT_STATUS_BAR_HEIGHT; - // get the real value - ResourceValue value = resources.getFrameworkResource(ResourceType.DIMEN, - "status_bar_height"); + // get the real value + ResourceValue value = resources.getFrameworkResource(ResourceType.DIMEN, + "status_bar_height"); - if (value != null) { - TypedValue typedValue = ResourceHelper.getValue("status_bar_height", - value.getValue(), true /*requireUnit*/); - if (typedValue != null) { - // compute the pixel value based on the display metrics - mStatusBarSize = (int)typedValue.getDimension(metrics); - } + if (value != null) { + TypedValue typedValue = ResourceHelper.getValue("status_bar_height", + value.getValue(), true /*requireUnit*/); + if (typedValue != null) { + // compute the pixel value based on the display metrics + mStatusBarSize = (int)typedValue.getDimension(metrics); } } } @@ -1062,22 +1113,48 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } } - private void findSystemBar(RenderResources resources, DisplayMetrics metrics) { - if (isTabletUi() && mWindowIsFloating == false) { + private void findNavigationBar(RenderResources resources, DisplayMetrics metrics) { + if (hasSoftwareButtons() && mWindowIsFloating == false) { // default value - mSystemBarSize = 48; // ?? + mNavigationBarSize = 48; // ?? + + HardwareConfig hardwareConfig = getParams().getHardwareConfig(); + + boolean barOnBottom = true; + + if (hardwareConfig.getOrientation() == ScreenOrientation.LANDSCAPE) { + // compute the dp of the screen. + int shortSize = hardwareConfig.getScreenHeight(); + + // compute in dp + int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / hardwareConfig.getDensity().getDpiValue(); + + if (shortSizeDp < 600) { + // 0-599dp: "phone" UI with bar on the side + barOnBottom = false; + } else { + // 600+dp: "tablet" UI with bar on the bottom + barOnBottom = true; + } + } + + if (barOnBottom) { + mNavigationBarOrientation = LinearLayout.HORIZONTAL; + } else { + mNavigationBarOrientation = LinearLayout.VERTICAL; + } // get the real value ResourceValue value = resources.getFrameworkResource(ResourceType.DIMEN, - "status_bar_height"); + barOnBottom ? "navigation_bar_height" : "navigation_bar_width"); if (value != null) { - TypedValue typedValue = ResourceHelper.getValue("status_bar_height", + TypedValue typedValue = ResourceHelper.getValue("navigation_bar_height", value.getValue(), true /*requireUnit*/); if (typedValue != null) { // compute the pixel value based on the display metrics - mSystemBarSize = (int)typedValue.getDimension(metrics); + mNavigationBarSize = (int)typedValue.getDimension(metrics); } } } diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java index 93ab4a4..0b0d738 100644 --- a/wifi/java/android/net/wifi/WifiMonitor.java +++ b/wifi/java/android/net/wifi/WifiMonitor.java @@ -178,6 +178,7 @@ public class WifiMonitor { private static final String P2P_GO_NEG_SUCCESS_STR = "P2P-GO-NEG-SUCCESS"; + /* P2P-GO-NEG-FAILURE status=x */ private static final String P2P_GO_NEG_FAILURE_STR = "P2P-GO-NEG-FAILURE"; private static final String P2P_GROUP_FORMATION_SUCCESS_STR = @@ -566,6 +567,26 @@ public class WifiMonitor { WifiManager.ERROR, 0)); } + /* <event> status=<err> and the special case of <event> reason=FREQ_CONFLICT */ + private P2pStatus p2pError(String dataString) { + P2pStatus err = P2pStatus.UNKNOWN; + String[] tokens = dataString.split(" "); + if (tokens.length < 2) return err; + String[] nameValue = tokens[1].split("="); + if (nameValue.length != 2) return err; + + /* Handle the special case of reason=FREQ+CONFLICT */ + if (nameValue[1].equals("FREQ_CONFLICT")) { + return P2pStatus.NO_COMMON_CHANNEL; + } + try { + err = P2pStatus.valueOf(Integer.parseInt(nameValue[1])); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + return err; + } + /** * Handle p2p events */ @@ -582,11 +603,11 @@ public class WifiMonitor { } else if (dataString.startsWith(P2P_GO_NEG_SUCCESS_STR)) { mStateMachine.sendMessage(P2P_GO_NEGOTIATION_SUCCESS_EVENT); } else if (dataString.startsWith(P2P_GO_NEG_FAILURE_STR)) { - mStateMachine.sendMessage(P2P_GO_NEGOTIATION_FAILURE_EVENT); + mStateMachine.sendMessage(P2P_GO_NEGOTIATION_FAILURE_EVENT, p2pError(dataString)); } else if (dataString.startsWith(P2P_GROUP_FORMATION_SUCCESS_STR)) { mStateMachine.sendMessage(P2P_GROUP_FORMATION_SUCCESS_EVENT); } else if (dataString.startsWith(P2P_GROUP_FORMATION_FAILURE_STR)) { - mStateMachine.sendMessage(P2P_GROUP_FORMATION_FAILURE_EVENT); + mStateMachine.sendMessage(P2P_GROUP_FORMATION_FAILURE_EVENT, p2pError(dataString)); } else if (dataString.startsWith(P2P_GROUP_STARTED_STR)) { mStateMachine.sendMessage(P2P_GROUP_STARTED_EVENT, new WifiP2pGroup(dataString)); } else if (dataString.startsWith(P2P_GROUP_REMOVED_STR)) { @@ -595,17 +616,7 @@ public class WifiMonitor { mStateMachine.sendMessage(P2P_INVITATION_RECEIVED_EVENT, new WifiP2pGroup(dataString)); } else if (dataString.startsWith(P2P_INVITATION_RESULT_STR)) { - String[] tokens = dataString.split(" "); - if (tokens.length != 2) return; - String[] nameValue = tokens[1].split("="); - if (nameValue.length != 2) return; - P2pStatus err = P2pStatus.UNKNOWN; - try { - err = P2pStatus.valueOf(Integer.parseInt(nameValue[1])); - } catch (NumberFormatException e) { - e.printStackTrace(); - } - mStateMachine.sendMessage(P2P_INVITATION_RESULT_EVENT, err); + mStateMachine.sendMessage(P2P_INVITATION_RESULT_EVENT, p2pError(dataString)); } else if (dataString.startsWith(P2P_PROV_DISC_PBC_REQ_STR)) { mStateMachine.sendMessage(P2P_PROV_DISC_PBC_REQ_EVENT, new WifiP2pProvDiscEvent(dataString)); diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index db539e4..fd76fc8 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -114,6 +114,7 @@ public class WifiStateMachine extends StateMachine { private final boolean mP2pSupported; private final AtomicBoolean mP2pConnected = new AtomicBoolean(false); + private boolean mTemporarilyDisconnectWifi = false; private final String mPrimaryDeviceType; /* Scan results handling */ @@ -358,8 +359,12 @@ public class WifiStateMachine extends StateMachine { static final int CMD_RESET_SUPPLICANT_STATE = BASE + 111; /* P2p commands */ + /* We are ok with no response here since we wont do much with it anyway */ public static final int CMD_ENABLE_P2P = BASE + 131; - public static final int CMD_DISABLE_P2P = BASE + 132; + /* In order to shut down supplicant cleanly, we wait till p2p has + * been disabled */ + public static final int CMD_DISABLE_P2P_REQ = BASE + 132; + public static final int CMD_DISABLE_P2P_RSP = BASE + 133; private static final int CONNECT_MODE = 1; private static final int SCAN_ONLY_MODE = 2; @@ -457,6 +462,11 @@ public class WifiStateMachine extends StateMachine { private State mDriverStartingState = new DriverStartingState(); /* Driver started */ private State mDriverStartedState = new DriverStartedState(); + /* Wait until p2p is disabled + * This is a special state which is entered right after we exit out of DriverStartedState + * before transitioning to another state. + */ + private State mWaitForP2pDisableState = new WaitForP2pDisableState(); /* Driver stopping */ private State mDriverStoppingState = new DriverStoppingState(); /* Driver stopped */ @@ -698,6 +708,7 @@ public class WifiStateMachine extends StateMachine { addState(mDisconnectingState, mConnectModeState); addState(mDisconnectedState, mConnectModeState); addState(mWpsRunningState, mConnectModeState); + addState(mWaitForP2pDisableState, mSupplicantStartedState); addState(mDriverStoppingState, mSupplicantStartedState); addState(mDriverStoppedState, mSupplicantStartedState); addState(mSupplicantStoppingState, mDefaultState); @@ -2017,6 +2028,10 @@ public class WifiStateMachine extends StateMachine { NetworkInfo info = (NetworkInfo) message.obj; mP2pConnected.set(info.isConnected()); break; + case WifiP2pService.DISCONNECT_WIFI_REQUEST: + mTemporarilyDisconnectWifi = (message.arg1 == 1); + replyToMessage(message, WifiP2pService.DISCONNECT_WIFI_RESPONSE); + break; default: loge("Error! unhandled message" + message); break; @@ -2428,7 +2443,11 @@ public class WifiStateMachine extends StateMachine { WifiConfiguration config; switch(message.what) { case CMD_STOP_SUPPLICANT: /* Supplicant stopped by user */ - transitionTo(mSupplicantStoppingState); + if (mP2pSupported) { + transitionTo(mWaitForP2pDisableState); + } else { + transitionTo(mSupplicantStoppingState); + } break; case WifiMonitor.SUP_DISCONNECTION_EVENT: /* Supplicant connection lost */ loge("Connection lost, restart supplicant"); @@ -2438,7 +2457,11 @@ public class WifiStateMachine extends StateMachine { handleNetworkDisconnect(); sendSupplicantConnectionChangedBroadcast(false); mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE); - transitionTo(mDriverLoadedState); + if (mP2pSupported) { + transitionTo(mWaitForP2pDisableState); + } else { + transitionTo(mDriverLoadedState); + } sendMessageDelayed(CMD_START_SUPPLICANT, SUPPLICANT_RESTART_INTERVAL_MSECS); break; case WifiMonitor.SCAN_RESULTS_EVENT: @@ -2833,8 +2856,12 @@ public class WifiStateMachine extends StateMachine { } mWakeLock.acquire(); mWifiNative.stopDriver(); - transitionTo(mDriverStoppingState); mWakeLock.release(); + if (mP2pSupported) { + transitionTo(mWaitForP2pDisableState); + } else { + transitionTo(mDriverStoppingState); + } break; case CMD_START_PACKET_FILTERING: if (message.arg1 == MULTICAST_V6) { @@ -2880,8 +2907,63 @@ public class WifiStateMachine extends StateMachine { mIsRunning = false; updateBatteryWorkSource(null); mScanResults = new ArrayList<ScanResult>(); + } + } - if (mP2pSupported) mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P); + class WaitForP2pDisableState extends State { + private State mTransitionToState; + @Override + public void enter() { + if (DBG) log(getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + switch (getCurrentMessage().what) { + case WifiMonitor.SUP_DISCONNECTION_EVENT: + mTransitionToState = mDriverLoadedState; + break; + case CMD_DELAYED_STOP_DRIVER: + mTransitionToState = mDriverStoppingState; + break; + case CMD_STOP_SUPPLICANT: + mTransitionToState = mSupplicantStoppingState; + break; + default: + mTransitionToState = mDriverStoppingState; + break; + } + mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_REQ); + } + @Override + public boolean processMessage(Message message) { + if (DBG) log(getName() + message.toString() + "\n"); + switch(message.what) { + case WifiStateMachine.CMD_DISABLE_P2P_RSP: + transitionTo(mTransitionToState); + break; + /* Defer wifi start/shut and driver commands */ + case CMD_LOAD_DRIVER: + case CMD_UNLOAD_DRIVER: + case CMD_START_SUPPLICANT: + case CMD_STOP_SUPPLICANT: + case CMD_START_AP: + case CMD_STOP_AP: + case CMD_START_DRIVER: + case CMD_STOP_DRIVER: + case CMD_SET_SCAN_MODE: + case CMD_SET_SCAN_TYPE: + case CMD_SET_COUNTRY_CODE: + case CMD_SET_FREQUENCY_BAND: + case CMD_START_PACKET_FILTERING: + case CMD_STOP_PACKET_FILTERING: + case CMD_START_SCAN: + case CMD_DISCONNECT: + case CMD_REASSOCIATE: + case CMD_RECONNECT: + deferMessage(message); + break; + default: + return NOT_HANDLED; + } + return HANDLED; } } @@ -3030,6 +3112,15 @@ public class WifiStateMachine extends StateMachine { transitionTo(mDisconnectedState); } break; + case WifiP2pService.DISCONNECT_WIFI_REQUEST: + if (message.arg1 == 1) { + mWifiNative.disconnect(); + mTemporarilyDisconnectWifi = true; + } else { + mWifiNative.reconnect(); + mTemporarilyDisconnectWifi = false; + } + break; /* Do a redundant disconnect without transition */ case CMD_DISCONNECT: mWifiNative.disconnect(); @@ -3159,6 +3250,13 @@ public class WifiStateMachine extends StateMachine { mWifiNative.disconnect(); transitionTo(mDisconnectingState); break; + case WifiP2pService.DISCONNECT_WIFI_REQUEST: + if (message.arg1 == 1) { + mWifiNative.disconnect(); + mTemporarilyDisconnectWifi = true; + transitionTo(mDisconnectingState); + } + break; case CMD_SET_SCAN_MODE: if (message.arg1 == SCAN_ONLY_MODE) { sendMessage(CMD_DISCONNECT); @@ -3465,6 +3563,13 @@ public class WifiStateMachine extends StateMachine { if (DBG) log(getName() + "\n"); EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + // We dont scan frequently if this is a temporary disconnect + // due to p2p + if (mTemporarilyDisconnectWifi) { + mWifiP2pChannel.sendMessage(WifiP2pService.DISCONNECT_WIFI_RESPONSE); + return; + } + mFrameworkScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(), Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS, mDefaultFrameworkScanIntervalMs); @@ -3579,6 +3684,12 @@ public class WifiStateMachine extends StateMachine { sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN, ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs); } + case CMD_RECONNECT: + case CMD_REASSOCIATE: + // Drop a third party reconnect/reassociate if we are + // tempoarily disconnected for p2p + if (mTemporarilyDisconnectWifi) ret = NOT_HANDLED; + break; default: ret = NOT_HANDLED; } diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java index 9c727f9..c8f0712 100644 --- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java +++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java @@ -556,8 +556,8 @@ public class WifiWatchdogStateMachine extends StateMachine { mLinkProperties = (LinkProperties) intent.getParcelableExtra( WifiManager.EXTRA_LINK_PROPERTIES); if (mPoorNetworkDetectionEnabled) { - if (mWifiInfo == null) { - if (DBG) logd("Ignoring link verification, mWifiInfo is NULL"); + if (mWifiInfo == null || mCurrentBssid == null) { + loge("Ignore, wifiinfo " + mWifiInfo +" bssid " + mCurrentBssid); sendLinkStatusNotification(true); } else { transitionTo(mVerifyingLinkState); @@ -726,7 +726,7 @@ public class WifiWatchdogStateMachine extends StateMachine { } private void handleRssiChange() { - if (mCurrentSignalLevel <= LINK_MONITOR_LEVEL_THRESHOLD) { + if (mCurrentSignalLevel <= LINK_MONITOR_LEVEL_THRESHOLD && mCurrentBssid != null) { transitionTo(mLinkMonitoringState); } else { // stay here @@ -920,11 +920,15 @@ public class WifiWatchdogStateMachine extends StateMachine { if (DBG) logd("########################################"); if (isGood) { mWsmChannel.sendMessage(GOOD_LINK_DETECTED); - mCurrentBssid.mLastTimeGood = SystemClock.elapsedRealtime(); - logd("Good link notification is sent"); + if (mCurrentBssid != null) { + mCurrentBssid.mLastTimeGood = SystemClock.elapsedRealtime(); + } + if (DBG) logd("Good link notification is sent"); } else { mWsmChannel.sendMessage(POOR_LINK_DETECTED); - mCurrentBssid.mLastTimePoor = SystemClock.elapsedRealtime(); + if (mCurrentBssid != null) { + mCurrentBssid.mLastTimePoor = SystemClock.elapsedRealtime(); + } logd("Poor link notification is sent"); } } diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java index 70baf13..7f32431 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java @@ -134,6 +134,9 @@ public class WifiP2pService extends IWifiP2pManager.Stub { private static final int GROUP_CREATING_WAIT_TIME_MS = 120 * 1000; private static int mGroupCreatingTimeoutIndex = 0; + private static final int DISABLE_P2P_WAIT_TIME_MS = 5 * 1000; + private static int mDisableP2pTimeoutIndex = 0; + /* Set a two minute discover timeout to avoid STA scans from being blocked */ private static final int DISCOVER_TIMEOUT_S = 120; @@ -149,9 +152,30 @@ public class WifiP2pService extends IWifiP2pManager.Stub { private static final int PEER_CONNECTION_USER_ACCEPT = BASE + 2; /* User rejected a peer request */ private static final int PEER_CONNECTION_USER_REJECT = BASE + 3; + /* User wants to disconnect wifi in favour of p2p */ + private static final int DROP_WIFI_USER_ACCEPT = BASE + 4; + /* User wants to keep his wifi connection and drop p2p */ + private static final int DROP_WIFI_USER_REJECT = BASE + 5; + /* Delayed message to timeout p2p disable */ + public static final int DISABLE_P2P_TIMED_OUT = BASE + 6; + /* Commands to the WifiStateMachine */ - public static final int P2P_CONNECTION_CHANGED = BASE + 11; + public static final int P2P_CONNECTION_CHANGED = BASE + 11; + + /* These commands are used to tempoarily disconnect wifi when we detect + * a frequency conflict which would make it impossible to have with p2p + * and wifi active at the same time. + * + * If the user chooses to disable wifi tempoarily, we keep wifi disconnected + * until the p2p connection is done and terminated at which point we will + * bring back wifi up + * + * DISCONNECT_WIFI_REQUEST + * msg.arg1 = 1 enables temporary disconnect and 0 disables it. + */ + public static final int DISCONNECT_WIFI_REQUEST = BASE + 12; + public static final int DISCONNECT_WIFI_RESPONSE = BASE + 13; private final boolean mP2pSupported; @@ -172,6 +196,8 @@ public class WifiP2pService extends IWifiP2pManager.Stub { private NetworkInfo mNetworkInfo; + private boolean mTempoarilyDisconnectedWifi = false; + /* The transaction Id of service discovery request */ private byte mServiceTransactionId = 0; @@ -222,7 +248,7 @@ public class WifiP2pService extends IWifiP2pManager.Stub { PREVIOUS_PROTOCOL_ERROR, /* There is no common channels the both devices can use. */ - NO_COMMON_CHANNE, + NO_COMMON_CHANNEL, /* Unknown p2p group. For example, Device A tries to invoke the previous persistent group, * but device B has removed the specified credential already. */ @@ -257,7 +283,7 @@ public class WifiP2pService extends IWifiP2pManager.Stub { case 6: return PREVIOUS_PROTOCOL_ERROR; case 7: - return NO_COMMON_CHANNE; + return NO_COMMON_CHANNEL; case 8: return UNKNOWN_P2P_GROUP; case 9: @@ -346,6 +372,7 @@ public class WifiP2pService extends IWifiP2pManager.Stub { = new UserAuthorizingInvitationState(); private ProvisionDiscoveryState mProvisionDiscoveryState = new ProvisionDiscoveryState(); private GroupNegotiationState mGroupNegotiationState = new GroupNegotiationState(); + private FrequencyConflictState mFrequencyConflictState =new FrequencyConflictState(); private GroupCreatedState mGroupCreatedState = new GroupCreatedState(); private UserAuthorizingJoinState mUserAuthorizingJoinState = new UserAuthorizingJoinState(); @@ -400,6 +427,7 @@ public class WifiP2pService extends IWifiP2pManager.Stub { addState(mUserAuthorizingInvitationState, mGroupCreatingState); addState(mProvisionDiscoveryState, mGroupCreatingState); addState(mGroupNegotiationState, mGroupCreatingState); + addState(mFrequencyConflictState, mGroupCreatingState); addState(mGroupCreatedState, mP2pEnabledState); addState(mUserAuthorizingJoinState, mGroupCreatedState); addState(mOngoingGroupRemovalState, mGroupCreatedState); @@ -551,16 +579,25 @@ public class WifiP2pService extends IWifiP2pManager.Stub { case WifiMonitor.P2P_DEVICE_LOST_EVENT: case WifiMonitor.P2P_FIND_STOPPED_EVENT: case WifiMonitor.P2P_SERV_DISC_RESP_EVENT: - case WifiStateMachine.CMD_ENABLE_P2P: - case WifiStateMachine.CMD_DISABLE_P2P: case PEER_CONNECTION_USER_ACCEPT: case PEER_CONNECTION_USER_REJECT: + case DISCONNECT_WIFI_RESPONSE: + case DROP_WIFI_USER_ACCEPT: + case DROP_WIFI_USER_REJECT: case GROUP_CREATING_TIMED_OUT: + case DISABLE_P2P_TIMED_OUT: case DhcpStateMachine.CMD_PRE_DHCP_ACTION: case DhcpStateMachine.CMD_POST_DHCP_ACTION: case DhcpStateMachine.CMD_ON_QUIT: case WifiMonitor.P2P_PROV_DISC_FAILURE_EVENT: break; + case WifiStateMachine.CMD_ENABLE_P2P: + // Enable is lazy and has no response + break; + case WifiStateMachine.CMD_DISABLE_P2P_REQ: + // If we end up handling in default, p2p is not enabled + mWifiChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_RSP); + break; /* unexpected group created, remove */ case WifiMonitor.P2P_GROUP_STARTED_EVENT: mGroup = (WifiP2pGroup) message.obj; @@ -663,6 +700,13 @@ public class WifiP2pService extends IWifiP2pManager.Stub { class P2pDisablingState extends State { @Override + public void enter() { + if (DBG) logd(getName()); + sendMessageDelayed(obtainMessage(DISABLE_P2P_TIMED_OUT, + ++mDisableP2pTimeoutIndex, 0), DISABLE_P2P_WAIT_TIME_MS); + } + + @Override public boolean processMessage(Message message) { if (DBG) logd(getName() + message.toString()); switch (message.what) { @@ -671,14 +715,25 @@ public class WifiP2pService extends IWifiP2pManager.Stub { transitionTo(mP2pDisabledState); break; case WifiStateMachine.CMD_ENABLE_P2P: - case WifiStateMachine.CMD_DISABLE_P2P: + case WifiStateMachine.CMD_DISABLE_P2P_REQ: deferMessage(message); break; + case DISABLE_P2P_TIMED_OUT: + if (mGroupCreatingTimeoutIndex == message.arg1) { + loge("P2p disable timed out"); + transitionTo(mP2pDisabledState); + } + break; default: return NOT_HANDLED; } return HANDLED; } + + @Override + public void exit() { + mWifiChannel.sendMessage(WifiStateMachine.CMD_DISABLE_P2P_RSP); + } } class P2pDisabledState extends State { @@ -702,9 +757,6 @@ public class WifiP2pService extends IWifiP2pManager.Stub { mWifiMonitor.startMonitoring(); transitionTo(mP2pEnablingState); break; - case WifiStateMachine.CMD_DISABLE_P2P: - //Nothing to do - break; default: return NOT_HANDLED; } @@ -731,7 +783,7 @@ public class WifiP2pService extends IWifiP2pManager.Stub { transitionTo(mP2pDisabledState); break; case WifiStateMachine.CMD_ENABLE_P2P: - case WifiStateMachine.CMD_DISABLE_P2P: + case WifiStateMachine.CMD_DISABLE_P2P_REQ: deferMessage(message); break; default: @@ -762,7 +814,7 @@ public class WifiP2pService extends IWifiP2pManager.Stub { case WifiStateMachine.CMD_ENABLE_P2P: //Nothing to do break; - case WifiStateMachine.CMD_DISABLE_P2P: + case WifiStateMachine.CMD_DISABLE_P2P_REQ: if (mPeers.clear()) sendP2pPeersChangedBroadcast(); if (mGroups.clear()) sendP2pPersistentGroupsChangedBroadcast(); @@ -1027,20 +1079,7 @@ public class WifiP2pService extends IWifiP2pManager.Stub { // remain at this state. } break; - case WifiMonitor.P2P_GROUP_STARTED_EVENT: - mGroup = (WifiP2pGroup) message.obj; - if (DBG) logd(getName() + " group started"); - - if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) { - // This is an invocation case. - mAutonomousGroup = false; - deferMessage(message); - transitionTo(mGroupNegotiationState); - } else { - return NOT_HANDLED; - } - break; - default: + default: return NOT_HANDLED; } return HANDLED; @@ -1074,6 +1113,10 @@ public class WifiP2pService extends IWifiP2pManager.Stub { // mSavedPeerConfig can be empty if (mSavedPeerConfig != null && !mSavedPeerConfig.deviceAddress.equals(device.deviceAddress)) { + if (DBG) { + logd("mSavedPeerConfig " + mSavedPeerConfig.deviceAddress + + "device " + device.deviceAddress); + } // Do the regular device lost handling ret = NOT_HANDLED; break; @@ -1269,6 +1312,12 @@ public class WifiP2pService extends IWifiP2pManager.Stub { transitionTo(mGroupCreatedState); break; case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT: + P2pStatus status = (P2pStatus) message.obj; + if (status == P2pStatus.NO_COMMON_CHANNEL) { + transitionTo(mFrequencyConflictState); + break; + } + /* continue with group removal handling */ case WifiMonitor.P2P_GROUP_REMOVED_EVENT: if (DBG) logd(getName() + " go failure"); handleGroupCreationFailure(); @@ -1278,9 +1327,14 @@ public class WifiP2pService extends IWifiP2pManager.Stub { // a group removed event. Flushing things at group formation // failure causes supplicant issues. Ignore right now. case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT: + status = (P2pStatus) message.obj; + if (status == P2pStatus.NO_COMMON_CHANNEL) { + transitionTo(mFrequencyConflictState); + break; + } break; case WifiMonitor.P2P_INVITATION_RESULT_EVENT: - P2pStatus status = (P2pStatus)message.obj; + status = (P2pStatus)message.obj; if (status == P2pStatus.SUCCESS) { // invocation was succeeded. // wait P2P_GROUP_STARTED_EVENT. @@ -1300,6 +1354,8 @@ public class WifiP2pService extends IWifiP2pManager.Stub { handleGroupCreationFailure(); transitionTo(mInactiveState); } + } else if (status == P2pStatus.NO_COMMON_CHANNEL) { + transitionTo(mFrequencyConflictState); } else { handleGroupCreationFailure(); transitionTo(mInactiveState); @@ -1312,7 +1368,90 @@ public class WifiP2pService extends IWifiP2pManager.Stub { } } + class FrequencyConflictState extends State { + private AlertDialog mFrequencyConflictDialog; + @Override + public void enter() { + if (DBG) logd(getName()); + notifyFrequencyConflict(); + } + private void notifyFrequencyConflict() { + logd("Notify frequency conflict"); + Resources r = Resources.getSystem(); + + AlertDialog dialog = new AlertDialog.Builder(mContext) + .setMessage(r.getString(R.string.wifi_p2p_frequency_conflict_message, + getDeviceName(mSavedPeerConfig.deviceAddress))) + .setPositiveButton(r.getString(R.string.dlg_ok), new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + sendMessage(DROP_WIFI_USER_ACCEPT); + } + }) + .setNegativeButton(r.getString(R.string.decline), new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + sendMessage(DROP_WIFI_USER_REJECT); + } + }) + .setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface arg0) { + sendMessage(DROP_WIFI_USER_REJECT); + } + }) + .create(); + + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); + dialog.show(); + mFrequencyConflictDialog = dialog; + } + + @Override + public boolean processMessage(Message message) { + if (DBG) logd(getName() + message.toString()); + switch (message.what) { + case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT: + case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT: + loge(getName() + "group sucess during freq conflict!"); + break; + case WifiMonitor.P2P_GROUP_STARTED_EVENT: + loge(getName() + "group started after freq conflict, handle anyway"); + deferMessage(message); + transitionTo(mGroupNegotiationState); + break; + case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT: + case WifiMonitor.P2P_GROUP_REMOVED_EVENT: + case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT: + // Ignore failures since we retry again + break; + case DROP_WIFI_USER_REJECT: + // User rejected dropping wifi in favour of p2p + handleGroupCreationFailure(); + transitionTo(mInactiveState); + break; + case DROP_WIFI_USER_ACCEPT: + // User accepted dropping wifi in favour of p2p + mWifiChannel.sendMessage(WifiP2pService.DISCONNECT_WIFI_REQUEST, 1); + mTempoarilyDisconnectedWifi = true; + break; + case DISCONNECT_WIFI_RESPONSE: + // Got a response from wifistatemachine, retry p2p + if (DBG) logd(getName() + "Wifi disconnected, retry p2p"); + transitionTo(mInactiveState); + sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + + public void exit() { + if (mFrequencyConflictDialog != null) mFrequencyConflictDialog.dismiss(); + } + } class GroupCreatedState extends State { @Override @@ -1421,7 +1560,7 @@ public class WifiP2pService extends IWifiP2pManager.Stub { } // Do the regular device lost handling return NOT_HANDLED; - case WifiStateMachine.CMD_DISABLE_P2P: + case WifiStateMachine.CMD_DISABLE_P2P_REQ: sendMessage(WifiP2pManager.REMOVE_GROUP); deferMessage(message); break; @@ -2195,6 +2334,11 @@ public class WifiP2pService extends IWifiP2pManager.Stub { mPeersLostDuringConnection.clear(); mServiceDiscReqId = null; if (changed) sendP2pPeersChangedBroadcast(); + + if (mTempoarilyDisconnectedWifi) { + mWifiChannel.sendMessage(WifiP2pService.DISCONNECT_WIFI_REQUEST, 0); + mTempoarilyDisconnectedWifi = false; + } } //State machine initiated requests can have replyTo set to null indicating |
