Merge "Magnifier not respecting RTL/LTR direction and keyguard dialogs not properly centered." into jb-mr1-dev
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 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 @@
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.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 @@
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 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 @@
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.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 @@
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 @@
}
}
+ /**
+ * 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 @@
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 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/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 @@
* 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 @@
* 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 @@
/**
* 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 @@
/**
* 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/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_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
new file mode 100644
index 0000000..35f27df
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_notify_wifidisplay.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_notify_wifidisplay.png b/core/res/res/drawable-mdpi/ic_notify_wifidisplay.png
new file mode 100644
index 0000000..f9c8678
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_notify_wifidisplay.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_notify_wifidisplay.png b/core/res/res/drawable-xhdpi/ic_notify_wifidisplay.png
new file mode 100644
index 0000000..4cc0ee8
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_notify_wifidisplay.png
Binary files differ
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/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 @@
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 @@
})
.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 @@
<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 @@
}
// 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 @@
</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 @@
.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 @@
-
-
<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 @@
<pre>
public void confirmFireMissiles() {
- DialogFragment newFragment = FireMissilesDialog.newInstance(this);
+ DialogFragment newFragment = new FireMissilesDialogFragment();
newFragment.show(getSupportFragmentManager(), "missiles");
}
</pre>
@@ -653,7 +648,7 @@
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 @@
<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 @@
}
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::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 @@
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 @@
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 @@
}
}
+ if (transform && !transform->isIdentity()) {
+ restore();
+ }
+
return DrawGlInfo::kStatusDrew;
}
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 @@
/** @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/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/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 @@
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 @@
}
synchronized (mLock) {
- mBackgroundWidth = mBackgroundHeight = -1;
+ mLastSurfaceWidth = mLastSurfaceHeight = -1;
mBackground = null;
mRedrawNeeded = true;
drawFrameLocked();
@@ -172,6 +172,9 @@
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 @@
@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 @@
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 @@
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 @@
}
- 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 @@
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 @@
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 @@
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 @@
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 a857eba..248a516 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -35,7 +35,7 @@
View mHandleView;
int mFingers;
PhoneStatusBar mStatusBar;
- private boolean mFlipped;
+ boolean mOkToFlip;
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -94,10 +94,10 @@
if (PhoneStatusBar.SETTINGS_DRAG_SHORTCUT && mStatusBar.mHasFlipSettings) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
- mFlipped = false;
+ mOkToFlip = getExpandedHeight() == 0;
break;
case MotionEvent.ACTION_POINTER_DOWN:
- if (!mFlipped) {
+ if (mOkToFlip) {
float miny = event.getY(0);
float maxy = miny;
for (int i=1; i<event.getPointerCount(); i++) {
@@ -111,7 +111,7 @@
} else {
mStatusBar.flipToSettings();
}
- mFlipped = true;
+ mOkToFlip = false;
}
}
break;
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 27a4db5..1c4dff8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1487,6 +1487,7 @@
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);
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 @@
int mCarDockRotation;
int mDeskDockRotation;
int mHdmiRotation;
+ boolean mHdmiRotationLock;
int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
int mUserRotation = Surface.ROTATION_0;
@@ -1035,11 +1036,14 @@
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 @@
// 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 @@
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 @@
/**
* 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 @@
if (mScreenOn) {
mKeyguardView.show();
+ mKeyguardView.requestFocus();
}
}
@@ -314,22 +315,25 @@
// 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 @@
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 @@
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 @@
mScreenOn = true;
cancelDoKeyguardLaterLocked();
if (DEBUG) Log.d(TAG, "onScreenTurnedOn, seq = " + mDelayedShowingSequence);
- if (showListener != null) {
- notifyScreenOnLocked(showListener);
- }
+ notifyScreenOnLocked(showListener);
}
maybeSendUserPresentBroadcast();
}
@@ -1365,7 +1363,7 @@
/**
* 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 @@
if (MORE_DEBUG) Slog.v(TAG, " removing participant " + packageName);
removeEverBackedUp(packageName);
set.remove(packageName);
+ mPendingBackups.remove(packageName);
}
}
@@ -1625,6 +1626,7 @@
} catch (InterruptedException e) {
// just bail
if (DEBUG) Slog.w(TAG, "Interrupted: " + e);
+ mActivityManager.clearPendingBackup();
return null;
}
}
@@ -1632,6 +1634,7 @@
// 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.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 @@
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 @@
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 @@
*/
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 @@
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 @@
} else {
mKey = intent;
}
- mPermission = checkPermission();
+ mAllowedResolutionLevel = getAllowedResolutionLevel(pid, uid);
mUid = uid;
mPid = pid;
mPackageName = packageName;
@@ -440,7 +513,7 @@
// 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 @@
// 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 @@
// 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 @@
}
/**
- * 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 void checkGeofencePermission() {
- if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) !=
- PackageManager.PERMISSION_GRANTED) {
+ 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 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 @@
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 @@
*/
@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 @@
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 @@
@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 @@
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 @@
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 @@
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 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 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 @@
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 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 @@
@Override
public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
- checkGeofencePermission();
+ checkResolutionLevelIsSufficientForGeofenceUse(getCallerAllowedResolutionLevel());
checkPendingIntent(intent);
checkPackageName(packageName);
@@ -1272,10 +1382,8 @@
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 @@
// 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 @@
return null;
}
- checkPermissionForProvider(getBestCallingPermission(), provider);
+ checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
+ provider);
LocationProviderInterface p;
synchronized (mLock) {
@@ -1357,7 +1467,8 @@
@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 @@
}
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 @@
*/
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 @@
// 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 @@
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 @@
// 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 @@
| 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 @@
}
// 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 @@
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/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 7132e1e..1737876a 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -11119,8 +11119,8 @@
// 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 @@
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 @@
}
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;
- }
-
- ProcessRecord proc = mBackupTarget.app;
- mBackupTarget = null;
- mBackupAppName = null;
-
- // Not backing this app up any more; reset its OOM adjustment
- 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();
+ try {
+ 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;
+ }
+
+ // 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();
+ }
+ }
+ } 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 @@
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 @@
*/
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 @@
&& xDpi == other.xDpi
&& yDpi == other.yDpi
&& flags == other.flags
- && touch == other.touch;
+ && touch == other.touch
+ && rotation == other.rotation;
}
@Override
@@ -157,14 +180,18 @@
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 @@
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..e58a0a5cb 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -127,6 +127,13 @@
// 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 @@
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 @@
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 @@
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.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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height,
refreshRate, deviceFlags, surface);
sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED);
+
+ scheduleUpdateNotificationLocked();
}
private void handleDisconnectLocked() {
@@ -273,6 +315,8 @@
mDisplayDevice.clearSurfaceLocked();
sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED);
mDisplayDevice = null;
+
+ scheduleUpdateNotificationLocked();
}
}
@@ -280,28 +324,81 @@
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);
+ }
+ }
- 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());
+ // Runs on the handler.
+ private void handleSendStatusChangeBroadcast() {
+ final Intent intent;
+ synchronized (getSyncRoot()) {
+ if (!mPendingStatusChangeBroadcast) {
+ return;
}
- // Send protected broadcast about wifi display status to registered receivers.
- getContext().sendBroadcast(intent);
+ 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;
+ }
+
+ 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 @@
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 @@
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 @@
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 @@
*/
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 @@
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 @@
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 @@
}
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 @@
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 @@
*/
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 @@
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 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
}
/**
+ * 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 @@
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 @@
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 @@
}
}
+ 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 @@
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/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 196bf2e..fd76fc8d 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -359,8 +359,12 @@
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;
@@ -458,6 +462,11 @@
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 */
@@ -699,6 +708,7 @@
addState(mDisconnectingState, mConnectModeState);
addState(mDisconnectedState, mConnectModeState);
addState(mWpsRunningState, mConnectModeState);
+ addState(mWaitForP2pDisableState, mSupplicantStartedState);
addState(mDriverStoppingState, mSupplicantStartedState);
addState(mDriverStoppedState, mSupplicantStartedState);
addState(mSupplicantStoppingState, mDefaultState);
@@ -2433,7 +2443,11 @@
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");
@@ -2443,7 +2457,11 @@
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:
@@ -2838,8 +2856,12 @@
}
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) {
@@ -2885,8 +2907,63 @@
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;
}
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 4b90901..7f32431 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -134,6 +134,9 @@
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;
@@ -153,6 +156,8 @@
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 */
@@ -574,19 +579,25 @@
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;
@@ -689,6 +700,13 @@
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) {
@@ -697,14 +715,25 @@
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 {
@@ -728,9 +757,6 @@
mWifiMonitor.startMonitoring();
transitionTo(mP2pEnablingState);
break;
- case WifiStateMachine.CMD_DISABLE_P2P:
- //Nothing to do
- break;
default:
return NOT_HANDLED;
}
@@ -757,7 +783,7 @@
transitionTo(mP2pDisabledState);
break;
case WifiStateMachine.CMD_ENABLE_P2P:
- case WifiStateMachine.CMD_DISABLE_P2P:
+ case WifiStateMachine.CMD_DISABLE_P2P_REQ:
deferMessage(message);
break;
default:
@@ -788,7 +814,7 @@
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();
@@ -1534,7 +1560,7 @@
}
// 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;