}
public void systemReady() {
- initUser(UserHandle.USER_OWNER);
}
private UserManager getUserManager() {
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();
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;
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");
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;
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;
}
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;
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);
}
}
}
}
+ /**
+ * 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.
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
public SyncQueue(SyncStorageEngine syncStorageEngine, final SyncAdaptersCache syncAdapters) {
mSyncStorageEngine = syncStorageEngine;
mSyncAdapters = syncAdapters;
-
- addPendingOperations(UserHandle.USER_OWNER);
}
public void addPendingOperations(int userId) {
* <h2>The 4 steps</h2>
* <p>When an asynchronous task is executed, the task goes through 4 steps:</p>
* <ol>
- * <li>{@link #onPreExecute()}, invoked on the UI thread immediately after the task
+ * <li>{@link #onPreExecute()}, invoked on the UI thread before the task
* is executed. This step is normally used to setup the task, for instance by
* showing a progress bar in the user interface.</li>
* <li>{@link #doInBackground}, invoked on the background thread
* 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>
* 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() + "]";
/**
* 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)
/**
* 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.
// Intersect with the bounds of the window to skip
// updates that lie outside of the visible region
final float appScale = mAttachInfo.mApplicationScale;
- localDirty.intersect(0, 0,
- (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
-
- if (!mWillDrawSoon) {
- scheduleTraversals();
+ if (localDirty.intersect(0, 0,
+ (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f))) {
+ if (!mWillDrawSoon) {
+ scheduleTraversals();
+ }
+ } else {
+ localDirty.setEmpty();
}
return null;
static void android_view_GLES20Canvas_setTextureLayerTransform(JNIEnv* env, jobject clazz,
Layer* layer, SkMatrix* matrix) {
-
layer->getTransform().load(*matrix);
}
<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>
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>
<!-- 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>
<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" />
<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" />
<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" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
</manifest>
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
})
.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();
<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.
}
// 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
</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
.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();
-
-
<h2 id="ShowingADialog">Showing a Dialog</h2>
<p>When you want to show your dialog, create an instance of your {@link
<pre>
public void confirmFireMissiles() {
- DialogFragment newFragment = FireMissilesDialog.newInstance(this);
+ DialogFragment newFragment = new FireMissilesDialogFragment();
newFragment.show(getSupportFragmentManager(), "missiles");
}
</pre>
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
<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
}
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);
}
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();
glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
finishDrawTexture();
-
- if (!transform.isIdentity()) {
- restore();
- }
}
void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) {
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;
}
}
}
+ if (transform && !transform->isIdentity()) {
+ restore();
+ }
+
return DrawGlInfo::kStatusDrew;
}
INIT_LOGD(" Maximum texture dimension is %d pixels", mMaxTextureSize);
mDebugEnabled = readDebugLevel() & kDebugCaches;
-
- mHasNPot = false; //Caches::getInstance().extensions.hasNPot();
}
///////////////////////////////////////////////////////////////////////////////
return;
}
+ // We could also enable mipmapping if both bitmap dimensions are powers
+ // of 2 but we'd have to deal with size changes. Let's keep this simple
+ const bool canMipMap = Caches::getInstance().extensions.hasNPot();
+
// If the texture had mipmap enabled but not anymore,
// force a glTexImage2D to discard the mipmap levels
const bool resize = !regenerate || bitmap->width() != int(texture->width) ||
bitmap->height() != int(texture->height) ||
- (regenerate && mHasNPot && texture->mipMap && !bitmap->hasHardwareMipMap());
+ (regenerate && canMipMap && texture->mipMap && !bitmap->hasHardwareMipMap());
if (!regenerate) {
glGenTextures(1, &texture->id);
break;
}
- if (mHasNPot) {
+ if (canMipMap) {
texture->mipMap = bitmap->hasHardwareMipMap();
if (texture->mipMap) {
glGenerateMipmap(GL_TEXTURE_2D);
float mFlushRate;
- bool mHasNPot;
bool mDebugEnabled;
Vector<SkBitmap*> mGarbage;
/** @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.
*
}
mMemWriter = new BufferedWriter(new FileWriter
(new File(MEDIA_MEMORY_OUTPUT), true));
+ mMemWriter.write(this.getName() + "\n");
}
@Override
-->
<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" />
<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>
<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"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_sysbar_quicksettings"
- android:contentDescription="@string/accessibility_settings_button"
+ android:contentDescription="@string/accessibility_desc_quick_settings"
/>
<ImageView
private WallpaperObserver mReceiver;
Bitmap mBackground;
- int mBackgroundWidth = -1, mBackgroundHeight = -1;
+ int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
int mLastRotation = -1;
float mXOffset;
float mYOffset;
}
synchronized (mLock) {
- mBackgroundWidth = mBackgroundHeight = -1;
+ mLastSurfaceWidth = mLastSurfaceHeight = -1;
mBackground = null;
mRedrawNeeded = true;
drawFrameLocked();
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();
@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
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 "
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) {
}
- void updateWallpaperLocked() {
+ private void updateWallpaperLocked() {
Throwable exception = null;
try {
+ mWallpaperManager.forgetLoadedWallpaper(); // force reload
mBackground = mWallpaperManager.getBitmap();
} catch (RuntimeException e) {
exception = e;
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) {
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();
}
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();
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);
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.MotionEvent;
import android.view.View;
import com.android.systemui.R;
Drawable mHandleBar;
float mHandleBarHeight;
View mHandleView;
+ int mFingers;
+ PhoneStatusBar mStatusBar;
+ boolean mOkToFlip;
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
}
+ public void setStatusBar(PhoneStatusBar bar) {
+ mStatusBar = bar;
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mHandleBar.draw(canvas);
canvas.translate(0, -off);
}
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (PhoneStatusBar.SETTINGS_DRAG_SHORTCUT && mStatusBar.mHasFlipSettings) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mOkToFlip = getExpandedHeight() == 0;
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ if (mOkToFlip) {
+ float miny = event.getY(0);
+ float maxy = miny;
+ for (int i=1; i<event.getPointerCount(); i++) {
+ final float y = event.getY(i);
+ if (y < miny) miny = y;
+ if (y > maxy) maxy = y;
+ }
+ if (maxy - miny < mHandleBarHeight) {
+ if (getMeasuredHeight() < mHandleBarHeight) {
+ mStatusBar.switchToSettings();
+ } else {
+ mStatusBar.flipToSettings();
+ }
+ mOkToFlip = false;
+ }
+ }
+ break;
+ }
+ }
+ return mHandleView.dispatchTouchEvent(event);
+ }
}
startOpeningPanel(panel);
}
final boolean result = mTouchingPanel != null
- ? mTouchingPanel.getHandle().dispatchTouchEvent(event)
+ ? mTouchingPanel.onTouchEvent(event)
: true;
return result;
}
case MotionEvent.ACTION_DOWN:
mTracking = true;
mHandleView.setPressed(true);
+ postInvalidate(); // catch the press state change
mInitialTouchY = y;
mVelocityTracker = VelocityTracker.obtain();
trackMovement(event);
mFinalTouchY = y;
mTracking = false;
mHandleView.setPressed(false);
+ postInvalidate(); // catch the press state change
mBar.onTrackingStopped(PanelView.this);
trackMovement(event);
public static final boolean ENABLE_NOTIFICATION_PANEL_CLING = false;
+ public static final boolean SETTINGS_DRAG_SHORTCUT = true;
+
// additional instrumentation for testing purposes; intended to be left on during development
public static final boolean CHATTY = DEBUG;
View mMoreIcon;
// expanded notifications
- PanelView mNotificationPanel; // the sliding/resizing panel within the notification window
+ NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
ScrollView mScrollView;
View mExpandedContents;
int mNotificationPanelGravity;
PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
mStatusBarView.setPanelHolder(holder);
- mNotificationPanel = (PanelView) mStatusBarWindow.findViewById(R.id.notification_panel);
+ mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(R.id.notification_panel);
+ mNotificationPanel.setStatusBar(this);
mNotificationPanelIsFullScreenWidth =
(mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT);
if (mHasFlipSettings
&& mFlipSettingsView != null
- && mFlipSettingsView.getVisibility() == View.VISIBLE) {
- // the flip settings panel is showing; we should not be shown
+ && mFlipSettingsView.getVisibility() == View.VISIBLE
+ && mScrollView.getVisibility() != View.VISIBLE) {
+ // the flip settings panel is unequivocally showing; we should not be shown
mClearButton.setVisibility(View.INVISIBLE);
} else if (mClearButton.isShown()) {
if (clearable != (mClearButton.getAlpha() == 1.0f)) {
}
}
+ public Handler getHandler() {
+ return mHandler;
+ }
+
View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
// Because 'v' is a ViewGroup, all its children will be (un)selected
return;
}
- if (mHasFlipSettings && !mExpandedVisible) {
- // reset things to their proper state
- mScrollView.setScaleX(1f);
- mScrollView.setVisibility(View.VISIBLE);
- mSettingsButton.setAlpha(1f);
- mSettingsButton.setVisibility(View.VISIBLE);
- mNotificationPanel.setVisibility(View.GONE);
- mFlipSettingsView.setVisibility(View.GONE);
- mNotificationButton.setVisibility(View.GONE);
- setAreThereNotifications(); // show the clear button
- }
-
mExpandedVisible = true;
mPile.setLayoutTransitionsEnabled(true);
if (mNavigationBarView != null)
}
mNotificationPanel.expand();
- if (mHasFlipSettings) {
- if (mScrollView.getVisibility() != View.VISIBLE) {
- if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
- if (mScrollViewAnim != null) mScrollViewAnim.cancel();
- if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel();
- if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
- if (mClearButtonAnim != null) mClearButtonAnim.cancel();
-
- mScrollView.setVisibility(View.VISIBLE);
- mScrollViewAnim = start(
- startDelay(FLIP_DURATION_OUT,
- interpolator(mDecelerateInterpolator,
- ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 0f, 1f)
- .setDuration(FLIP_DURATION_IN)
- )));
- mFlipSettingsViewAnim = start(
- setVisibilityWhenDone(
- interpolator(mAccelerateInterpolator,
- ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 1f, 0f)
- )
- .setDuration(FLIP_DURATION_OUT),
- mFlipSettingsView, View.INVISIBLE));
- mNotificationButtonAnim = start(
- setVisibilityWhenDone(
- ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 0f)
- .setDuration(FLIP_DURATION),
- mNotificationButton, View.INVISIBLE));
- mSettingsButton.setVisibility(View.VISIBLE);
- mSettingsButtonAnim = start(
- ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 1f)
- .setDuration(FLIP_DURATION));
- mClearButton.setVisibility(View.VISIBLE);
- mClearButton.setAlpha(0f);
- setAreThereNotifications(); // this will show/hide the button as necessary
- mNotificationPanel.postDelayed(new Runnable() {
- public void run() {
- updateCarrierLabelVisibility(false);
- }
- }, FLIP_DURATION - 150);
- }
+ if (mHasFlipSettings && mScrollView.getVisibility() != View.VISIBLE) {
+ flipToNotifications();
}
if (false) postStartTracing();
}
+ public void flipToNotifications() {
+ if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
+ if (mScrollViewAnim != null) mScrollViewAnim.cancel();
+ if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel();
+ if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
+ if (mClearButtonAnim != null) mClearButtonAnim.cancel();
+
+ mScrollView.setVisibility(View.VISIBLE);
+ mScrollViewAnim = start(
+ startDelay(FLIP_DURATION_OUT,
+ interpolator(mDecelerateInterpolator,
+ ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 0f, 1f)
+ .setDuration(FLIP_DURATION_IN)
+ )));
+ mFlipSettingsViewAnim = start(
+ setVisibilityWhenDone(
+ interpolator(mAccelerateInterpolator,
+ ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 1f, 0f)
+ )
+ .setDuration(FLIP_DURATION_OUT),
+ mFlipSettingsView, View.INVISIBLE));
+ mNotificationButtonAnim = start(
+ setVisibilityWhenDone(
+ ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 0f)
+ .setDuration(FLIP_DURATION),
+ mNotificationButton, View.INVISIBLE));
+ mSettingsButton.setVisibility(View.VISIBLE);
+ mSettingsButtonAnim = start(
+ ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 1f)
+ .setDuration(FLIP_DURATION));
+ mClearButton.setVisibility(View.VISIBLE);
+ mClearButton.setAlpha(0f);
+ setAreThereNotifications(); // this will show/hide the button as necessary
+ mNotificationPanel.postDelayed(new Runnable() {
+ public void run() {
+ updateCarrierLabelVisibility(false);
+ }
+ }, FLIP_DURATION - 150);
+ }
+
@Override
public void animateExpandSettingsPanel() {
if (SPEW) Slog.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
if (mHasFlipSettings) {
mNotificationPanel.expand();
if (mFlipSettingsView.getVisibility() != View.VISIBLE) {
- if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
- if (mScrollViewAnim != null) mScrollViewAnim.cancel();
- if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel();
- if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
- if (mClearButtonAnim != null) mClearButtonAnim.cancel();
-
- mFlipSettingsView.setVisibility(View.VISIBLE);
- mFlipSettingsView.setScaleX(0f);
- mFlipSettingsViewAnim = start(
- startDelay(FLIP_DURATION_OUT,
- interpolator(mDecelerateInterpolator,
- ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 0f, 1f)
- .setDuration(FLIP_DURATION_IN)
- )));
- mScrollViewAnim = start(
- setVisibilityWhenDone(
- interpolator(mAccelerateInterpolator,
- ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 1f, 0f)
- )
- .setDuration(FLIP_DURATION_OUT),
- mScrollView, View.INVISIBLE));
- mSettingsButtonAnim = start(
- setVisibilityWhenDone(
- ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 0f)
- .setDuration(FLIP_DURATION),
- mScrollView, View.INVISIBLE));
- mNotificationButton.setVisibility(View.VISIBLE);
- mNotificationButtonAnim = start(
- ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 1f)
- .setDuration(FLIP_DURATION));
- mClearButtonAnim = start(
- setVisibilityWhenDone(
- ObjectAnimator.ofFloat(mClearButton, View.ALPHA, 0f)
- .setDuration(FLIP_DURATION),
- mClearButton, View.INVISIBLE));
- mNotificationPanel.postDelayed(new Runnable() {
- public void run() {
- updateCarrierLabelVisibility(false);
- }
- }, FLIP_DURATION - 150);
+ flipToSettings();
}
} else if (mSettingsPanel != null) {
mSettingsPanel.expand();
if (false) postStartTracing();
}
+ public void switchToSettings() {
+ mFlipSettingsView.setScaleX(1f);
+ mFlipSettingsView.setVisibility(View.VISIBLE);
+ mSettingsButton.setVisibility(View.GONE);
+ mScrollView.setVisibility(View.GONE);
+ mScrollView.setScaleX(0f);
+ mNotificationButton.setVisibility(View.VISIBLE);
+ mNotificationButton.setAlpha(1f);
+ mClearButton.setVisibility(View.GONE);
+ }
+
+ public void flipToSettings() {
+ if (mFlipSettingsViewAnim != null) mFlipSettingsViewAnim.cancel();
+ if (mScrollViewAnim != null) mScrollViewAnim.cancel();
+ if (mSettingsButtonAnim != null) mSettingsButtonAnim.cancel();
+ if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel();
+ if (mClearButtonAnim != null) mClearButtonAnim.cancel();
+
+ mFlipSettingsView.setVisibility(View.VISIBLE);
+ mFlipSettingsView.setScaleX(0f);
+ mFlipSettingsViewAnim = start(
+ startDelay(FLIP_DURATION_OUT,
+ interpolator(mDecelerateInterpolator,
+ ObjectAnimator.ofFloat(mFlipSettingsView, View.SCALE_X, 0f, 1f)
+ .setDuration(FLIP_DURATION_IN)
+ )));
+ mScrollViewAnim = start(
+ setVisibilityWhenDone(
+ interpolator(mAccelerateInterpolator,
+ ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 1f, 0f)
+ )
+ .setDuration(FLIP_DURATION_OUT),
+ mScrollView, View.INVISIBLE));
+ mSettingsButtonAnim = start(
+ setVisibilityWhenDone(
+ ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 0f)
+ .setDuration(FLIP_DURATION),
+ mScrollView, View.INVISIBLE));
+ mNotificationButton.setVisibility(View.VISIBLE);
+ mNotificationButtonAnim = start(
+ ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 1f)
+ .setDuration(FLIP_DURATION));
+ mClearButtonAnim = start(
+ setVisibilityWhenDone(
+ ObjectAnimator.ofFloat(mClearButton, View.ALPHA, 0f)
+ .setDuration(FLIP_DURATION),
+ mClearButton, View.INVISIBLE));
+ mNotificationPanel.postDelayed(new Runnable() {
+ public void run() {
+ updateCarrierLabelVisibility(false);
+ }
+ }, FLIP_DURATION - 150);
+ }
+
+ public void flipPanels() {
+ if (mHasFlipSettings) {
+ if (mFlipSettingsView.getVisibility() != View.VISIBLE) {
+ flipToSettings();
+ } else {
+ flipToNotifications();
+ }
+ }
+ }
+
public void animateCollapseQuickSettings() {
mStatusBarView.collapseAllPanels(true);
}
// Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
mStatusBarView.collapseAllPanels(/*animate=*/ false);
+ if (mHasFlipSettings) {
+ // reset things to their proper state
+ mScrollView.setScaleX(1f);
+ mScrollView.setVisibility(View.VISIBLE);
+ mSettingsButton.setAlpha(1f);
+ mSettingsButton.setVisibility(View.VISIBLE);
+ mNotificationPanel.setVisibility(View.GONE);
+ mFlipSettingsView.setVisibility(View.GONE);
+ mNotificationButton.setVisibility(View.GONE);
+ setAreThereNotifications(); // show the clear button
+ }
+
mExpandedVisible = false;
mPile.setLayoutTransitionsEnabled(false);
if (mNavigationBarView != null)
int mCarDockRotation;
int mDeskDockRotation;
int mHdmiRotation;
+ boolean mHdmiRotationLock;
int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
int mUserRotation = Surface.ROTATION_0;
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() {
// 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;
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);
}
}
/**
* 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}.
*/
if (mScreenOn) {
mKeyguardView.show();
+ mKeyguardView.requestFocus();
}
}
// 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);
}
}
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;
}
/**
final KeyguardViewBase lastView = mKeyguardView;
mKeyguardView = null;
mKeyguardHost.postDelayed(new Runnable() {
+ @Override
public void run() {
synchronized (KeyguardViewManager.this) {
lastView.cleanUp();
mScreenOn = true;
cancelDoKeyguardLaterLocked();
if (DEBUG) Log.d(TAG, "onScreenTurnedOn, seq = " + mDelayedShowingSequence);
- if (showListener != null) {
- notifyScreenOnLocked(showListener);
- }
+ notifyScreenOnLocked(showListener);
}
maybeSendUserPresentBroadcast();
}
/**
* Handle message sent by {@link #verifyUnlock}
- * @see #RESET
+ * @see #VERIFY_UNLOCK
*/
private void handleVerifyUnlock() {
synchronized (KeyguardViewMediator.this) {
if (MORE_DEBUG) Slog.v(TAG, " removing participant " + packageName);
removeEverBackedUp(packageName);
set.remove(packageName);
+ mPendingBackups.remove(packageName);
}
}
} catch (InterruptedException e) {
// just bail
if (DEBUG) Slog.w(TAG, "Interrupted: " + e);
+ mActivityManager.clearPendingBackup();
return null;
}
}
// 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);
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;
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 =
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);
*/
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(
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;
} else {
mKey = intent;
}
- mPermission = checkPermission();
+ mAllowedResolutionLevel = getAllowedResolutionLevel(pid, uid);
mUid = uid;
mPid = pid;
mPackageName = packageName;
// 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();
// 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();
// 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();
}
/**
- * Returns the best permission available to the caller.
+ * Returns the permission string associated with the specified resolution level.
+ *
+ * @param resolutionLevel the resolution level
+ * @return the permission string
*/
- private String getBestCallingPermission() {
- if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) ==
- PackageManager.PERMISSION_GRANTED) {
- return ACCESS_FINE_LOCATION;
- } else if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) ==
- PackageManager.PERMISSION_GRANTED) {
- return ACCESS_COARSE_LOCATION;
+ private String getResolutionPermission(int resolutionLevel) {
+ switch (resolutionLevel) {
+ case RESOLUTION_LEVEL_FINE:
+ return android.Manifest.permission.ACCESS_FINE_LOCATION;
+ case RESOLUTION_LEVEL_COARSE:
+ return android.Manifest.permission.ACCESS_COARSE_LOCATION;
+ default:
+ return null;
}
- return null;
}
/**
- * Throw SecurityException if caller has neither COARSE or FINE.
- * Otherwise, return the best permission.
+ * Returns the resolution level allowed to the given PID/UID pair.
+ *
+ * @param pid the PID
+ * @param uid the UID
+ * @return resolution level allowed to the pid/uid pair
*/
- private String checkPermission() {
- String perm = getBestCallingPermission();
- if (perm == null) {
- throw new SecurityException("Location requires either ACCESS_COARSE_LOCATION or" +
- " ACCESS_FINE_LOCATION permission");
+ private int getAllowedResolutionLevel(int pid, int uid) {
+ if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
+ pid, uid) == PackageManager.PERMISSION_GRANTED) {
+ return RESOLUTION_LEVEL_FINE;
+ } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION,
+ pid, uid) == PackageManager.PERMISSION_GRANTED) {
+ return RESOLUTION_LEVEL_COARSE;
+ } else {
+ return RESOLUTION_LEVEL_NONE;
}
- return perm;
}
/**
- * Throw SecurityException if caller lacks permission to use Geofences.
+ * Returns the resolution level allowed to the caller
+ *
+ * @return resolution level allowed to caller
+ */
+ private int getCallerAllowedResolutionLevel() {
+ return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid());
+ }
+
+ /**
+ * Throw SecurityException if specified resolution level is insufficient to use geofences.
+ *
+ * @param allowedResolutionLevel resolution level allowed to caller
*/
- private void checkGeofencePermission() {
- if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) !=
- PackageManager.PERMISSION_GRANTED) {
+ private void checkResolutionLevelIsSufficientForGeofenceUse(int allowedResolutionLevel) {
+ if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
throw new SecurityException("Geofence usage requires ACCESS_FINE_LOCATION permission");
}
}
- private String getMinimumPermissionForProvider(String provider) {
+ /**
+ * Return the minimum resolution level required to use the specified location provider.
+ *
+ * @param provider the name of the location provider
+ * @return minimum resolution level required for provider
+ */
+ private int getMinimumResolutionLevelForProviderUse(String provider) {
if (LocationManager.GPS_PROVIDER.equals(provider) ||
LocationManager.PASSIVE_PROVIDER.equals(provider)) {
// gps and passive providers require FINE permission
- return ACCESS_FINE_LOCATION;
+ return RESOLUTION_LEVEL_FINE;
} else if (LocationManager.NETWORK_PROVIDER.equals(provider) ||
LocationManager.FUSED_PROVIDER.equals(provider)) {
// network and fused providers are ok with COARSE or FINE
- return ACCESS_COARSE_LOCATION;
+ return RESOLUTION_LEVEL_COARSE;
} else {
// mock providers
LocationProviderInterface lp = mMockProviders.get(provider);
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.");
}
}
}
*/
@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 {
if (LocationManager.FUSED_PROVIDER.equals(name)) {
continue;
}
- if (isPermissionSufficient(perm, getMinimumPermissionForProvider(name))) {
+ if (allowedResolutionLevel >= getMinimumResolutionLevelForProviderUse(name)) {
if (enabledOnly && !isAllowedBySettingsLocked(name, callingUserId)) {
continue;
}
@Override
public boolean providerMeetsCriteria(String provider, Criteria criteria) {
- checkPermission();
-
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) {
throw new IllegalArgumentException("provider=" + provider);
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) {
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();
long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- requestLocationUpdatesLocked(request, recevier, pid, uid, packageName);
+ requestLocationUpdatesLocked(sanitizedRequest, recevier, pid, uid, packageName);
}
} finally {
Binder.restoreCallingIdentity(identity);
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);
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 {
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;
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);
}
@Override
public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
- checkGeofencePermission();
+ checkResolutionLevelIsSufficientForGeofenceUse(getCallerAllowedResolutionLevel());
checkPendingIntent(intent);
checkPackageName(packageName);
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);
// 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)) {
return null;
}
- checkPermissionForProvider(getBestCallingPermission(), provider);
+ checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
+ provider);
LocationProviderInterface p;
synchronized (mLock) {
@Override
public boolean isProviderEnabled(String provider) {
- checkPermissionForProvider(getBestCallingPermission(), provider);
+ checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
+ provider);
if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
long identity = Binder.clearCallingIdentity();
}
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;
*/
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;
// 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;
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() {
// 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)));
| 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
}
// test input against each of the signature sets
- for (HashSet<Signature> referenceSet : mSignatureSets) {
+ for (HashSet<Signature> referenceSet : sigSets) {
if (referenceSet.equals(inputSet)) {
return true;
}
return false;
}
+ private boolean isSignatureMatch(Signature[] signatures) {
+ return isSignatureMatch(signatures, mSignatureSets);
+ }
+
private final PackageMonitor mPackageMonitor = new PackageMonitor() {
/**
* Called when package has been reinstalled
import android.os.ServiceManager;
import android.os.SystemClock;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Property;
import android.util.Slog;
import android.view.Display;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Locale;
/**
* This class handles the screen magnification when accessibility is enabled.
mViewport.recomputeBounds(mMagnificationController.isMagnifying());
} break;
}
- } else {
- switch (transition) {
- case WindowManagerPolicy.TRANSIT_ENTER:
- case WindowManagerPolicy.TRANSIT_SHOW: {
- if (!magnifying || !isScreenMagnificationAutoUpdateEnabled(mContext)) {
- break;
- }
- final int type = info.type;
- switch (type) {
- // TODO: Are these all the windows we want to make
- // visible when they appear on the screen?
- // Do we need to take some of them out?
- case WindowManager.LayoutParams.TYPE_APPLICATION:
- case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
- case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
- case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
- case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
- case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
- case WindowManager.LayoutParams.TYPE_PHONE:
- case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
- case WindowManager.LayoutParams.TYPE_TOAST:
- case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
- case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
- case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
- case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
- case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
- case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
- case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL:
- case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: {
- Rect magnifiedRegionBounds = mMagnificationController
- .getMagnifiedRegionBounds();
- Rect touchableRegion = info.touchableRegion;
- if (!magnifiedRegionBounds.intersect(touchableRegion)) {
- ensureRectangleInMagnifiedRegionBounds(
- magnifiedRegionBounds, touchableRegion);
- }
- } break;
- } break;
+ }
+ switch (transition) {
+ case WindowManagerPolicy.TRANSIT_ENTER:
+ case WindowManagerPolicy.TRANSIT_SHOW: {
+ if (!magnifying || !isScreenMagnificationAutoUpdateEnabled(mContext)) {
+ break;
}
+ final int type = info.type;
+ switch (type) {
+ // TODO: Are these all the windows we want to make
+ // visible when they appear on the screen?
+ // Do we need to take some of them out?
+ case WindowManager.LayoutParams.TYPE_APPLICATION:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
+ case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
+ case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
+ case WindowManager.LayoutParams.TYPE_PHONE:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
+ case WindowManager.LayoutParams.TYPE_TOAST:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
+ case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
+ case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
+ case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
+ case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
+ case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL:
+ case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: {
+ Rect magnifiedRegionBounds = mMagnificationController
+ .getMagnifiedRegionBounds();
+ Rect touchableRegion = info.touchableRegion;
+ if (!magnifiedRegionBounds.intersect(touchableRegion)) {
+ ensureRectangleInMagnifiedRegionBounds(
+ magnifiedRegionBounds, touchableRegion);
+ }
+ } break;
+ } break;
}
}
} finally {
final float scrollX;
final float scrollY;
if (rectangle.width() > magnifiedRegionBounds.width()) {
- scrollX = rectangle.left - magnifiedRegionBounds.left;
+ final int direction = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault());
+ if (direction == View.LAYOUT_DIRECTION_LTR) {
+ scrollX = rectangle.left - magnifiedRegionBounds.left;
+ } else {
+ scrollX = rectangle.right - magnifiedRegionBounds.right;
+ }
} else if (rectangle.left < magnifiedRegionBounds.left) {
scrollX = rectangle.left - magnifiedRegionBounds.left;
} else if (rectangle.right > magnifiedRegionBounds.right) {
// 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
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
}
synchronized(this) {
- if (mBackupAppName == null) {
- Slog.w(TAG, "Unbinding backup agent with no active backup");
- return;
- }
-
- if (!mBackupAppName.equals(appInfo.packageName)) {
- Slog.e(TAG, "Unbind of " + appInfo + " but is not the current backup target");
- return;
- }
+ try {
+ if (mBackupAppName == null) {
+ Slog.w(TAG, "Unbinding backup agent with no active backup");
+ return;
+ }
- ProcessRecord proc = mBackupTarget.app;
- mBackupTarget = null;
- mBackupAppName = null;
+ if (!mBackupAppName.equals(appInfo.packageName)) {
+ Slog.e(TAG, "Unbind of " + appInfo + " but is not the current backup target");
+ return;
+ }
- // Not backing this app up any more; reset its OOM adjustment
- updateOomAdjLocked(proc);
+ // Not backing this app up any more; reset its OOM adjustment
+ final ProcessRecord proc = mBackupTarget.app;
+ updateOomAdjLocked(proc);
- // If the app crashed during backup, 'thread' will be null here
- if (proc.thread != null) {
- try {
- proc.thread.scheduleDestroyBackupAgent(appInfo,
- compatibilityInfoForPackageLocked(appInfo));
- } catch (Exception e) {
- Slog.e(TAG, "Exception when unbinding backup agent:");
- e.printStackTrace();
+ // If the app crashed during backup, 'thread' will be null here
+ if (proc.thread != null) {
+ try {
+ proc.thread.scheduleDestroyBackupAgent(appInfo,
+ compatibilityInfoForPackageLocked(appInfo));
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception when unbinding backup agent:");
+ e.printStackTrace();
+ }
}
+ } finally {
+ mBackupTarget = null;
+ mBackupAppName = null;
}
}
}
package com.android.server.display;
import android.util.DisplayMetrics;
+import android.view.Surface;
import libcore.util.Objects;
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.
*/
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
&& xDpi == other.xDpi
&& yDpi == other.yDpi
&& flags == other.flags
- && touch == other.touch;
+ && touch == other.touch
+ && rotation == other.rotation;
}
@Override
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) {
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");
// 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>();
mHandler = new DisplayManagerHandler(mainHandler.getLooper());
mUiHandler = uiHandler;
mDisplayAdapterListener = new DisplayAdapterListener();
+ mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
mHandler.sendEmptyMessage(MSG_REGISTER_DEFAULT_DISPLAY_ADAPTER);
}
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);
pw.println(" mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId);
pw.println(" mDefaultViewport=" + mDefaultViewport);
pw.println(" mExternalTouchViewport=" + mExternalTouchViewport);
+ pw.println(" mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
ipw.increaseIndent();
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;
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;
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;
// 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.
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;
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;
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
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.
mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height,
refreshRate, deviceFlags, surface);
sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED);
+
+ scheduleUpdateNotificationLocked();
}
private void handleDisconnectLocked() {
mDisplayDevice.clearSurfaceLocked();
sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED);
mDisplayDevice = null;
+
+ scheduleUpdateNotificationLocked();
}
}
mCurrentStatus = null;
if (!mPendingStatusChangeBroadcast) {
mPendingStatusChangeBroadcast = true;
- getHandler().post(mStatusChangeBroadcast);
+ mHandler.sendEmptyMessage(MSG_SEND_STATUS_CHANGE_BROADCAST);
}
}
- private final Runnable mStatusChangeBroadcast = new Runnable() {
- @Override
- public void run() {
- final Intent intent;
- synchronized (getSyncRoot()) {
- if (!mPendingStatusChangeBroadcast) {
- return;
- }
+ private void scheduleUpdateNotificationLocked() {
+ if (!mPendingNotificationUpdate) {
+ mPendingNotificationUpdate = true;
+ mHandler.sendEmptyMessage(MSG_UPDATE_NOTIFICATION);
+ }
+ }
+
+ // Runs on the handler.
+ private void handleSendStatusChangeBroadcast() {
+ final Intent intent;
+ synchronized (getSyncRoot()) {
+ if (!mPendingStatusChangeBroadcast) {
+ return;
+ }
+
+ mPendingStatusChangeBroadcast = false;
+ intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS,
+ getWifiDisplayStatusLocked());
+ }
- mPendingStatusChangeBroadcast = false;
- intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS,
- getWifiDisplayStatusLocked());
+ // Send protected broadcast about wifi display status to registered receivers.
+ getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ // Runs on the handler.
+ private void handleUpdateNotification() {
+ final boolean isConnected;
+ synchronized (getSyncRoot()) {
+ if (!mPendingNotificationUpdate) {
+ return;
}
- // Send protected broadcast about wifi display status to registered receivers.
- getContext().sendBroadcast(intent);
+ mPendingNotificationUpdate = false;
+ isConnected = (mDisplayDevice != null);
+ }
+
+ mNotificationManager.cancelAsUser(null,
+ R.string.wifi_display_notification_title, UserHandle.ALL);
+
+ if (isConnected) {
+ Context context = getContext();
+
+ Resources r = context.getResources();
+ Notification notification = new Notification.Builder(context)
+ .setContentTitle(r.getString(
+ R.string.wifi_display_notification_title))
+ .setContentText(r.getString(
+ R.string.wifi_display_notification_message))
+ .setContentIntent(mSettingsPendingIntent)
+ .setSmallIcon(R.drawable.ic_notify_wifidisplay)
+ .setOngoing(true)
+ .addAction(android.R.drawable.ic_menu_close_clear_cancel,
+ r.getString(R.string.wifi_display_notification_disconnect),
+ mDisconnectPendingIntent)
+ .build();
+ mNotificationManager.notifyAsUser(null,
+ R.string.wifi_display_notification_title,
+ notification, UserHandle.ALL);
+ }
+ }
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(ACTION_DISCONNECT)) {
+ synchronized (getSyncRoot()) {
+ requestDisconnectLocked();
+ }
+ }
}
};
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;
+ }
+ }
+ }
}
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";
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;
*/
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;
}
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);
user.partial = true;
writeUserLocked(user);
}
-
+ if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle);
int res;
try {
res = ActivityManagerNative.getDefault().stopUser(userHandle,
}
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 {
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;
*/
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;
}
}
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.
// 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;
// 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;
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(
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);
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);
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,
}
/**
+ * 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.
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);
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="
}
}
+ 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);
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);
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"/>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"/>
- <ImageView
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"/>
- <ImageView
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_marginLeft="3dip"
- android:layout_marginRight="15dip"/>
</merge>
Capability.PLAY_ANIMATION,
Capability.ANIMATED_VIEW_MANIPULATION,
Capability.ADAPTER_BINDING,
- Capability.EXTENDED_VIEWINFO);
+ Capability.EXTENDED_VIEWINFO,
+ Capability.FIXED_SCALABLE_NINE_PATCH);
BridgeAssetManager.initSystem();
protected abstract TextView getStyleableTextView();
- protected CustomBar(Context context, Density density, String layoutPath, String name)
- throws XmlPullParserException {
+ protected CustomBar(Context context, Density density, int orientation, String layoutPath,
+ String name) throws XmlPullParserException {
super(context);
- setOrientation(LinearLayout.HORIZONTAL);
- setGravity(Gravity.CENTER_VERTICAL);
+ setOrientation(orientation);
+ if (orientation == LinearLayout.HORIZONTAL) {
+ setGravity(Gravity.CENTER_VERTICAL);
+ } else {
+ setGravity(Gravity.CENTER_HORIZONTAL);
+ }
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
+import android.widget.LinearLayout;
import android.widget.TextView;
public class FakeActionBar extends CustomBar {
public FakeActionBar(Context context, Density density, String label, String icon)
throws XmlPullParserException {
- super(context, density, "/bars/action_bar.xml", "action_bar.xml");
+ super(context, density, LinearLayout.HORIZONTAL, "/bars/action_bar.xml", "action_bar.xml");
// Cannot access the inside items through id because no R.id values have been
// created for them.
package com.android.layoutlib.bridge.bars;
import com.android.resources.Density;
-import com.android.resources.ResourceType;
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LevelListDrawable;
+import android.widget.LinearLayout;
import android.widget.TextView;
-public class TabletSystemBar extends CustomBar {
+public class NavigationBar extends CustomBar {
- public TabletSystemBar(Context context, Density density) throws XmlPullParserException {
- super(context, density, "/bars/tablet_system_bar.xml", "tablet_system_bar.xml");
+ public NavigationBar(Context context, Density density, int orientation) throws XmlPullParserException {
+ super(context, density, orientation, "/bars/navigation_bar.xml", "navigation_bar.xml");
setBackgroundColor(0xFF000000);
// Cannot access the inside items through id because no R.id values have been
// created for them.
// We do know the order though.
- loadIcon(0, "ic_sysbar_back_default.png", density);
- loadIcon(1, "ic_sysbar_home_default.png", density);
- loadIcon(2, "ic_sysbar_recent_default.png", density);
- // 3 is the spacer
- loadIcon(4, "stat_sys_wifi_signal_4_fully.png", density);
- Drawable drawable = loadIcon(5, ResourceType.DRAWABLE, "stat_sys_battery_charge");
- if (drawable instanceof LevelListDrawable) {
- ((LevelListDrawable) drawable).setLevel(100);
+ // 0 is a spacer.
+ int back = 1;
+ int recent = 3;
+ if (orientation == LinearLayout.VERTICAL) {
+ back = 3;
+ recent = 1;
}
+
+ loadIcon(back, "ic_sysbar_back.png", density);
+ loadIcon(2, "ic_sysbar_home.png", density);
+ loadIcon(recent, "ic_sysbar_recent.png", density);
}
@Override
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LevelListDrawable;
import android.view.Gravity;
+import android.widget.LinearLayout;
import android.widget.TextView;
-public class PhoneSystemBar extends CustomBar {
+public class StatusBar extends CustomBar {
- public PhoneSystemBar(Context context, Density density) throws XmlPullParserException {
- super(context, density, "/bars/phone_system_bar.xml", "phone_system_bar.xml");
+ public StatusBar(Context context, Density density) throws XmlPullParserException {
+ super(context, density, LinearLayout.HORIZONTAL, "/bars/status_bar.xml", "status_bar.xml");
// FIXME: use FILL_H?
setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT);
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
+import android.widget.LinearLayout;
import android.widget.TextView;
public class TitleBar extends CustomBar {
public TitleBar(Context context, Density density, String label)
throws XmlPullParserException {
- super(context, density, "/bars/title_bar.xml", "title_bar.xml");
+ super(context, density, LinearLayout.HORIZONTAL, "/bars/title_bar.xml", "title_bar.xml");
// Cannot access the inside items through id because no R.id values have been
// created for them.
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.IOException;
import java.io.InputStream;
/**
public static XmlPullParser create(File f)
throws XmlPullParserException, FileNotFoundException {
- KXmlParser parser = instantiateParser(f.getName());
- parser.setInput(new FileInputStream(f), ENCODING);
- return parser;
+ InputStream stream = new FileInputStream(f);
+ return create(stream, f.getName(), f.length());
}
public static XmlPullParser create(InputStream stream, String name)
+ throws XmlPullParserException {
+ return create(stream, name, -1);
+ }
+
+ private static XmlPullParser create(InputStream stream, String name, long size)
throws XmlPullParserException {
KXmlParser parser = instantiateParser(name);
+
+ stream = readAndClose(stream, name, size);
+
parser.setInput(stream, ENCODING);
return parser;
}
return parser;
}
+ private static InputStream readAndClose(InputStream stream, String name, long size)
+ throws XmlPullParserException {
+ // just a sanity check. It's doubtful we'll have such big files!
+ if (size > Integer.MAX_VALUE) {
+ throw new XmlPullParserException("File " + name + " is too big to be parsed");
+ }
+ int intSize = (int) size;
+
+ // create a buffered reader to facilitate reading.
+ BufferedInputStream bufferedStream = new BufferedInputStream(stream);
+ try {
+ int avail;
+ if (intSize != -1) {
+ avail = intSize;
+ } else {
+ // get the size to read.
+ avail = bufferedStream.available();
+ }
+
+ // create the initial buffer and read it.
+ byte[] buffer = new byte[avail];
+ int read = stream.read(buffer);
+
+ // this is the easy case.
+ if (read == intSize) {
+ return new ByteArrayInputStream(buffer);
+ }
+
+ // check if there is more to read (read() does not necessarily read all that
+ // available() returned!)
+ while ((avail = bufferedStream.available()) > 0) {
+ if (read + avail > buffer.length) {
+ // just allocate what is needed. We're mostly reading small files
+ // so it shouldn't be too problematic.
+ byte[] moreBuffer = new byte[read + avail];
+ System.arraycopy(buffer, 0, moreBuffer, 0, read);
+ buffer = moreBuffer;
+ }
+
+ read += stream.read(buffer, read, avail);
+ }
+
+ // return a new stream encapsulating this buffer.
+ return new ByteArrayInputStream(buffer);
+
+ } catch (IOException e) {
+ throw new XmlPullParserException("Failed to read " + name, null, e);
+ } finally {
+ try {
+ bufferedStream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
private static class CustomParser extends KXmlParser {
private final String mName;
import static com.android.ide.common.rendering.api.Result.Status.ERROR_TIMEOUT;
import static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
+import com.android.ide.common.rendering.api.HardwareConfig;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.RenderParams;
import com.android.ide.common.rendering.api.RenderResources;
-import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.RenderResources.FrameworkResourceIdProvider;
+import com.android.ide.common.rendering.api.Result;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.resources.Density;
return result;
}
+ HardwareConfig hardwareConfig = mParams.getHardwareConfig();
+
// setup the display Metrics.
DisplayMetrics metrics = new DisplayMetrics();
- metrics.densityDpi = metrics.noncompatDensityDpi = mParams.getDensity().getDpiValue();
+ metrics.densityDpi = metrics.noncompatDensityDpi =
+ hardwareConfig.getDensity().getDpiValue();
metrics.density = metrics.noncompatDensity =
metrics.densityDpi / (float) DisplayMetrics.DENSITY_DEFAULT;
metrics.scaledDensity = metrics.noncompatScaledDensity = metrics.density;
- metrics.widthPixels = metrics.noncompatWidthPixels = mParams.getScreenWidth();
- metrics.heightPixels = metrics.noncompatHeightPixels = mParams.getScreenHeight();
- metrics.xdpi = metrics.noncompatXdpi = mParams.getXdpi();
- metrics.ydpi = metrics.noncompatYdpi = mParams.getYdpi();
+ metrics.widthPixels = metrics.noncompatWidthPixels = hardwareConfig.getScreenWidth();
+ metrics.heightPixels = metrics.noncompatHeightPixels = hardwareConfig.getScreenHeight();
+ metrics.xdpi = metrics.noncompatXdpi = hardwareConfig.getXdpi();
+ metrics.ydpi = metrics.noncompatYdpi = hardwareConfig.getYdpi();
RenderResources resources = mParams.getResources();
private Configuration getConfiguration() {
Configuration config = new Configuration();
- ScreenSize screenSize = mParams.getConfigScreenSize();
+ HardwareConfig hardwareConfig = mParams.getHardwareConfig();
+
+ ScreenSize screenSize = hardwareConfig.getScreenSize();
if (screenSize != null) {
switch (screenSize) {
case SMALL:
}
}
- Density density = mParams.getDensity();
+ Density density = hardwareConfig.getDensity();
if (density == null) {
density = Density.MEDIUM;
}
- config.screenWidthDp = mParams.getScreenWidth() / density.getDpiValue();
- config.screenHeightDp = mParams.getScreenHeight() / density.getDpiValue();
+ config.screenWidthDp = hardwareConfig.getScreenWidth() / density.getDpiValue();
+ config.screenHeightDp = hardwareConfig.getScreenHeight() / density.getDpiValue();
if (config.screenHeightDp < config.screenWidthDp) {
config.smallestScreenWidthDp = config.screenHeightDp;
} else {
import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN;
import com.android.ide.common.rendering.api.DrawableParams;
+import com.android.ide.common.rendering.api.HardwareConfig;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.Result.Status;
try {
// get the drawable resource value
DrawableParams params = getParams();
+ HardwareConfig hardwareConfig = params.getHardwareConfig();
ResourceValue drawableResource = params.getDrawable();
// resolve it
// get the actual Drawable object to draw
Drawable d = ResourceHelper.getDrawable(drawableResource, context);
- content.setBackgroundDrawable(d);
+ content.setBackground(d);
// set the AttachInfo on the root view.
AttachInfo_Accessor.setAttachInfo(content);
// measure
- int w = params.getScreenWidth();
- int h = params.getScreenHeight();
+ int w = hardwareConfig.getScreenWidth();
+ int h = hardwareConfig.getScreenHeight();
int w_spec = MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY);
int h_spec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY);
content.measure(w_spec, h_spec);
// create an Android bitmap around the BufferedImage
Bitmap bitmap = Bitmap_Delegate.createBitmap(image,
- true /*isMutable*/, params.getDensity());
+ true /*isMutable*/, hardwareConfig.getDensity());
// create a Canvas around the Android bitmap
Canvas canvas = new Canvas(bitmap);
- canvas.setDensity(params.getDensity().getDpiValue());
+ canvas.setDensity(hardwareConfig.getDensity().getDpiValue());
// and draw
content.draw(canvas);
import static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
import com.android.ide.common.rendering.api.AdapterBinding;
+import com.android.ide.common.rendering.api.HardwareConfig;
import com.android.ide.common.rendering.api.IAnimationListener;
import com.android.ide.common.rendering.api.ILayoutPullParser;
import com.android.ide.common.rendering.api.IProjectCallback;
import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
import com.android.layoutlib.bridge.bars.FakeActionBar;
-import com.android.layoutlib.bridge.bars.PhoneSystemBar;
-import com.android.layoutlib.bridge.bars.TabletSystemBar;
+import com.android.layoutlib.bridge.bars.NavigationBar;
+import com.android.layoutlib.bridge.bars.StatusBar;
import com.android.layoutlib.bridge.bars.TitleBar;
import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
import com.android.resources.ResourceType;
-import com.android.resources.ScreenSize;
+import com.android.resources.ScreenOrientation;
import com.android.util.Pair;
import org.xmlpull.v1.XmlPullParserException;
import android.util.TypedValue;
import android.view.AttachInfo_Accessor;
import android.view.BridgeInflater;
-import android.view.IWindowManagerImpl;
import android.view.IWindowManager;
+import android.view.IWindowManagerImpl;
import android.view.Surface;
import android.view.View;
import android.view.View.MeasureSpec;
private boolean mWindowIsFloating;
private int mStatusBarSize;
- private int mSystemBarSize;
+ private int mNavigationBarSize;
+ private int mNavigationBarOrientation = LinearLayout.HORIZONTAL;
private int mTitleBarSize;
private int mActionBarSize;
findBackground(resources);
findStatusBar(resources, metrics);
findActionBar(resources, metrics);
- findSystemBar(resources, metrics);
+ findNavigationBar(resources, metrics);
// FIXME: find those out, and possibly add them to the render params
boolean hasSystemNavBar = true;
try {
SessionParams params = getParams();
+ HardwareConfig hardwareConfig = params.getHardwareConfig();
BridgeContext context = getContext();
+
// the view group that receives the window background.
ViewGroup backgroundView = null;
if (mWindowIsFloating || params.isForceNoDecor()) {
backgroundView = mViewRoot = mContentRoot = new FrameLayout(context);
} else {
+ if (hasSoftwareButtons() && mNavigationBarOrientation == LinearLayout.VERTICAL) {
+ /*
+ * This is a special case where the navigation bar is on the right.
+ +-------------------------------------------------+---+
+ | Status bar (always) | |
+ +-------------------------------------------------+ |
+ | (Layout with background drawable) | |
+ | +---------------------------------------------+ | |
+ | | Title/Action bar (optional) | | |
+ | +---------------------------------------------+ | |
+ | | Content, vertical extending | | |
+ | | | | |
+ | +---------------------------------------------+ | |
+ +-------------------------------------------------+---+
+
+ So we create a horizontal layout, with the nav bar on the right,
+ and the left part is the normal layout below without the nav bar at
+ the bottom
+ */
+ LinearLayout topLayout = new LinearLayout(context);
+ mViewRoot = topLayout;
+ topLayout.setOrientation(LinearLayout.HORIZONTAL);
+
+ try {
+ NavigationBar navigationBar = new NavigationBar(context,
+ hardwareConfig.getDensity(), LinearLayout.VERTICAL);
+ navigationBar.setLayoutParams(
+ new LinearLayout.LayoutParams(
+ mNavigationBarSize,
+ LayoutParams.MATCH_PARENT));
+ topLayout.addView(navigationBar);
+ } catch (XmlPullParserException e) {
+
+ }
+ }
+
/*
* we're creating the following layout
*
+-------------------------------------------------+
- | System bar (only in phone UI) |
+ | Status bar (always) |
+-------------------------------------------------+
| (Layout with background drawable) |
| +---------------------------------------------+ |
| | | |
| +---------------------------------------------+ |
+-------------------------------------------------+
- | System bar (only in tablet UI) |
+ | Navigation bar for soft buttons, maybe see above|
+-------------------------------------------------+
*/
LinearLayout topLayout = new LinearLayout(context);
- mViewRoot = topLayout;
topLayout.setOrientation(LinearLayout.VERTICAL);
+ // if we don't already have a view root this is it
+ if (mViewRoot == null) {
+ mViewRoot = topLayout;
+ } else {
+ LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+ LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
+ layoutParams.weight = 1;
+ topLayout.setLayoutParams(layoutParams);
+
+ // this is the case of soft buttons + vertical bar.
+ // this top layout is the first layout in the horizontal layout. see above)
+ mViewRoot.addView(topLayout, 0);
+ }
if (mStatusBarSize > 0) {
// system bar
try {
- PhoneSystemBar systemBar = new PhoneSystemBar(context,
- params.getDensity());
+ StatusBar systemBar = new StatusBar(context, hardwareConfig.getDensity());
systemBar.setLayoutParams(
new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, mStatusBarSize));
if (mActionBarSize > 0) {
try {
FakeActionBar actionBar = new FakeActionBar(context,
- params.getDensity(),
+ hardwareConfig.getDensity(),
params.getAppLabel(), params.getAppIcon());
actionBar.setLayoutParams(
new LinearLayout.LayoutParams(
} else if (mTitleBarSize > 0) {
try {
TitleBar titleBar = new TitleBar(context,
- params.getDensity(), params.getAppLabel());
+ hardwareConfig.getDensity(), params.getAppLabel());
titleBar.setLayoutParams(
new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, mTitleBarSize));
mContentRoot.setLayoutParams(layoutParams);
backgroundLayout.addView(mContentRoot);
- if (mSystemBarSize > 0) {
+ if (mNavigationBarOrientation == LinearLayout.HORIZONTAL &&
+ mNavigationBarSize > 0) {
// system bar
try {
- TabletSystemBar systemBar = new TabletSystemBar(context,
- params.getDensity());
- systemBar.setLayoutParams(
+ NavigationBar navigationBar = new NavigationBar(context,
+ hardwareConfig.getDensity(), LinearLayout.HORIZONTAL);
+ navigationBar.setLayoutParams(
new LinearLayout.LayoutParams(
- LayoutParams.MATCH_PARENT, mSystemBarSize));
- topLayout.addView(systemBar);
+ LayoutParams.MATCH_PARENT, mNavigationBarSize));
+ topLayout.addView(navigationBar);
} catch (XmlPullParserException e) {
}
// get the background drawable
if (mWindowBackground != null && backgroundView != null) {
Drawable d = ResourceHelper.getDrawable(mWindowBackground, context);
- backgroundView.setBackgroundDrawable(d);
+ backgroundView.setBackground(d);
}
return SUCCESS.createResult();
}
RenderingMode renderingMode = params.getRenderingMode();
+ HardwareConfig hardwareConfig = params.getHardwareConfig();
// only do the screen measure when needed.
boolean newRenderSize = false;
if (mMeasuredScreenWidth == -1) {
newRenderSize = true;
- mMeasuredScreenWidth = params.getScreenWidth();
- mMeasuredScreenHeight = params.getScreenHeight();
+ mMeasuredScreenWidth = hardwareConfig.getScreenWidth();
+ mMeasuredScreenHeight = hardwareConfig.getScreenHeight();
if (renderingMode != RenderingMode.NORMAL) {
int widthMeasureSpecMode = renderingMode.isHorizExpand() ?
// create an Android bitmap around the BufferedImage
Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage,
- true /*isMutable*/, params.getDensity());
+ true /*isMutable*/, hardwareConfig.getDensity());
// create a Canvas around the Android bitmap
mCanvas = new Canvas(bitmap);
- mCanvas.setDensity(params.getDensity().getDpiValue());
+ mCanvas.setDensity(hardwareConfig.getDensity().getDpiValue());
}
if (freshRender && newImage == false) {
}
}
- private boolean isTabletUi() {
- return getParams().getConfigScreenSize() == ScreenSize.XLARGE;
+ private boolean hasSoftwareButtons() {
+ return getParams().getHardwareConfig().hasSoftwareButtons();
}
private void findStatusBar(RenderResources resources, DisplayMetrics metrics) {
- if (isTabletUi() == false) {
- boolean windowFullscreen = getBooleanThemeValue(resources,
- "windowFullscreen", false /*defaultValue*/);
+ boolean windowFullscreen = getBooleanThemeValue(resources,
+ "windowFullscreen", false /*defaultValue*/);
- if (windowFullscreen == false && mWindowIsFloating == false) {
- // default value
- mStatusBarSize = DEFAULT_STATUS_BAR_HEIGHT;
+ if (windowFullscreen == false && mWindowIsFloating == false) {
+ // default value
+ mStatusBarSize = DEFAULT_STATUS_BAR_HEIGHT;
- // get the real value
- ResourceValue value = resources.getFrameworkResource(ResourceType.DIMEN,
- "status_bar_height");
+ // get the real value
+ ResourceValue value = resources.getFrameworkResource(ResourceType.DIMEN,
+ "status_bar_height");
- if (value != null) {
- TypedValue typedValue = ResourceHelper.getValue("status_bar_height",
- value.getValue(), true /*requireUnit*/);
- if (typedValue != null) {
- // compute the pixel value based on the display metrics
- mStatusBarSize = (int)typedValue.getDimension(metrics);
- }
+ if (value != null) {
+ TypedValue typedValue = ResourceHelper.getValue("status_bar_height",
+ value.getValue(), true /*requireUnit*/);
+ if (typedValue != null) {
+ // compute the pixel value based on the display metrics
+ mStatusBarSize = (int)typedValue.getDimension(metrics);
}
}
}
}
}
- private void findSystemBar(RenderResources resources, DisplayMetrics metrics) {
- if (isTabletUi() && mWindowIsFloating == false) {
+ private void findNavigationBar(RenderResources resources, DisplayMetrics metrics) {
+ if (hasSoftwareButtons() && mWindowIsFloating == false) {
// default value
- mSystemBarSize = 48; // ??
+ mNavigationBarSize = 48; // ??
+
+ HardwareConfig hardwareConfig = getParams().getHardwareConfig();
+
+ boolean barOnBottom = true;
+
+ if (hardwareConfig.getOrientation() == ScreenOrientation.LANDSCAPE) {
+ // compute the dp of the screen.
+ int shortSize = hardwareConfig.getScreenHeight();
+
+ // compute in dp
+ int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / hardwareConfig.getDensity().getDpiValue();
+
+ if (shortSizeDp < 600) {
+ // 0-599dp: "phone" UI with bar on the side
+ barOnBottom = false;
+ } else {
+ // 600+dp: "tablet" UI with bar on the bottom
+ barOnBottom = true;
+ }
+ }
+
+ if (barOnBottom) {
+ mNavigationBarOrientation = LinearLayout.HORIZONTAL;
+ } else {
+ mNavigationBarOrientation = LinearLayout.VERTICAL;
+ }
// get the real value
ResourceValue value = resources.getFrameworkResource(ResourceType.DIMEN,
- "status_bar_height");
+ barOnBottom ? "navigation_bar_height" : "navigation_bar_width");
if (value != null) {
- TypedValue typedValue = ResourceHelper.getValue("status_bar_height",
+ TypedValue typedValue = ResourceHelper.getValue("navigation_bar_height",
value.getValue(), true /*requireUnit*/);
if (typedValue != null) {
// compute the pixel value based on the display metrics
- mSystemBarSize = (int)typedValue.getDimension(metrics);
+ mNavigationBarSize = (int)typedValue.getDimension(metrics);
}
}
}
private static final String P2P_GO_NEG_SUCCESS_STR = "P2P-GO-NEG-SUCCESS";
+ /* P2P-GO-NEG-FAILURE status=x */
private static final String P2P_GO_NEG_FAILURE_STR = "P2P-GO-NEG-FAILURE";
private static final String P2P_GROUP_FORMATION_SUCCESS_STR =
WifiManager.ERROR, 0));
}
+ /* <event> status=<err> and the special case of <event> reason=FREQ_CONFLICT */
+ private P2pStatus p2pError(String dataString) {
+ P2pStatus err = P2pStatus.UNKNOWN;
+ String[] tokens = dataString.split(" ");
+ if (tokens.length < 2) return err;
+ String[] nameValue = tokens[1].split("=");
+ if (nameValue.length != 2) return err;
+
+ /* Handle the special case of reason=FREQ+CONFLICT */
+ if (nameValue[1].equals("FREQ_CONFLICT")) {
+ return P2pStatus.NO_COMMON_CHANNEL;
+ }
+ try {
+ err = P2pStatus.valueOf(Integer.parseInt(nameValue[1]));
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ }
+ return err;
+ }
+
/**
* Handle p2p events
*/
} else if (dataString.startsWith(P2P_GO_NEG_SUCCESS_STR)) {
mStateMachine.sendMessage(P2P_GO_NEGOTIATION_SUCCESS_EVENT);
} else if (dataString.startsWith(P2P_GO_NEG_FAILURE_STR)) {
- mStateMachine.sendMessage(P2P_GO_NEGOTIATION_FAILURE_EVENT);
+ mStateMachine.sendMessage(P2P_GO_NEGOTIATION_FAILURE_EVENT, p2pError(dataString));
} else if (dataString.startsWith(P2P_GROUP_FORMATION_SUCCESS_STR)) {
mStateMachine.sendMessage(P2P_GROUP_FORMATION_SUCCESS_EVENT);
} else if (dataString.startsWith(P2P_GROUP_FORMATION_FAILURE_STR)) {
- mStateMachine.sendMessage(P2P_GROUP_FORMATION_FAILURE_EVENT);
+ mStateMachine.sendMessage(P2P_GROUP_FORMATION_FAILURE_EVENT, p2pError(dataString));
} else if (dataString.startsWith(P2P_GROUP_STARTED_STR)) {
mStateMachine.sendMessage(P2P_GROUP_STARTED_EVENT, new WifiP2pGroup(dataString));
} else if (dataString.startsWith(P2P_GROUP_REMOVED_STR)) {
mStateMachine.sendMessage(P2P_INVITATION_RECEIVED_EVENT,
new WifiP2pGroup(dataString));
} else if (dataString.startsWith(P2P_INVITATION_RESULT_STR)) {
- String[] tokens = dataString.split(" ");
- if (tokens.length != 2) return;
- String[] nameValue = tokens[1].split("=");
- if (nameValue.length != 2) return;
- P2pStatus err = P2pStatus.UNKNOWN;
- try {
- err = P2pStatus.valueOf(Integer.parseInt(nameValue[1]));
- } catch (NumberFormatException e) {
- e.printStackTrace();
- }
- mStateMachine.sendMessage(P2P_INVITATION_RESULT_EVENT, err);
+ mStateMachine.sendMessage(P2P_INVITATION_RESULT_EVENT, p2pError(dataString));
} else if (dataString.startsWith(P2P_PROV_DISC_PBC_REQ_STR)) {
mStateMachine.sendMessage(P2P_PROV_DISC_PBC_REQ_EVENT,
new WifiP2pProvDiscEvent(dataString));
private final boolean mP2pSupported;
private final AtomicBoolean mP2pConnected = new AtomicBoolean(false);
+ private boolean mTemporarilyDisconnectWifi = false;
private final String mPrimaryDeviceType;
/* Scan results handling */
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;
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 */
addState(mDisconnectingState, mConnectModeState);
addState(mDisconnectedState, mConnectModeState);
addState(mWpsRunningState, mConnectModeState);
+ addState(mWaitForP2pDisableState, mSupplicantStartedState);
addState(mDriverStoppingState, mSupplicantStartedState);
addState(mDriverStoppedState, mSupplicantStartedState);
addState(mSupplicantStoppingState, mDefaultState);
NetworkInfo info = (NetworkInfo) message.obj;
mP2pConnected.set(info.isConnected());
break;
+ case WifiP2pService.DISCONNECT_WIFI_REQUEST:
+ mTemporarilyDisconnectWifi = (message.arg1 == 1);
+ replyToMessage(message, WifiP2pService.DISCONNECT_WIFI_RESPONSE);
+ break;
default:
loge("Error! unhandled message" + message);
break;
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");
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:
}
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) {
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;
}
}
transitionTo(mDisconnectedState);
}
break;
+ case WifiP2pService.DISCONNECT_WIFI_REQUEST:
+ if (message.arg1 == 1) {
+ mWifiNative.disconnect();
+ mTemporarilyDisconnectWifi = true;
+ } else {
+ mWifiNative.reconnect();
+ mTemporarilyDisconnectWifi = false;
+ }
+ break;
/* Do a redundant disconnect without transition */
case CMD_DISCONNECT:
mWifiNative.disconnect();
mWifiNative.disconnect();
transitionTo(mDisconnectingState);
break;
+ case WifiP2pService.DISCONNECT_WIFI_REQUEST:
+ if (message.arg1 == 1) {
+ mWifiNative.disconnect();
+ mTemporarilyDisconnectWifi = true;
+ transitionTo(mDisconnectingState);
+ }
+ break;
case CMD_SET_SCAN_MODE:
if (message.arg1 == SCAN_ONLY_MODE) {
sendMessage(CMD_DISCONNECT);
if (DBG) log(getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ // We dont scan frequently if this is a temporary disconnect
+ // due to p2p
+ if (mTemporarilyDisconnectWifi) {
+ mWifiP2pChannel.sendMessage(WifiP2pService.DISCONNECT_WIFI_RESPONSE);
+ return;
+ }
+
mFrameworkScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
Settings.Global.WIFI_FRAMEWORK_SCAN_INTERVAL_MS,
mDefaultFrameworkScanIntervalMs);
sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
}
+ case CMD_RECONNECT:
+ case CMD_REASSOCIATE:
+ // Drop a third party reconnect/reassociate if we are
+ // tempoarily disconnected for p2p
+ if (mTemporarilyDisconnectWifi) ret = NOT_HANDLED;
+ break;
default:
ret = NOT_HANDLED;
}
mLinkProperties = (LinkProperties) intent.getParcelableExtra(
WifiManager.EXTRA_LINK_PROPERTIES);
if (mPoorNetworkDetectionEnabled) {
- if (mWifiInfo == null) {
- if (DBG) logd("Ignoring link verification, mWifiInfo is NULL");
+ if (mWifiInfo == null || mCurrentBssid == null) {
+ loge("Ignore, wifiinfo " + mWifiInfo +" bssid " + mCurrentBssid);
sendLinkStatusNotification(true);
} else {
transitionTo(mVerifyingLinkState);
}
private void handleRssiChange() {
- if (mCurrentSignalLevel <= LINK_MONITOR_LEVEL_THRESHOLD) {
+ if (mCurrentSignalLevel <= LINK_MONITOR_LEVEL_THRESHOLD && mCurrentBssid != null) {
transitionTo(mLinkMonitoringState);
} else {
// stay here
if (DBG) logd("########################################");
if (isGood) {
mWsmChannel.sendMessage(GOOD_LINK_DETECTED);
- mCurrentBssid.mLastTimeGood = SystemClock.elapsedRealtime();
- logd("Good link notification is sent");
+ if (mCurrentBssid != null) {
+ mCurrentBssid.mLastTimeGood = SystemClock.elapsedRealtime();
+ }
+ if (DBG) logd("Good link notification is sent");
} else {
mWsmChannel.sendMessage(POOR_LINK_DETECTED);
- mCurrentBssid.mLastTimePoor = SystemClock.elapsedRealtime();
+ if (mCurrentBssid != null) {
+ mCurrentBssid.mLastTimePoor = SystemClock.elapsedRealtime();
+ }
logd("Poor link notification is sent");
}
}
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;
private static final int PEER_CONNECTION_USER_ACCEPT = BASE + 2;
/* User rejected a peer request */
private static final int PEER_CONNECTION_USER_REJECT = BASE + 3;
+ /* User wants to disconnect wifi in favour of p2p */
+ private static final int DROP_WIFI_USER_ACCEPT = BASE + 4;
+ /* User wants to keep his wifi connection and drop p2p */
+ private static final int DROP_WIFI_USER_REJECT = BASE + 5;
+ /* Delayed message to timeout p2p disable */
+ public static final int DISABLE_P2P_TIMED_OUT = BASE + 6;
+
/* Commands to the WifiStateMachine */
- public static final int P2P_CONNECTION_CHANGED = BASE + 11;
+ public static final int P2P_CONNECTION_CHANGED = BASE + 11;
+
+ /* These commands are used to tempoarily disconnect wifi when we detect
+ * a frequency conflict which would make it impossible to have with p2p
+ * and wifi active at the same time.
+ *
+ * If the user chooses to disable wifi tempoarily, we keep wifi disconnected
+ * until the p2p connection is done and terminated at which point we will
+ * bring back wifi up
+ *
+ * DISCONNECT_WIFI_REQUEST
+ * msg.arg1 = 1 enables temporary disconnect and 0 disables it.
+ */
+ public static final int DISCONNECT_WIFI_REQUEST = BASE + 12;
+ public static final int DISCONNECT_WIFI_RESPONSE = BASE + 13;
private final boolean mP2pSupported;
private NetworkInfo mNetworkInfo;
+ private boolean mTempoarilyDisconnectedWifi = false;
+
/* The transaction Id of service discovery request */
private byte mServiceTransactionId = 0;
PREVIOUS_PROTOCOL_ERROR,
/* There is no common channels the both devices can use. */
- NO_COMMON_CHANNE,
+ NO_COMMON_CHANNEL,
/* Unknown p2p group. For example, Device A tries to invoke the previous persistent group,
* but device B has removed the specified credential already. */
case 6:
return PREVIOUS_PROTOCOL_ERROR;
case 7:
- return NO_COMMON_CHANNE;
+ return NO_COMMON_CHANNEL;
case 8:
return UNKNOWN_P2P_GROUP;
case 9:
= new UserAuthorizingInvitationState();
private ProvisionDiscoveryState mProvisionDiscoveryState = new ProvisionDiscoveryState();
private GroupNegotiationState mGroupNegotiationState = new GroupNegotiationState();
+ private FrequencyConflictState mFrequencyConflictState =new FrequencyConflictState();
private GroupCreatedState mGroupCreatedState = new GroupCreatedState();
private UserAuthorizingJoinState mUserAuthorizingJoinState = new UserAuthorizingJoinState();
addState(mUserAuthorizingInvitationState, mGroupCreatingState);
addState(mProvisionDiscoveryState, mGroupCreatingState);
addState(mGroupNegotiationState, mGroupCreatingState);
+ addState(mFrequencyConflictState, mGroupCreatingState);
addState(mGroupCreatedState, mP2pEnabledState);
addState(mUserAuthorizingJoinState, mGroupCreatedState);
addState(mOngoingGroupRemovalState, mGroupCreatedState);
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;
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) {
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 {
mWifiMonitor.startMonitoring();
transitionTo(mP2pEnablingState);
break;
- case WifiStateMachine.CMD_DISABLE_P2P:
- //Nothing to do
- break;
default:
return NOT_HANDLED;
}
transitionTo(mP2pDisabledState);
break;
case WifiStateMachine.CMD_ENABLE_P2P:
- case WifiStateMachine.CMD_DISABLE_P2P:
+ case WifiStateMachine.CMD_DISABLE_P2P_REQ:
deferMessage(message);
break;
default:
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();
// remain at this state.
}
break;
- case WifiMonitor.P2P_GROUP_STARTED_EVENT:
- mGroup = (WifiP2pGroup) message.obj;
- if (DBG) logd(getName() + " group started");
-
- if (mGroup.getNetworkId() == WifiP2pGroup.PERSISTENT_NET_ID) {
- // This is an invocation case.
- mAutonomousGroup = false;
- deferMessage(message);
- transitionTo(mGroupNegotiationState);
- } else {
- return NOT_HANDLED;
- }
- break;
- default:
+ default:
return NOT_HANDLED;
}
return HANDLED;
// mSavedPeerConfig can be empty
if (mSavedPeerConfig != null &&
!mSavedPeerConfig.deviceAddress.equals(device.deviceAddress)) {
+ if (DBG) {
+ logd("mSavedPeerConfig " + mSavedPeerConfig.deviceAddress +
+ "device " + device.deviceAddress);
+ }
// Do the regular device lost handling
ret = NOT_HANDLED;
break;
transitionTo(mGroupCreatedState);
break;
case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
+ P2pStatus status = (P2pStatus) message.obj;
+ if (status == P2pStatus.NO_COMMON_CHANNEL) {
+ transitionTo(mFrequencyConflictState);
+ break;
+ }
+ /* continue with group removal handling */
case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
if (DBG) logd(getName() + " go failure");
handleGroupCreationFailure();
// a group removed event. Flushing things at group formation
// failure causes supplicant issues. Ignore right now.
case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
+ status = (P2pStatus) message.obj;
+ if (status == P2pStatus.NO_COMMON_CHANNEL) {
+ transitionTo(mFrequencyConflictState);
+ break;
+ }
break;
case WifiMonitor.P2P_INVITATION_RESULT_EVENT:
- P2pStatus status = (P2pStatus)message.obj;
+ status = (P2pStatus)message.obj;
if (status == P2pStatus.SUCCESS) {
// invocation was succeeded.
// wait P2P_GROUP_STARTED_EVENT.
handleGroupCreationFailure();
transitionTo(mInactiveState);
}
+ } else if (status == P2pStatus.NO_COMMON_CHANNEL) {
+ transitionTo(mFrequencyConflictState);
} else {
handleGroupCreationFailure();
transitionTo(mInactiveState);
}
}
+ class FrequencyConflictState extends State {
+ private AlertDialog mFrequencyConflictDialog;
+ @Override
+ public void enter() {
+ if (DBG) logd(getName());
+ notifyFrequencyConflict();
+ }
+ private void notifyFrequencyConflict() {
+ logd("Notify frequency conflict");
+ Resources r = Resources.getSystem();
+
+ AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setMessage(r.getString(R.string.wifi_p2p_frequency_conflict_message,
+ getDeviceName(mSavedPeerConfig.deviceAddress)))
+ .setPositiveButton(r.getString(R.string.dlg_ok), new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ sendMessage(DROP_WIFI_USER_ACCEPT);
+ }
+ })
+ .setNegativeButton(r.getString(R.string.decline), new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ sendMessage(DROP_WIFI_USER_REJECT);
+ }
+ })
+ .setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface arg0) {
+ sendMessage(DROP_WIFI_USER_REJECT);
+ }
+ })
+ .create();
+
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ dialog.show();
+ mFrequencyConflictDialog = dialog;
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) logd(getName() + message.toString());
+ switch (message.what) {
+ case WifiMonitor.P2P_GO_NEGOTIATION_SUCCESS_EVENT:
+ case WifiMonitor.P2P_GROUP_FORMATION_SUCCESS_EVENT:
+ loge(getName() + "group sucess during freq conflict!");
+ break;
+ case WifiMonitor.P2P_GROUP_STARTED_EVENT:
+ loge(getName() + "group started after freq conflict, handle anyway");
+ deferMessage(message);
+ transitionTo(mGroupNegotiationState);
+ break;
+ case WifiMonitor.P2P_GO_NEGOTIATION_FAILURE_EVENT:
+ case WifiMonitor.P2P_GROUP_REMOVED_EVENT:
+ case WifiMonitor.P2P_GROUP_FORMATION_FAILURE_EVENT:
+ // Ignore failures since we retry again
+ break;
+ case DROP_WIFI_USER_REJECT:
+ // User rejected dropping wifi in favour of p2p
+ handleGroupCreationFailure();
+ transitionTo(mInactiveState);
+ break;
+ case DROP_WIFI_USER_ACCEPT:
+ // User accepted dropping wifi in favour of p2p
+ mWifiChannel.sendMessage(WifiP2pService.DISCONNECT_WIFI_REQUEST, 1);
+ mTempoarilyDisconnectedWifi = true;
+ break;
+ case DISCONNECT_WIFI_RESPONSE:
+ // Got a response from wifistatemachine, retry p2p
+ if (DBG) logd(getName() + "Wifi disconnected, retry p2p");
+ transitionTo(mInactiveState);
+ sendMessage(WifiP2pManager.CONNECT, mSavedPeerConfig);
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+
+ public void exit() {
+ if (mFrequencyConflictDialog != null) mFrequencyConflictDialog.dismiss();
+ }
+ }
class GroupCreatedState extends State {
@Override
}
// 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;
mPeersLostDuringConnection.clear();
mServiceDiscReqId = null;
if (changed) sendP2pPeersChangedBroadcast();
+
+ if (mTempoarilyDisconnectedWifi) {
+ mWifiChannel.sendMessage(WifiP2pService.DISCONNECT_WIFI_REQUEST, 0);
+ mTempoarilyDisconnectedWifi = false;
+ }
}
//State machine initiated requests can have replyTo set to null indicating