Merge "Fixing where call goes from disconnecting to active to disconnecting state." into lmp-dev
diff --git a/Android.mk b/Android.mk
index 6a9da2f..b323650 100644
--- a/Android.mk
+++ b/Android.mk
@@ -187,7 +187,6 @@
core/java/android/nfc/INfcAdapterExtras.aidl \
core/java/android/nfc/INfcTag.aidl \
core/java/android/nfc/INfcCardEmulation.aidl \
- core/java/android/nfc/INfcLockscreenDispatch.aidl \
core/java/android/nfc/INfcUnlockHandler.aidl \
core/java/android/os/IBatteryPropertiesListener.aidl \
core/java/android/os/IBatteryPropertiesRegistrar.aidl \
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 68606836..1e1a613 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -80,6 +80,9 @@
private final static boolean DEBUG = false;
private final static boolean DEBUG_ICONS = false;
+ // Default flags to use with PackageManager when no flags are given.
+ private final static int sDefaultFlags = PackageManager.GET_SHARED_LIBRARY_FILES;
+
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -730,7 +733,7 @@
}
if (appInfo == null) {
try {
- appInfo = getApplicationInfo(packageName, 0);
+ appInfo = getApplicationInfo(packageName, sDefaultFlags);
} catch (NameNotFoundException e) {
return null;
}
@@ -770,7 +773,7 @@
@Override public Drawable getActivityIcon(ComponentName activityName)
throws NameNotFoundException {
- return getActivityInfo(activityName, 0).loadIcon(this);
+ return getActivityInfo(activityName, sDefaultFlags).loadIcon(this);
}
@Override public Drawable getActivityIcon(Intent intent)
@@ -799,13 +802,13 @@
@Override public Drawable getApplicationIcon(String packageName)
throws NameNotFoundException {
- return getApplicationIcon(getApplicationInfo(packageName, 0));
+ return getApplicationIcon(getApplicationInfo(packageName, sDefaultFlags));
}
@Override
public Drawable getActivityBanner(ComponentName activityName)
throws NameNotFoundException {
- return getActivityInfo(activityName, 0).loadBanner(this);
+ return getActivityInfo(activityName, sDefaultFlags).loadBanner(this);
}
@Override
@@ -832,13 +835,13 @@
@Override
public Drawable getApplicationBanner(String packageName)
throws NameNotFoundException {
- return getApplicationBanner(getApplicationInfo(packageName, 0));
+ return getApplicationBanner(getApplicationInfo(packageName, sDefaultFlags));
}
@Override
public Drawable getActivityLogo(ComponentName activityName)
throws NameNotFoundException {
- return getActivityInfo(activityName, 0).loadLogo(this);
+ return getActivityInfo(activityName, sDefaultFlags).loadLogo(this);
}
@Override
@@ -865,7 +868,7 @@
@Override
public Drawable getApplicationLogo(String packageName)
throws NameNotFoundException {
- return getApplicationLogo(getApplicationInfo(packageName, 0));
+ return getApplicationLogo(getApplicationInfo(packageName, sDefaultFlags));
}
@Override
@@ -914,7 +917,7 @@
@Override public Resources getResourcesForActivity(
ComponentName activityName) throws NameNotFoundException {
return getResourcesForApplication(
- getActivityInfo(activityName, 0).applicationInfo);
+ getActivityInfo(activityName, sDefaultFlags).applicationInfo);
}
@Override public Resources getResourcesForApplication(
@@ -926,7 +929,8 @@
Resources r = mContext.mMainThread.getTopLevelResources(
sameUid ? app.sourceDir : app.publicSourceDir,
sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
- app.resourceDirs, null, Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo);
+ app.resourceDirs, app.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
+ null, mContext.mPackageInfo);
if (r != null) {
return r;
}
@@ -936,7 +940,7 @@
@Override public Resources getResourcesForApplication(
String appPackageName) throws NameNotFoundException {
return getResourcesForApplication(
- getApplicationInfo(appPackageName, 0));
+ getApplicationInfo(appPackageName, sDefaultFlags));
}
/** @hide */
@@ -951,7 +955,7 @@
return mContext.mMainThread.getSystemContext().getResources();
}
try {
- ApplicationInfo ai = mPM.getApplicationInfo(appPackageName, 0, userId);
+ ApplicationInfo ai = mPM.getApplicationInfo(appPackageName, sDefaultFlags, userId);
if (ai != null) {
return getResourcesForApplication(ai);
}
@@ -1136,7 +1140,7 @@
}
if (appInfo == null) {
try {
- appInfo = getApplicationInfo(packageName, 0);
+ appInfo = getApplicationInfo(packageName, sDefaultFlags);
} catch (NameNotFoundException e) {
return null;
}
@@ -1164,7 +1168,7 @@
ApplicationInfo appInfo) {
if (appInfo == null) {
try {
- appInfo = getApplicationInfo(packageName, 0);
+ appInfo = getApplicationInfo(packageName, sDefaultFlags);
} catch (NameNotFoundException e) {
return null;
}
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 9129121..5b926ad 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -26,7 +26,6 @@
import android.nfc.INfcAdapterExtras;
import android.nfc.INfcTag;
import android.nfc.INfcCardEmulation;
-import android.nfc.INfcLockscreenDispatch;
import android.nfc.INfcUnlockHandler;
import android.os.Bundle;
diff --git a/core/java/android/nfc/INfcLockscreenDispatch.aidl b/core/java/android/nfc/INfcLockscreenDispatch.aidl
deleted file mode 100644
index 27e9a8c..0000000
--- a/core/java/android/nfc/INfcLockscreenDispatch.aidl
+++ /dev/null
@@ -1,12 +0,0 @@
-package android.nfc;
-
-import android.nfc.Tag;
-
-/**
- * @hide
- */
-interface INfcLockscreenDispatch {
-
- boolean onTagDetected(in Tag tag);
-
-}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 37294e7..984f12f 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -721,6 +721,39 @@
}
/**
+ * Creates a secondary user with the specified name and options and configures it with default
+ * restrictions.
+ * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
+ *
+ * @param name the user's name
+ * @param flags flags that identify the type of user and other properties.
+ * @see UserInfo
+ *
+ * @return the UserInfo object for the created user, or null if the user could not be created.
+ * @hide
+ */
+ public UserInfo createSecondaryUser(String name, int flags) {
+ try {
+ UserInfo user = mService.createUser(name, flags);
+ if (user == null) {
+ return null;
+ }
+ Bundle userRestrictions = mService.getUserRestrictions(user.id);
+ addDefaultUserRestrictions(userRestrictions);
+ mService.setUserRestrictions(userRestrictions, user.id);
+ return user;
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not create a user", re);
+ return null;
+ }
+ }
+
+ private static void addDefaultUserRestrictions(Bundle restrictions) {
+ restrictions.putBoolean(DISALLOW_OUTGOING_CALLS, true);
+ restrictions.putBoolean(DISALLOW_SMS, true);
+ }
+
+ /**
* Creates a user with the specified name and options as a profile of another user.
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
*
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 5be6371..52b4649 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -18,10 +18,15 @@
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
+import java.util.Iterator;
+import android.os.Binder;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.Log;
/**
@@ -99,6 +104,8 @@
private final static String TAG = "android.media.AudioRecord";
+ /** @hide */
+ public final static String SUBMIX_FIXED_VOLUME = "fixedVolume";
//---------------------------------------------------------
// Used exclusively by native code
@@ -184,6 +191,7 @@
* AudioAttributes
*/
private AudioAttributes mAudioAttributes;
+ private boolean mIsSubmixFullVolume = false;
//---------------------------------------------------------
// Constructor, Finalize
@@ -267,6 +275,18 @@
mAudioAttributes = attributes;
+ // is this AudioRecord using REMOTE_SUBMIX at full volume?
+ if (mAudioAttributes.getCapturePreset() == MediaRecorder.AudioSource.REMOTE_SUBMIX) {
+ final Iterator<String> tagsIter = mAudioAttributes.getTags().iterator();
+ while (tagsIter.hasNext()) {
+ if (tagsIter.next().equalsIgnoreCase(SUBMIX_FIXED_VOLUME)) {
+ mIsSubmixFullVolume = true;
+ Log.v(TAG, "Will record from REMOTE_SUBMIX at full fixed volume");
+ break;
+ }
+ }
+ }
+
int rate = 0;
if ((format.getPropertySetMask()
& AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) != 0)
@@ -419,7 +439,8 @@
@Override
protected void finalize() {
- native_finalize();
+ // will cause stop() to be called, and if appropriate, will handle fixed volume recording
+ release();
}
@@ -586,6 +607,7 @@
// start recording
synchronized(mRecordingStateLock) {
if (native_start(MediaSyncEvent.SYNC_EVENT_NONE, 0) == SUCCESS) {
+ handleFullVolumeRec(true);
mRecordingState = RECORDSTATE_RECORDING;
}
}
@@ -608,6 +630,7 @@
// start recording
synchronized(mRecordingStateLock) {
if (native_start(syncEvent.getType(), syncEvent.getAudioSessionId()) == SUCCESS) {
+ handleFullVolumeRec(true);
mRecordingState = RECORDSTATE_RECORDING;
}
}
@@ -625,11 +648,25 @@
// stop recording
synchronized(mRecordingStateLock) {
+ handleFullVolumeRec(false);
native_stop();
mRecordingState = RECORDSTATE_STOPPED;
}
}
+ private final IBinder mICallBack = new Binder();
+ private void handleFullVolumeRec(boolean starting) {
+ if (!mIsSubmixFullVolume) {
+ return;
+ }
+ final IBinder b = ServiceManager.getService(android.content.Context.AUDIO_SERVICE);
+ final IAudioService ias = IAudioService.Stub.asInterface(b);
+ try {
+ ias.forceRemoteSubmixFullVolume(starting, mICallBack);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error talking to AudioService when handling full submix volume", e);
+ }
+ }
//---------------------------------------------------------
// Audio data supply
@@ -880,6 +917,7 @@
int sampleRate, int channelMask, int audioFormat,
int buffSizeInBytes, int[] sessionId);
+ // TODO remove: implementation calls directly into implementation of native_release()
private native final void native_finalize();
private native final void native_release();
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 5c2abc53..96ce2c1 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -485,6 +485,7 @@
AudioSystem.DEVICE_OUT_HDMI_ARC |
AudioSystem.DEVICE_OUT_SPDIF |
AudioSystem.DEVICE_OUT_AUX_LINE;
+ int mFullVolumeDevices = 0;
// TODO merge orientation and rotation
private final boolean mMonitorOrientation;
@@ -727,6 +728,10 @@
}
}
+ private void checkAllFixedVolumeDevices(int streamType) {
+ mStreamStates[streamType].checkFixedVolumeDevices();
+ }
+
private void createStreamStates() {
int numStreamTypes = AudioSystem.getNumStreamTypes();
VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
@@ -1466,6 +1471,106 @@
return mStreamStates[streamType].isMuted();
}
+ private class RmtSbmxFullVolDeathHandler implements IBinder.DeathRecipient {
+ private IBinder mICallback; // To be notified of client's death
+
+ RmtSbmxFullVolDeathHandler(IBinder cb) {
+ mICallback = cb;
+ try {
+ cb.linkToDeath(this, 0/*flags*/);
+ } catch (RemoteException e) {
+ Log.e(TAG, "can't link to death", e);
+ }
+ }
+
+ boolean isHandlerFor(IBinder cb) {
+ return mICallback.equals(cb);
+ }
+
+ void forget() {
+ try {
+ mICallback.unlinkToDeath(this, 0/*flags*/);
+ } catch (NoSuchElementException e) {
+ Log.e(TAG, "error unlinking to death", e);
+ }
+ }
+
+ public void binderDied() {
+ Log.w(TAG, "Recorder with remote submix at full volume died " + mICallback);
+ forceRemoteSubmixFullVolume(false, mICallback);
+ }
+ }
+
+ /**
+ * call must be synchronized on mRmtSbmxFullVolDeathHandlers
+ * @return true if there is a registered death handler, false otherwise */
+ private boolean discardRmtSbmxFullVolDeathHandlerFor(IBinder cb) {
+ Iterator<RmtSbmxFullVolDeathHandler> it = mRmtSbmxFullVolDeathHandlers.iterator();
+ while (it.hasNext()) {
+ final RmtSbmxFullVolDeathHandler handler = it.next();
+ if (handler.isHandlerFor(cb)) {
+ handler.forget();
+ mRmtSbmxFullVolDeathHandlers.remove(handler);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** call synchronized on mRmtSbmxFullVolDeathHandlers */
+ private boolean hasRmtSbmxFullVolDeathHandlerFor(IBinder cb) {
+ Iterator<RmtSbmxFullVolDeathHandler> it = mRmtSbmxFullVolDeathHandlers.iterator();
+ while (it.hasNext()) {
+ if (it.next().isHandlerFor(cb)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private int mRmtSbmxFullVolRefCount = 0;
+ private ArrayList<RmtSbmxFullVolDeathHandler> mRmtSbmxFullVolDeathHandlers =
+ new ArrayList<RmtSbmxFullVolDeathHandler>();
+
+ public void forceRemoteSubmixFullVolume(boolean startForcing, IBinder cb) {
+ if (cb == null) {
+ return;
+ }
+ if ((PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.CAPTURE_AUDIO_OUTPUT))) {
+ Log.w(TAG, "Trying to call forceRemoteSubmixFullVolume() without CAPTURE_AUDIO_OUTPUT");
+ return;
+ }
+ synchronized(mRmtSbmxFullVolDeathHandlers) {
+ boolean applyRequired = false;
+ if (startForcing) {
+ if (!hasRmtSbmxFullVolDeathHandlerFor(cb)) {
+ mRmtSbmxFullVolDeathHandlers.add(new RmtSbmxFullVolDeathHandler(cb));
+ if (mRmtSbmxFullVolRefCount == 0) {
+ mFullVolumeDevices |= AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
+ mFixedVolumeDevices |= AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
+ applyRequired = true;
+ }
+ mRmtSbmxFullVolRefCount++;
+ }
+ } else {
+ if (discardRmtSbmxFullVolDeathHandlerFor(cb) && (mRmtSbmxFullVolRefCount > 0)) {
+ mRmtSbmxFullVolRefCount--;
+ if (mRmtSbmxFullVolRefCount == 0) {
+ mFullVolumeDevices &= ~AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
+ mFixedVolumeDevices &= ~AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
+ applyRequired = true;
+ }
+ }
+ }
+ if (applyRequired) {
+ // Assumes only STREAM_MUSIC going through DEVICE_OUT_REMOTE_SUBMIX
+ checkAllFixedVolumeDevices(AudioSystem.STREAM_MUSIC);
+ mStreamStates[AudioSystem.STREAM_MUSIC].applyAllVolumes();
+ }
+ }
+ }
+
/** @see AudioManager#setMasterMute(boolean, int) */
public void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb) {
if (mUseFixedVolume) {
@@ -3243,8 +3348,8 @@
int index;
if (isMuted()) {
index = 0;
- } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
- mAvrcpAbsVolSupported) {
+ } else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && mAvrcpAbsVolSupported)
+ || ((device & mFullVolumeDevices) != 0)) {
index = (mIndexMax + 5)/10;
} else {
index = (getIndex(device) + 5)/10;
@@ -3272,8 +3377,10 @@
if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
if (isMuted()) {
index = 0;
- } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
- mAvrcpAbsVolSupported) {
+ } else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
+ mAvrcpAbsVolSupported)
+ || ((device & mFullVolumeDevices) != 0))
+ {
index = (mIndexMax + 5)/10;
} else {
index = ((Integer)entry.getValue() + 5)/10;
@@ -3403,7 +3510,8 @@
Map.Entry entry = (Map.Entry)i.next();
int device = ((Integer)entry.getKey()).intValue();
int index = ((Integer)entry.getValue()).intValue();
- if (((device & mFixedVolumeDevices) != 0) && index != 0) {
+ if (((device & mFullVolumeDevices) != 0)
+ || (((device & mFixedVolumeDevices) != 0) && index != 0)) {
entry.setValue(mIndexMax);
}
applyDeviceVolume(device);
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 75fc03c..1c41432 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -56,6 +56,8 @@
boolean isStreamMute(int streamType);
+ void forceRemoteSubmixFullVolume(boolean startForcing, IBinder cb);
+
void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb);
boolean isMasterMute();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 954046c..fcdbfc1 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -48,6 +48,8 @@
private static SummaryStats sScreenOnPulsingStats;
private static SummaryStats sScreenOnNotPulsingStats;
private static SummaryStats sEmergencyCallStats;
+ private static SummaryStats sProxNearStats;
+ private static SummaryStats sProxFarStats;
public static void tracePickupPulse(boolean withinVibrationThreshold) {
if (!ENABLED) return;
@@ -88,6 +90,8 @@
sScreenOnPulsingStats = new SummaryStats();
sScreenOnNotPulsingStats = new SummaryStats();
sEmergencyCallStats = new SummaryStats();
+ sProxNearStats = new SummaryStats();
+ sProxFarStats = new SummaryStats();
log("init");
KeyguardUpdateMonitor.getInstance(context).registerCallback(sKeyguardCallback);
}
@@ -133,6 +137,12 @@
}
}
+ public static void traceProximityResult(boolean near, long millis) {
+ if (!ENABLED) return;
+ log("proximityResult near=" + near + " millis=" + millis);
+ (near ? sProxNearStats : sProxFarStats).append();
+ }
+
public static void dump(PrintWriter pw) {
synchronized (DozeLog.class) {
if (sMessages == null) return;
@@ -154,6 +164,8 @@
sScreenOnPulsingStats.dump(pw, "Screen on (pulsing)");
sScreenOnNotPulsingStats.dump(pw, "Screen on (not pulsing)");
sEmergencyCallStats.dump(pw, "Emergency call");
+ sProxNearStats.dump(pw, "Proximity (near)");
+ sProxFarStats.dump(pw, "Proximity (far)");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 3afdc3d..f8c5e9c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -25,11 +25,15 @@
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
import android.media.AudioAttributes;
+import android.os.Handler;
import android.os.PowerManager;
+import android.os.SystemClock;
import android.os.Vibrator;
import android.service.dreams.DreamService;
import android.util.Log;
@@ -55,6 +59,7 @@
private final String mTag = String.format(TAG + ".%08x", hashCode());
private final Context mContext = this;
private final DozeParameters mDozeParameters = new DozeParameters(mContext);
+ private final Handler mHandler = new Handler();
private DozeHost mHost;
private SensorManager mSensors;
@@ -197,33 +202,49 @@
// Here we need a wakelock to stay awake until the pulse is finished.
mWakeLock.acquire();
mPulsing = true;
- mHost.pulseWhileDozing(new DozeHost.PulseCallback() {
+ final long start = SystemClock.uptimeMillis();
+ new ProximityCheck() {
@Override
- public void onPulseStarted() {
- if (mPulsing && mDreaming) {
- turnDisplayOn();
- }
- }
-
- @Override
- public void onPulseFinished() {
- if (mPulsing && mDreaming) {
+ public void onProximityResult(int result) {
+ // avoid pulsing in pockets
+ final boolean isNear = result == RESULT_NEAR;
+ DozeLog.traceProximityResult(isNear, SystemClock.uptimeMillis() - start);
+ if (isNear) {
mPulsing = false;
- turnDisplayOff();
+ mWakeLock.release();
+ return;
}
- mWakeLock.release(); // needs to be unconditional to balance acquire
+
+ // not in-pocket, continue pulsing
+ mHost.pulseWhileDozing(new DozeHost.PulseCallback() {
+ @Override
+ public void onPulseStarted() {
+ if (mPulsing && mDreaming) {
+ turnDisplayOn();
+ }
+ }
+
+ @Override
+ public void onPulseFinished() {
+ if (mPulsing && mDreaming) {
+ mPulsing = false;
+ turnDisplayOff();
+ }
+ mWakeLock.release(); // needs to be unconditional to balance acquire
+ }
+ });
}
- });
+ }.check();
}
}
private void turnDisplayOff() {
- if (DEBUG) Log.d(TAG, "Display off");
+ if (DEBUG) Log.d(mTag, "Display off");
setDozeScreenState(Display.STATE_OFF);
}
private void turnDisplayOn() {
- if (DEBUG) Log.d(TAG, "Display on");
+ if (DEBUG) Log.d(mTag, "Display on");
setDozeScreenState(mDisplayStateSupported ? Display.STATE_DOZE : Display.STATE_ON);
}
@@ -270,24 +291,24 @@
}
private void resetNotificationResets() {
- if (DEBUG) Log.d(TAG, "resetNotificationResets");
+ if (DEBUG) Log.d(mTag, "resetNotificationResets");
mScheduleResetsRemaining = mDozeParameters.getPulseScheduleResets();
}
private void updateNotificationPulse() {
- if (DEBUG) Log.d(TAG, "updateNotificationPulse");
+ if (DEBUG) Log.d(mTag, "updateNotificationPulse");
if (!mDozeParameters.getPulseOnNotifications()) return;
if (mScheduleResetsRemaining <= 0) {
- if (DEBUG) Log.d(TAG, "No more schedule resets remaining");
+ if (DEBUG) Log.d(mTag, "No more schedule resets remaining");
return;
}
final long now = System.currentTimeMillis();
if ((now - mNotificationPulseTime) < mDozeParameters.getPulseDuration()) {
- if (DEBUG) Log.d(TAG, "Recently updated, not resetting schedule");
+ if (DEBUG) Log.d(mTag, "Recently updated, not resetting schedule");
return;
}
mScheduleResetsRemaining--;
- if (DEBUG) Log.d(TAG, "mScheduleResetsRemaining = " + mScheduleResetsRemaining);
+ if (DEBUG) Log.d(mTag, "mScheduleResetsRemaining = " + mScheduleResetsRemaining);
mNotificationPulseTime = now;
rescheduleNotificationPulse(true /*predicate*/);
}
@@ -302,31 +323,31 @@
}
private void rescheduleNotificationPulse(boolean predicate) {
- if (DEBUG) Log.d(TAG, "rescheduleNotificationPulse predicate=" + predicate);
+ if (DEBUG) Log.d(mTag, "rescheduleNotificationPulse predicate=" + predicate);
final PendingIntent notificationPulseIntent = notificationPulseIntent(0);
mAlarmManager.cancel(notificationPulseIntent);
if (!predicate) {
- if (DEBUG) Log.d(TAG, " don't reschedule: predicate is false");
+ if (DEBUG) Log.d(mTag, " don't reschedule: predicate is false");
return;
}
final PulseSchedule schedule = mDozeParameters.getPulseSchedule();
if (schedule == null) {
- if (DEBUG) Log.d(TAG, " don't reschedule: schedule is null");
+ if (DEBUG) Log.d(mTag, " don't reschedule: schedule is null");
return;
}
final long now = System.currentTimeMillis();
final long time = schedule.getNextTime(now, mNotificationPulseTime);
if (time <= 0) {
- if (DEBUG) Log.d(TAG, " don't reschedule: time is " + time);
+ if (DEBUG) Log.d(mTag, " don't reschedule: time is " + time);
return;
}
final long delta = time - now;
if (delta <= 0) {
- if (DEBUG) Log.d(TAG, " don't reschedule: delta is " + delta);
+ if (DEBUG) Log.d(mTag, " don't reschedule: delta is " + delta);
return;
}
final long instance = time - mNotificationPulseTime;
- if (DEBUG) Log.d(TAG, "Scheduling pulse " + instance + " in " + delta + "ms for "
+ if (DEBUG) Log.d(mTag, "Scheduling pulse " + instance + " in " + delta + "ms for "
+ new Date(time));
mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, time, notificationPulseIntent(instance));
}
@@ -404,7 +425,9 @@
private final boolean mConfigured;
private final boolean mDebugVibrate;
- private boolean mEnabled;
+ private boolean mRequested;
+ private boolean mRegistered;
+ private boolean mDisabled;
public TriggerSensor(int type, boolean configured, boolean debugVibrate) {
mSensor = mSensors.getDefaultSensor(type);
@@ -413,19 +436,34 @@
}
public void setListening(boolean listen) {
+ if (mRequested == listen) return;
+ mRequested = listen;
+ updateListener();
+ }
+
+ public void setDisabled(boolean disabled) {
+ if (mDisabled == disabled) return;
+ mDisabled = disabled;
+ updateListener();
+ }
+
+ private void updateListener() {
if (!mConfigured || mSensor == null) return;
- if (listen) {
- mEnabled = mSensors.requestTriggerSensor(this, mSensor);
- } else if (mEnabled) {
+ if (mRequested && !mDisabled) {
+ mRegistered = mSensors.requestTriggerSensor(this, mSensor);
+ } else if (mRegistered) {
mSensors.cancelTriggerSensor(this, mSensor);
- mEnabled = false;
+ mRegistered = false;
}
}
@Override
public String toString() {
- return new StringBuilder("{mEnabled=").append(mEnabled).append(", mConfigured=")
- .append(mConfigured).append(", mDebugVibrate=").append(mDebugVibrate)
+ return new StringBuilder("{mRegistered=").append(mRegistered)
+ .append(", mRequested=").append(mRequested)
+ .append(", mDisabled=").append(mDisabled)
+ .append(", mConfigured=").append(mConfigured)
+ .append(", mDebugVibrate=").append(mDebugVibrate)
.append(", mSensor=").append(mSensor).append("}").toString();
}
@@ -449,7 +487,8 @@
// reset the notification pulse schedule, but only if we think we were not triggered
// by a notification-related vibration
- final long timeSinceNotification = System.currentTimeMillis() - mNotificationPulseTime;
+ final long timeSinceNotification = System.currentTimeMillis()
+ - mNotificationPulseTime;
final boolean withinVibrationThreshold =
timeSinceNotification < mDozeParameters.getPickupVibrationThreshold();
if (withinVibrationThreshold) {
@@ -465,4 +504,73 @@
}
}
}
+
+ private abstract class ProximityCheck implements SensorEventListener, Runnable {
+ private static final int TIMEOUT_DELAY_MS = 500;
+
+ protected static final int RESULT_UNKNOWN = 0;
+ protected static final int RESULT_NEAR = 1;
+ protected static final int RESULT_FAR = 2;
+
+ private final String mTag = DozeService.this.mTag + ".ProximityCheck";
+
+ private boolean mRegistered;
+ private boolean mFinished;
+ private float mMaxRange;
+
+ abstract public void onProximityResult(int result);
+
+ public void check() {
+ if (mFinished || mRegistered) return;
+ final Sensor sensor = mSensors.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ if (sensor == null) {
+ if (DEBUG) Log.d(mTag, "No sensor found");
+ finishWithResult(RESULT_UNKNOWN);
+ return;
+ }
+ // the pickup sensor interferes with the prox event, disable it until we have a result
+ mPickupSensor.setDisabled(true);
+
+ mMaxRange = sensor.getMaximumRange();
+ mSensors.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL, 0, mHandler);
+ mHandler.postDelayed(this, TIMEOUT_DELAY_MS);
+ mRegistered = true;
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (event.values.length == 0) {
+ if (DEBUG) Log.d(mTag, "Event has no values!");
+ finishWithResult(RESULT_UNKNOWN);
+ } else {
+ if (DEBUG) Log.d(mTag, "Event: value=" + event.values[0] + " max=" + mMaxRange);
+ final boolean isNear = event.values[0] < mMaxRange;
+ finishWithResult(isNear ? RESULT_NEAR : RESULT_FAR);
+ }
+ }
+
+ @Override
+ public void run() {
+ if (DEBUG) Log.d(mTag, "No event received before timeout");
+ finishWithResult(RESULT_UNKNOWN);
+ }
+
+ private void finishWithResult(int result) {
+ if (mFinished) return;
+ if (mRegistered) {
+ mHandler.removeCallbacks(this);
+ mSensors.unregisterListener(this);
+ // we're done - reenable the pickup sensor
+ mPickupSensor.setDisabled(false);
+ mRegistered = false;
+ }
+ onProximityResult(result);
+ mFinished = true;
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // noop
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 9984fca..ce99cc3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -66,7 +66,8 @@
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
- state.visible = mController.isHotspotSupported() && mUsageTracker.isRecentlyUsed();
+ state.visible = mController.isHotspotSupported() && mUsageTracker.isRecentlyUsed()
+ && !mController.isProvisioningNeeded();
state.label = mContext.getString(R.string.quick_settings_hotspot_label);
state.value = mController.isHotspotEnabled();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
index 582d165..0825aa3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/EmptyShadeView.java
@@ -17,9 +17,11 @@
package com.android.systemui.statusbar;
import android.content.Context;
+import android.content.res.Configuration;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Interpolator;
+import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
@@ -31,6 +33,12 @@
}
@Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ ((TextView) findViewById(R.id.no_notifications)).setText(R.string.empty_shade_text);
+ }
+
+ @Override
protected View findContentView() {
return findViewById(R.id.no_notifications);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
index 7ca91a5..0863c86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
@@ -22,6 +22,7 @@
boolean isHotspotEnabled();
boolean isHotspotSupported();
void setHotspotEnabled(boolean enabled);
+ boolean isProvisioningNeeded();
public interface Callback {
void onHotspotChanged(boolean enabled);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index dd706bb9..63c1100 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -24,6 +24,7 @@
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
@@ -74,6 +75,20 @@
}
@Override
+ public boolean isProvisioningNeeded() {
+ // Keep in sync with other usage of config_mobile_hotspot_provision_app.
+ // TetherSettings#isProvisioningNeeded and
+ // ConnectivityManager#enforceTetherChangePermission
+ String[] provisionApp = mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_mobile_hotspot_provision_app);
+ if (SystemProperties.getBoolean("net.tethering.noprovisioning", false)
+ || provisionApp == null) {
+ return false;
+ }
+ return (provisionApp.length == 2);
+ }
+
+ @Override
public void setHotspotEnabled(boolean enabled) {
final ContentResolver cr = mContext.getContentResolver();
// This needs to be kept up to date with Settings (WifiApEnabler.setSoftapEnabled)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index bbe6622..eb808c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -571,7 +571,7 @@
cancel();
} else {
dismiss();
- UserInfo user = mUserManager.createUser(
+ UserInfo user = mUserManager.createSecondaryUser(
mContext.getString(R.string.user_new_user_name), 0 /* flags */);
if (user == null) {
// Couldn't create user, most likely because there are too many, but we haven't
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 86cfdb9..fca13f8 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -2207,9 +2207,15 @@
private List<ResolveInfo> queryIntentReceivers(Intent intent, int userId) {
final long identity = Binder.clearCallingIdentity();
try {
+ int flags = PackageManager.GET_META_DATA;
+
+ // Widgets referencing shared libraries need to have their
+ // dependencies loaded.
+ flags |= PackageManager.GET_SHARED_LIBRARY_FILES;
+
return mPackageManager.queryIntentReceivers(intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- PackageManager.GET_META_DATA, userId);
+ flags, userId);
} catch (RemoteException re) {
return Collections.emptyList();
} finally {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 26e0db3..0cf22493 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -119,7 +119,7 @@
private static final int MIN_USER_ID = 10;
- private static final int USER_VERSION = 4;
+ private static final int USER_VERSION = 5;
private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
@@ -462,6 +462,17 @@
}
}
+ /**
+ * If default guest restrictions haven't been initialized yet, add the basic
+ * restrictions.
+ */
+ private void initDefaultGuestRestrictions() {
+ if (mGuestRestrictions.isEmpty()) {
+ mGuestRestrictions.putBoolean(UserManager.DISALLOW_OUTGOING_CALLS, true);
+ writeUserListLocked();
+ }
+ }
+
@Override
public Bundle getDefaultGuestRestrictions() {
checkManageUsersPermission("getDefaultGuestRestrictions");
@@ -693,6 +704,11 @@
userVersion = 4;
}
+ if (userVersion < 5) {
+ initDefaultGuestRestrictions();
+ userVersion = 5;
+ }
+
if (userVersion < USER_VERSION) {
Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to "
+ USER_VERSION);
@@ -715,6 +731,7 @@
mUserRestrictions.append(UserHandle.USER_OWNER, restrictions);
updateUserIdsLocked();
+ initDefaultGuestRestrictions();
writeUserListLocked();
writeUserLocked(primary);
diff --git a/tests/SharedLibrary/client/AndroidManifest.xml b/tests/SharedLibrary/client/AndroidManifest.xml
index a39c754..d1167fa 100644
--- a/tests/SharedLibrary/client/AndroidManifest.xml
+++ b/tests/SharedLibrary/client/AndroidManifest.xml
@@ -25,5 +25,13 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
+ <receiver android:name="DependentAppwidgetProvider">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+ </intent-filter>
+ <meta-data android:name="android.appwidget.provider"
+ android:resource="@xml/dependent_appwidget_info" />
+ </receiver>
</application>
</manifest>
diff --git a/tests/SharedLibrary/client/res/layout/dependent_appwidget.xml b/tests/SharedLibrary/client/res/layout/dependent_appwidget.xml
new file mode 100644
index 0000000..13beafa
--- /dev/null
+++ b/tests/SharedLibrary/client/res/layout/dependent_appwidget.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView android:id="@+id/label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@com.google.android.test.shared_library:string/shared_string"
+ style="@com.google.android.test.shared_library:style/CodeFont"/>
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@com.google.android.test.shared_library:drawable/size_48x48"/>
+</LinearLayout>
diff --git a/tests/SharedLibrary/client/res/xml/dependent_appwidget_info.xml b/tests/SharedLibrary/client/res/xml/dependent_appwidget_info.xml
new file mode 100644
index 0000000..452e010
--- /dev/null
+++ b/tests/SharedLibrary/client/res/xml/dependent_appwidget_info.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+ android:minWidth="40dp"
+ android:minHeight="40dp"
+ android:updatePeriodMillis="0"
+ android:initialLayout="@layout/dependent_appwidget"
+ android:resizeMode="horizontal|vertical" />
diff --git a/tests/SharedLibrary/client/src/com/google/android/test/lib_client/DependentAppwidgetProvider.java b/tests/SharedLibrary/client/src/com/google/android/test/lib_client/DependentAppwidgetProvider.java
new file mode 100644
index 0000000..4e9b26b
--- /dev/null
+++ b/tests/SharedLibrary/client/src/com/google/android/test/lib_client/DependentAppwidgetProvider.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.lib_client;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class DependentAppwidgetProvider extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+
+ }
+}
diff --git a/tests/SharedLibrary/lib/res/values/public.xml b/tests/SharedLibrary/lib/res/values/public.xml
index 37b1ec9..23d307e 100644
--- a/tests/SharedLibrary/lib/res/values/public.xml
+++ b/tests/SharedLibrary/lib/res/values/public.xml
@@ -13,5 +13,7 @@
<public type="attr" name="zip" id="0x00010005" />
<public type="attr" name="country" id="0x00010006" />
+ <public type="drawable" name="size_48x48" id="0x00030000" />
+
<public type="array" name="animals" id="0x02050000" />
</resources>