Merge "introduce GET_PASSWORD_PRIVILEDGED permission." into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index d756258..2295885 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -20581,12 +20581,13 @@
field public static final int DolbyVisionLevelUhd30 = 64; // 0x40
field public static final int DolbyVisionLevelUhd48 = 128; // 0x80
field public static final int DolbyVisionLevelUhd60 = 256; // 0x100
- field public static final int DolbyVisionProfileDvavDen = 2; // 0x2
- field public static final int DolbyVisionProfileDvavDer = 1; // 0x1
- field public static final int DolbyVisionProfileDvheDen = 4; // 0x4
- field public static final int DolbyVisionProfileDvheDer = 3; // 0x3
- field public static final int DolbyVisionProfileDvheDtr = 5; // 0x5
- field public static final int DolbyVisionProfileDvheStn = 6; // 0x6
+ field public static final int DolbyVisionProfileDvavPen = 2; // 0x2
+ field public static final int DolbyVisionProfileDvavPer = 1; // 0x1
+ field public static final int DolbyVisionProfileDvheDen = 8; // 0x8
+ field public static final int DolbyVisionProfileDvheDer = 4; // 0x4
+ field public static final int DolbyVisionProfileDvheDth = 64; // 0x40
+ field public static final int DolbyVisionProfileDvheDtr = 16; // 0x10
+ field public static final int DolbyVisionProfileDvheStn = 32; // 0x20
field public static final int H263Level10 = 1; // 0x1
field public static final int H263Level20 = 2; // 0x2
field public static final int H263Level30 = 4; // 0x4
diff --git a/api/system-current.txt b/api/system-current.txt
index f2c7b6c..8beb796 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -15271,6 +15271,7 @@
ctor public ContextHubInfo();
method public int describeContents();
method public int getId();
+ method public int getMaxPacketLengthBytes();
method public android.hardware.location.MemoryRegion[] getMemoryRegions();
method public java.lang.String getName();
method public float getPeakMips();
@@ -15298,10 +15299,6 @@
method public int sendMessage(int, int, android.hardware.location.ContextHubMessage);
method public int unloadNanoApp(int);
method public int unregisterCallback(android.hardware.location.ContextHubManager.Callback);
- field public static final int ANY_HUB = -1; // 0xffffffff
- field public static final int MSG_DATA_SEND = 3; // 0x3
- field public static final int MSG_LOAD_NANO_APP = 1; // 0x1
- field public static final int MSG_UNLOAD_NANO_APP = 2; // 0x2
}
public static abstract class ContextHubManager.Callback {
@@ -15495,7 +15492,7 @@
public class NanoAppInstanceInfo {
ctor public NanoAppInstanceInfo();
method public int describeContents();
- method public int getAppId();
+ method public long getAppId();
method public int getAppVersion();
method public int getContexthubId();
method public int getHandle();
@@ -15506,17 +15503,6 @@
method public int getNeededWriteMemBytes();
method public int[] getOutputEvents();
method public java.lang.String getPublisher();
- method public void setAppId(int);
- method public void setAppVersion(int);
- method public void setContexthubId(int);
- method public void setHandle(int);
- method public void setName(java.lang.String);
- method public void setNeededExecMemBytes(int);
- method public void setNeededReadMemBytes(int);
- method public void setNeededSensors(int[]);
- method public void setNeededWriteMemBytes(int);
- method public void setOutputEvents(int[]);
- method public void setPublisher(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.location.NanoAppInstanceInfo> CREATOR;
}
@@ -22074,12 +22060,13 @@
field public static final int DolbyVisionLevelUhd30 = 64; // 0x40
field public static final int DolbyVisionLevelUhd48 = 128; // 0x80
field public static final int DolbyVisionLevelUhd60 = 256; // 0x100
- field public static final int DolbyVisionProfileDvavDen = 2; // 0x2
- field public static final int DolbyVisionProfileDvavDer = 1; // 0x1
- field public static final int DolbyVisionProfileDvheDen = 4; // 0x4
- field public static final int DolbyVisionProfileDvheDer = 3; // 0x3
- field public static final int DolbyVisionProfileDvheDtr = 5; // 0x5
- field public static final int DolbyVisionProfileDvheStn = 6; // 0x6
+ field public static final int DolbyVisionProfileDvavPen = 2; // 0x2
+ field public static final int DolbyVisionProfileDvavPer = 1; // 0x1
+ field public static final int DolbyVisionProfileDvheDen = 8; // 0x8
+ field public static final int DolbyVisionProfileDvheDer = 4; // 0x4
+ field public static final int DolbyVisionProfileDvheDth = 64; // 0x40
+ field public static final int DolbyVisionProfileDvheDtr = 16; // 0x10
+ field public static final int DolbyVisionProfileDvheStn = 32; // 0x20
field public static final int H263Level10 = 1; // 0x1
field public static final int H263Level20 = 2; // 0x2
field public static final int H263Level30 = 4; // 0x4
@@ -24631,6 +24618,7 @@
method public android.media.tv.TvInputInfo.Builder setHdmiDeviceInfo(android.hardware.hdmi.HdmiDeviceInfo);
method public android.media.tv.TvInputInfo.Builder setIcon(android.graphics.drawable.Icon);
method public android.media.tv.TvInputInfo.Builder setIcon(android.graphics.drawable.Icon, int);
+ method public android.media.tv.TvInputInfo.Builder setLabel(java.lang.CharSequence);
method public android.media.tv.TvInputInfo.Builder setLabel(int);
method public android.media.tv.TvInputInfo.Builder setParentId(java.lang.String);
method public android.media.tv.TvInputInfo.Builder setTunerCount(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index b216231..dee2f55 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -20646,12 +20646,13 @@
field public static final int DolbyVisionLevelUhd30 = 64; // 0x40
field public static final int DolbyVisionLevelUhd48 = 128; // 0x80
field public static final int DolbyVisionLevelUhd60 = 256; // 0x100
- field public static final int DolbyVisionProfileDvavDen = 2; // 0x2
- field public static final int DolbyVisionProfileDvavDer = 1; // 0x1
- field public static final int DolbyVisionProfileDvheDen = 4; // 0x4
- field public static final int DolbyVisionProfileDvheDer = 3; // 0x3
- field public static final int DolbyVisionProfileDvheDtr = 5; // 0x5
- field public static final int DolbyVisionProfileDvheStn = 6; // 0x6
+ field public static final int DolbyVisionProfileDvavPen = 2; // 0x2
+ field public static final int DolbyVisionProfileDvavPer = 1; // 0x1
+ field public static final int DolbyVisionProfileDvheDen = 8; // 0x8
+ field public static final int DolbyVisionProfileDvheDer = 4; // 0x4
+ field public static final int DolbyVisionProfileDvheDth = 64; // 0x40
+ field public static final int DolbyVisionProfileDvheDtr = 16; // 0x10
+ field public static final int DolbyVisionProfileDvheStn = 32; // 0x20
field public static final int H263Level10 = 1; // 0x1
field public static final int H263Level20 = 2; // 0x2
field public static final int H263Level30 = 4; // 0x4
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index 83dc506..bd55a06 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -58,7 +58,7 @@
* @param context the parent context
*/
public DatePickerDialog(@NonNull Context context) {
- this(context, 0);
+ this(context, 0, null, Calendar.getInstance(), -1, -1, -1);
}
/**
@@ -70,24 +70,7 @@
* {@code context}'s default alert dialog theme
*/
public DatePickerDialog(@NonNull Context context, @StyleRes int themeResId) {
- super(context, resolveDialogTheme(context, themeResId));
-
- final Context themeContext = getContext();
- final LayoutInflater inflater = LayoutInflater.from(themeContext);
- final View view = inflater.inflate(R.layout.date_picker_dialog, null);
- setView(view);
-
- setButton(BUTTON_POSITIVE, themeContext.getString(R.string.ok), this);
- setButton(BUTTON_NEGATIVE, themeContext.getString(R.string.cancel), this);
- setButtonPanelLayoutHint(LAYOUT_HINT_SIDE);
-
- final Calendar calendar = Calendar.getInstance();
- final int year = calendar.get(Calendar.YEAR);
- final int monthOfYear = calendar.get(Calendar.MONTH);
- final int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
- mDatePicker = (DatePicker) view.findViewById(R.id.datePicker);
- mDatePicker.init(year, monthOfYear, dayOfMonth, this);
- mDatePicker.setValidationCallback(mValidationCallback);
+ this(context, themeResId, null, Calendar.getInstance(), -1, -1, -1);
}
/**
@@ -104,7 +87,7 @@
*/
public DatePickerDialog(@NonNull Context context, @Nullable OnDateSetListener listener,
int year, int month, int dayOfMonth) {
- this(context, 0, listener, year, month, dayOfMonth);
+ this(context, 0, listener, null, year, month, dayOfMonth);
}
/**
@@ -116,16 +99,40 @@
* {@code context}'s default alert dialog theme
* @param listener the listener to call when the user sets the date
* @param year the initially selected year
- * @param month the initially selected month (0-11 for compatibility with
- * {@link Calendar#MONTH})
+ * @param monthOfYear the initially selected month of the year (0-11 for
+ * compatibility with {@link Calendar#MONTH})
* @param dayOfMonth the initially selected day of month (1-31, depending
* on month)
*/
public DatePickerDialog(@NonNull Context context, @StyleRes int themeResId,
- @Nullable OnDateSetListener listener, int year, int month, int dayOfMonth) {
- this(context, themeResId);
+ @Nullable OnDateSetListener listener, int year, int monthOfYear, int dayOfMonth) {
+ this(context, themeResId, listener, null, year, monthOfYear, dayOfMonth);
+ }
- mDatePicker.updateDate(year, month, dayOfMonth);
+ private DatePickerDialog(@NonNull Context context, @StyleRes int themeResId,
+ @Nullable OnDateSetListener listener, @Nullable Calendar calendar, int year,
+ int monthOfYear, int dayOfMonth) {
+ super(context, resolveDialogTheme(context, themeResId));
+
+ final Context themeContext = getContext();
+ final LayoutInflater inflater = LayoutInflater.from(themeContext);
+ final View view = inflater.inflate(R.layout.date_picker_dialog, null);
+ setView(view);
+
+ setButton(BUTTON_POSITIVE, themeContext.getString(R.string.ok), this);
+ setButton(BUTTON_NEGATIVE, themeContext.getString(R.string.cancel), this);
+ setButtonPanelLayoutHint(LAYOUT_HINT_SIDE);
+
+ if (calendar != null) {
+ year = calendar.get(Calendar.YEAR);
+ monthOfYear = calendar.get(Calendar.MONTH);
+ dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
+ }
+
+ mDatePicker = (DatePicker) view.findViewById(R.id.datePicker);
+ mDatePicker.init(year, monthOfYear, dayOfMonth, this);
+ mDatePicker.setValidationCallback(mValidationCallback);
+
mDateSetListener = listener;
}
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 3f22385..4c4f128 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -440,6 +440,11 @@
compatInfo);
classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
+ if (DEBUG) {
+ Slog.d(TAG, "createBaseActivityResources activity=" + activityToken
+ + " with key=" + key);
+ }
+
synchronized (this) {
final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
activityToken);
@@ -651,6 +656,16 @@
activityResources.overrideConfig.setToDefaults();
}
+ if (DEBUG) {
+ Throwable here = new Throwable();
+ here.fillInStackTrace();
+ Slog.d(TAG, "updating resources override for activity=" + activityToken
+ + " from oldConfig=" + Configuration.resourceQualifierString(oldConfig)
+ + " to newConfig="
+ + Configuration.resourceQualifierString(activityResources.overrideConfig),
+ here);
+ }
+
final boolean activityHasOverrideConfig =
!activityResources.overrideConfig.equals(Configuration.EMPTY);
@@ -692,9 +707,15 @@
oldKey.mOverlayDirs, oldKey.mLibDirs, oldKey.mDisplayId,
rebasedOverrideConfig, oldKey.mCompatInfo);
+ if (DEBUG) {
+ Slog.d(TAG, "rebasing ref=" + resources + " from oldKey=" + oldKey
+ + " to newKey=" + newKey);
+ }
+
ResourcesImpl resourcesImpl = findResourcesImplForKeyLocked(newKey);
if (resourcesImpl == null) {
resourcesImpl = createResourcesImpl(newKey);
+ mResourceImpls.put(newKey, new WeakReference<>(resourcesImpl));
}
if (resourcesImpl != resources.getImpl()) {
diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
index 7d698b3..01f82e6 100644
--- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java
+++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
@@ -284,6 +284,8 @@
out.writeInt(mInstance);
out.writeInt(mProperties);
out.writeInt(mPermissions);
+ out.writeInt(mKeySize);
+ out.writeInt(mWriteType);
out.writeTypedList(mDescriptors);
}
@@ -303,6 +305,8 @@
mInstance = in.readInt();
mProperties = in.readInt();
mPermissions = in.readInt();
+ mKeySize = in.readInt();
+ mWriteType = in.readInt();
mDescriptors = new ArrayList<BluetoothGattDescriptor>();
diff --git a/core/java/android/hardware/location/ContextHubInfo.java b/core/java/android/hardware/location/ContextHubInfo.java
index 644e29f..ae44f1d 100644
--- a/core/java/android/hardware/location/ContextHubInfo.java
+++ b/core/java/android/hardware/location/ContextHubInfo.java
@@ -37,6 +37,7 @@
private float mStoppedPowerDrawMw;
private float mSleepPowerDrawMw;
private float mPeakPowerDrawMw;
+ private int mMaxPacketLengthBytes;
private int[] mSupportedSensors;
@@ -46,6 +47,27 @@
}
/**
+ * returns the maximum number of bytes that can be sent per message to the hub
+ *
+ * @return int - maximum bytes that can be transmitted in a
+ * single packet
+ */
+ public int getMaxPacketLengthBytes() {
+ return mMaxPacketLengthBytes;
+ }
+
+ /**
+ * set the context hub unique identifer
+ *
+ * @param bytes - Maximum number of bytes per message
+ *
+ * @hide
+ */
+ public void setMaxPacketLenBytes(int bytes) {
+ mMaxPacketLengthBytes = bytes;
+ }
+
+ /**
* get the context hub unique identifer
*
* @return int - unique system wide identifier
@@ -374,4 +396,4 @@
return new ContextHubInfo[size];
}
};
-}
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 4ddf767..89edaa9 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -43,23 +43,6 @@
private Handler mCallbackHandler;
/**
- * A special context hub identifier meaning any possible hub on the system.
- */
- public static final int ANY_HUB = -1;
- /**
- * A constant denoting a message to load a a Nano App
- */
- public static final int MSG_LOAD_NANO_APP = 1;
- /**
- * A constant denoting a message to unload a a Nano App
- */
- public static final int MSG_UNLOAD_NANO_APP = 2;
- /**
- * A constant denoting a message to send a message
- */
- public static final int MSG_DATA_SEND = 3;
-
- /**
* An interface to receive asynchronous communication from the context hub.
*/
public abstract static class Callback {
@@ -69,7 +52,7 @@
* Callback function called on message receipt from context hub.
*
* @param hubHandle Handle (system-wide unique identifier) of the hub of the message.
- * @param nanoAppHandle Handle (unique identifier) for the app that sent the message.
+ * @param nanoAppHandle Handle (unique identifier) for app instance that sent the message.
* @param message The context hub message.
*
* @see ContextHubMessage
@@ -89,7 +72,7 @@
try {
retVal = getBinder().getContextHubHandles();
} catch (RemoteException e) {
- Log.e(TAG, "Could not fetch context hub handles : " + e);
+ Log.w(TAG, "Could not fetch context hub handles : " + e);
}
return retVal;
}
@@ -107,7 +90,7 @@
try {
retVal = getBinder().getContextHubInfo(hubHandle);
} catch (RemoteException e) {
- Log.e(TAG, "Could not fetch context hub info :" + e);
+ Log.w(TAG, "Could not fetch context hub info :" + e);
}
return retVal;
@@ -126,6 +109,7 @@
*/
public int loadNanoApp(int hubHandle, NanoApp app) {
int retVal = -1;
+
if (app == null) {
return retVal;
}
@@ -133,7 +117,7 @@
try {
retVal = getBinder().loadNanoApp(hubHandle, app);
} catch (RemoteException e) {
- Log.e(TAG, "Could not fetch load nanoApp :" + e);
+ Log.w(TAG, "Could not load nanoApp :" + e);
}
return retVal;
@@ -152,7 +136,7 @@
try {
retVal = getBinder().unloadNanoApp(nanoAppHandle);
} catch (RemoteException e) {
- Log.e(TAG, "Could not fetch unload nanoApp :" + e);
+ Log.w(TAG, "Could not fetch unload nanoApp :" + e);
}
return retVal;
@@ -172,7 +156,7 @@
try {
retVal = getBinder().getNanoAppInstanceInfo(nanoAppHandle);
} catch (RemoteException e) {
- Log.e(TAG, "Could not fetch nanoApp info :" + e);
+ Log.w(TAG, "Could not fetch nanoApp info :" + e);
}
return retVal;
@@ -193,7 +177,7 @@
try {
retVal = getBinder().findNanoAppOnHub(hubHandle, filter);
} catch (RemoteException e) {
- Log.e(TAG, "Could not query nanoApp instance :" + e);
+ Log.w(TAG, "Could not query nanoApp instance :" + e);
}
return retVal;
}
@@ -212,10 +196,14 @@
public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage message) {
int retVal = -1;
+ if (message == null || message.getData() == null) {
+ Log.w(TAG, "null ptr");
+ return retVal;
+ }
try {
retVal = getBinder().sendMessage(hubHandle, nanoAppHandle, message);
} catch (RemoteException e) {
- Log.e(TAG, "Could not fetch send message :" + e.toString());
+ Log.w(TAG, "Could not send message :" + e.toString());
}
return retVal;
@@ -247,7 +235,7 @@
public int registerCallback(Callback callback, Handler handler) {
synchronized(this) {
if (mCallback != null) {
- Log.e(TAG, "Max number of callbacks reached!");
+ Log.w(TAG, "Max number of callbacks reached!");
return -1;
}
mCallback = callback;
@@ -268,7 +256,7 @@
public int unregisterCallback(Callback callback) {
synchronized(this) {
if (callback != mCallback) {
- Log.e(TAG, "Cannot recognize callback!");
+ Log.w(TAG, "Cannot recognize callback!");
return -1;
}
@@ -311,11 +299,11 @@
try {
getBinder().registerCallback(mClientCallback);
} catch (RemoteException e) {
- Log.e(TAG, "Could not register callback:" + e);
+ Log.w(TAG, "Could not register callback:" + e);
}
} else {
- Log.d(TAG, "failed to getService");
+ Log.w(TAG, "failed to getService");
}
}
diff --git a/core/java/android/hardware/location/ContextHubMessage.java b/core/java/android/hardware/location/ContextHubMessage.java
index 954e97d..bca2ae6 100644
--- a/core/java/android/hardware/location/ContextHubMessage.java
+++ b/core/java/android/hardware/location/ContextHubMessage.java
@@ -16,10 +16,10 @@
package android.hardware.location;
-
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Log;
import java.util.Arrays;
@@ -32,6 +32,9 @@
private int mVersion;
private byte[]mData;
+ private static final String TAG = "ContextHubMessage";
+
+
/**
* Get the message type
*
@@ -106,9 +109,11 @@
private ContextHubMessage(Parcel in) {
mType = in.readInt();
mVersion = in.readInt();
- byte[] byteBuffer = new byte[in.readInt()];
- in.readByteArray(byteBuffer);
+ int bufferLength = in.readInt();
+ mData = new byte[bufferLength];
+ in.readByteArray(mData);
}
+
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mType);
out.writeInt(mVersion);
diff --git a/core/java/android/hardware/location/ContextHubService.java b/core/java/android/hardware/location/ContextHubService.java
index 274babe..b65e24e 100644
--- a/core/java/android/hardware/location/ContextHubService.java
+++ b/core/java/android/hardware/location/ContextHubService.java
@@ -29,12 +29,30 @@
*/
public class ContextHubService extends IContextHubService.Stub {
+ public static final String CONTEXTHUB_SERVICE = "contexthub_service";
+
private static final String TAG = "ContextHubService";
private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
+ HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
- public static final String CONTEXTHUB_SERVICE = "contexthub_service";
+
+ public static final int ANY_HUB = -1;
+ public static final int MSG_LOAD_NANO_APP = 5;
+ public static final int MSG_UNLOAD_NANO_APP = 2;
+
+ private static final String PRE_LOADED_GENERIC_UNKNOWN = "Preloaded app, unknown";
+ private static final String PRE_LOADED_APP_NAME = PRE_LOADED_GENERIC_UNKNOWN;
+ private static final String PRE_LOADED_APP_PUBLISHER = PRE_LOADED_GENERIC_UNKNOWN;
+ private static final int PRE_LOADED_APP_MEM_REQ = 0;
+
+ private static final int MSG_HEADER_SIZE = 4;
+ private static final int MSG_FIELD_TYPE = 0;
+ private static final int MSG_FIELD_VERSION = 1;
+ private static final int MSG_FIELD_HUB_HANDLE = 2;
+ private static final int MSG_FIELD_APP_INSTANCE = 3;
+
+ private static final int OS_APP_INSTANCE = -1;
private final Context mContext;
@@ -42,44 +60,27 @@
private ContextHubInfo[] mContextHubInfo;
private IContextHubCallback mCallback;
+ private native int nativeSendMessage(int[] header, byte[] data);
+ private native ContextHubInfo[] nativeInitialize();
+
+
public ContextHubService(Context context) {
mContext = context;
mContextHubInfo = nativeInitialize();
+ mNanoAppHash = new HashMap<Integer, NanoAppInstanceInfo>();
for (int i = 0; i < mContextHubInfo.length; i++) {
- Log.v(TAG, "ContextHub[" + i + "] id: " + mContextHubInfo[i].getId()
+ Log.d(TAG, "ContextHub[" + i + "] id: " + mContextHubInfo[i].getId()
+ ", name: " + mContextHubInfo[i].getName());
}
}
- private native int nativeSendMessage(int[] header, byte[] data);
- private native ContextHubInfo[] nativeInitialize();
-
@Override
- public int registerCallback(IContextHubCallback callback) throws RemoteException{
+ public int registerCallback(IContextHubCallback callback) throws RemoteException {
checkPermissions();
- mCallback = callback;
- return 0;
- }
-
-
- private int onMessageReceipt(int[] header, byte[] data) {
- if (mCallback != null) {
- // TODO : Defend against unexpected header sizes
- // Add abstraction for magic numbers
- // onMessageRecipt should pass the right arguments
- ContextHubMessage msg = new ContextHubMessage(header[0], header[1], data);
-
- try {
- mCallback.onMessageReceipt(0, 0, msg);
- } catch (Exception e) {
- Log.e(TAG, "Exception " + e + " when calling remote callback");
- return -1;
- }
- } else {
- Log.d(TAG, "Message Callback is NULL");
+ synchronized(this) {
+ mCallback = callback;
}
-
return 0;
}
@@ -118,14 +119,17 @@
}
// Call Native interface here
- int[] msgHeader = new int[8];
- msgHeader[0] = contextHubHandle;
- msgHeader[1] = app.getAppId();
- msgHeader[2] = app.getAppVersion();
- msgHeader[3] = ContextHubManager.MSG_LOAD_NANO_APP;
- msgHeader[4] = 0; // Loading hints
+ int[] msgHeader = new int[MSG_HEADER_SIZE];
+ msgHeader[MSG_FIELD_HUB_HANDLE] = contextHubHandle;
+ msgHeader[MSG_FIELD_APP_INSTANCE] = OS_APP_INSTANCE;
+ msgHeader[MSG_FIELD_VERSION] = 0;
+ msgHeader[MSG_FIELD_TYPE] = MSG_LOAD_NANO_APP;
- return nativeSendMessage(msgHeader, app.getAppBinary());
+ if (nativeSendMessage(msgHeader, app.getAppBinary()) != 0) {
+ return -1;
+ }
+ // Do not add an entry to mNanoAppInstance Hash yet. The HAL may reject the app
+ return 0;
}
@Override
@@ -137,12 +141,18 @@
}
// Call Native interface here
- int[] msgHeader = new int[8];
- msgHeader[0] = info.getContexthubId();
- msgHeader[1] = ContextHubManager.MSG_UNLOAD_NANO_APP;
- msgHeader[2] = info.getHandle();
+ int[] msgHeader = new int[MSG_HEADER_SIZE];
+ msgHeader[MSG_FIELD_HUB_HANDLE] = ANY_HUB;
+ msgHeader[MSG_FIELD_APP_INSTANCE] = OS_APP_INSTANCE;
+ msgHeader[MSG_FIELD_VERSION] = 0;
+ msgHeader[MSG_FIELD_TYPE] = MSG_UNLOAD_NANO_APP;
- return nativeSendMessage(msgHeader, null);
+ if(nativeSendMessage(msgHeader, null) != 0) {
+ return -1;
+ }
+
+ // Do not add an entry to mNanoAppInstance Hash yet. The HAL may reject the app
+ return 0;
}
@Override
@@ -166,7 +176,7 @@
for(Integer nanoAppInstance : mNanoAppHash.keySet()) {
NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstance);
- if(filter.testMatch(info)){
+ if (filter.testMatch(info)){
foundInstances.add(nanoAppInstance);
}
}
@@ -183,12 +193,12 @@
public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage msg)
throws RemoteException {
checkPermissions();
- int[] msgHeader = new int[8];
- msgHeader[0] = ContextHubManager.MSG_DATA_SEND;
- msgHeader[1] = hubHandle;
- msgHeader[2] = nanoAppHandle;
- msgHeader[3] = msg.getMsgType();
- msgHeader[4] = msg.getVersion();
+
+ int[] msgHeader = new int[MSG_HEADER_SIZE];
+ msgHeader[MSG_FIELD_HUB_HANDLE] = hubHandle;
+ msgHeader[MSG_FIELD_APP_INSTANCE] = nanoAppHandle;
+ msgHeader[MSG_FIELD_VERSION] = msg.getVersion();
+ msgHeader[MSG_FIELD_TYPE] = msg.getMsgType();
return nativeSendMessage(msgHeader, msg.getData());
}
@@ -196,5 +206,52 @@
private void checkPermissions() {
mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
}
-}
+ private int onMessageReceipt(int[] header, byte[] data) {
+ if (header == null || data == null || header.length < MSG_HEADER_SIZE) {
+ return -1;
+ }
+
+ synchronized(this) {
+ if (mCallback != null) {
+ ContextHubMessage msg = new ContextHubMessage(header[MSG_FIELD_TYPE],
+ header[MSG_FIELD_VERSION],
+ data);
+
+ try {
+ mCallback.onMessageReceipt(header[MSG_FIELD_HUB_HANDLE],
+ header[MSG_FIELD_APP_INSTANCE],
+ msg);
+ } catch (Exception e) {
+ Log.w(TAG, "Exception " + e + " when calling remote callback");
+ return -1;
+ }
+ } else {
+ Log.d(TAG, "Message Callback is NULL");
+ }
+ }
+
+ return 0;
+ }
+
+ private int addAppInstance(int hubHandle, int appInstanceHandle, long appId, int appVersion) {
+ // App Id encodes vendor & version
+ NanoAppInstanceInfo appInfo = new NanoAppInstanceInfo();
+
+ appInfo.setAppId(appId);
+ appInfo.setAppVersion(appVersion);
+ appInfo.setName(PRE_LOADED_APP_NAME);
+ appInfo.setContexthubId(hubHandle);
+ appInfo.setHandle(appInstanceHandle);
+ appInfo.setPublisher(PRE_LOADED_APP_PUBLISHER);
+ appInfo.setNeededExecMemBytes(PRE_LOADED_APP_MEM_REQ);
+ appInfo.setNeededReadMemBytes(PRE_LOADED_APP_MEM_REQ);
+ appInfo.setNeededWriteMemBytes(PRE_LOADED_APP_MEM_REQ);
+
+ mNanoAppHash.put(appInstanceHandle, appInfo);
+ Log.d(TAG, "Added app instance " + appInstanceHandle + " with id " + appId
+ + " version " + appVersion);
+
+ return 0;
+ }
+}
diff --git a/core/java/android/hardware/location/NanoAppFilter.java b/core/java/android/hardware/location/NanoAppFilter.java
index 369f9e4..8db70e9 100644
--- a/core/java/android/hardware/location/NanoAppFilter.java
+++ b/core/java/android/hardware/location/NanoAppFilter.java
@@ -20,6 +20,7 @@
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Log;
/**
* @hide
@@ -27,6 +28,8 @@
@SystemApi
public class NanoAppFilter {
+ private static final String TAG = "NanoAppFilter";
+
// The appId, can be set to APP_ID_ANY
private long mAppId;
@@ -54,6 +57,10 @@
* If this flag is set, only versions strictly less than the version specified shall match.
*/
public static final int FLAGS_VERSION_LESS_THAN = 4;
+ /**
+ * If this flag is set, only versions strictly equal to the
+ * version specified shall match.
+ */
public static final int FLAGS_VERSION_STRICTLY_EQUAL = 8;
/**
@@ -117,14 +124,9 @@
* @return true if this is a match, false otherwise
*/
public boolean testMatch(NanoAppInstanceInfo info) {
- if ((mContextHubId == HUB_ANY || info.getContexthubId() == mContextHubId) &&
+ return (mContextHubId == HUB_ANY || info.getContexthubId() == mContextHubId) &&
(mAppId == APP_ANY || info.getAppId() == mAppId) &&
- // (mAppIdVendorMask == VENDOR_ANY) TODO : Expose Vendor mask cleanly
- (versionsMatch(mVersionRestrictionMask, mAppVersion, info.getAppVersion()))) {
- return true;
- } else {
- return false;
- }
+ (versionsMatch(mVersionRestrictionMask, mAppVersion, info.getAppVersion()));
}
public static final Parcelable.Creator<NanoAppFilter> CREATOR
diff --git a/core/java/android/hardware/location/NanoAppInstanceInfo.java b/core/java/android/hardware/location/NanoAppInstanceInfo.java
index ac62919..977f645 100644
--- a/core/java/android/hardware/location/NanoAppInstanceInfo.java
+++ b/core/java/android/hardware/location/NanoAppInstanceInfo.java
@@ -29,7 +29,7 @@
private String mPublisher;
private String mName;
- private int mAppId;
+ private long mAppId;
private int mAppVersion;
private int mNeededReadMemBytes;
@@ -59,6 +59,8 @@
* set the publisher name for the app
*
* @param publisher - name of the publisher
+ *
+ * @hide
*/
public void setPublisher(String publisher) {
mPublisher = publisher;
@@ -77,6 +79,8 @@
* set the name of the app
*
* @param name - name of the app
+ *
+ * @hide
*/
public void setName(String name) {
mName = name;
@@ -87,7 +91,7 @@
*
* @return int - application identifier
*/
- public int getAppId() {
+ public long getAppId() {
return mAppId;
}
@@ -95,8 +99,10 @@
* Set the application identifier
*
* @param appId - application identifier
+ *
+ * @hide
*/
- public void setAppId(int appId) {
+ public void setAppId(long appId) {
mAppId = appId;
}
@@ -113,6 +119,8 @@
* Set the application version
*
* @param appVersion - version of the app
+ *
+ * @hide
*/
public void setAppVersion(int appVersion) {
mAppVersion = appVersion;
@@ -131,6 +139,8 @@
* Set the read memory needed by the app
*
* @param neededReadMemBytes - readable Memory needed in bytes
+ *
+ * @hide
*/
public void setNeededReadMemBytes(int neededReadMemBytes) {
mNeededReadMemBytes = neededReadMemBytes;
@@ -150,6 +160,8 @@
*
* @param neededWriteMemBytes - writable memory needed by the
* app
+ *
+ * @hide
*/
public void setNeededWriteMemBytes(int neededWriteMemBytes) {
mNeededWriteMemBytes = neededWriteMemBytes;
@@ -169,6 +181,8 @@
*
* @param neededExecMemBytes - executable memory needed by the
* app
+ *
+ * @hide
*/
public void setNeededExecMemBytes(int neededExecMemBytes) {
mNeededExecMemBytes = neededExecMemBytes;
@@ -187,6 +201,8 @@
* set the sensors needed by this app
*
* @param neededSensors - all the sensors needed by this app
+ *
+ * @hide
*/
public void setNeededSensors(int[] neededSensors) {
mNeededSensors = neededSensors;
@@ -206,6 +222,8 @@
*
* @param outputEvents - the events that may be generated by
* this app
+ *
+ * @hide
*/
public void setOutputEvents(int[] outputEvents) {
mOutputEvents = outputEvents;
@@ -224,6 +242,8 @@
* set the context hub identifier
*
* @param contexthubId - system wide unique identifier
+ *
+ * @hide
*/
public void setContexthubId(int contexthubId) {
mContexthubId = contexthubId;
@@ -242,6 +262,8 @@
* set the handle for an app instance
*
* @param handle - handle to this instance
+ *
+ * @hide
*/
public void setHandle(int handle) {
mHandle = handle;
@@ -252,7 +274,7 @@
mPublisher = in.readString();
mName = in.readString();
- mAppId = in.readInt();
+ mAppId = in.readLong();
mAppVersion = in.readInt();
mNeededReadMemBytes = in.readInt();
mNeededWriteMemBytes = in.readInt();
@@ -274,7 +296,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeString(mPublisher);
out.writeString(mName);
- out.writeInt(mAppId);
+ out.writeLong(mAppId);
out.writeInt(mAppVersion);
out.writeInt(mContexthubId);
out.writeInt(mNeededReadMemBytes);
@@ -286,7 +308,6 @@
out.writeInt(mOutputEvents.length);
out.writeIntArray(mOutputEvents);
-
}
public static final Parcelable.Creator<NanoAppInstanceInfo> CREATOR
diff --git a/core/java/android/print/PrinterCapabilitiesInfo.java b/core/java/android/print/PrinterCapabilitiesInfo.java
index af41654..01c23f6 100644
--- a/core/java/android/print/PrinterCapabilitiesInfo.java
+++ b/core/java/android/print/PrinterCapabilitiesInfo.java
@@ -233,7 +233,7 @@
mDuplexModes = parcel.readInt();
enforceValidMask(mDuplexModes,
- (currentMode) -> PrintAttributes.enforceValidColorMode(currentMode));
+ (currentMode) -> PrintAttributes.enforceValidDuplexMode(currentMode));
readDefaults(parcel);
Preconditions.checkArgument(mMediaSizes.size() > mDefaults[PROPERTY_MEDIA_SIZE]);
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 6e3dbd8..e3357a7 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -75,8 +75,6 @@
*/
@Widget
public class DatePicker extends FrameLayout {
- private static final String LOG_TAG = DatePicker.class.getSimpleName();
-
private static final int MODE_SPINNER = 1;
private static final int MODE_CALENDAR = 2;
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 5adac01..332e89c 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -378,9 +378,9 @@
mCurrentDate.set(Calendar.MONTH, monthOfYear);
mCurrentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
- mDateChangedListener = callBack;
-
onDateChanged(false, false);
+
+ mDateChangedListener = callBack;
}
@Override
diff --git a/core/java/android/widget/DatePickerSpinnerDelegate.java b/core/java/android/widget/DatePickerSpinnerDelegate.java
index 255de79..d8a3c56 100644
--- a/core/java/android/widget/DatePickerSpinnerDelegate.java
+++ b/core/java/android/widget/DatePickerSpinnerDelegate.java
@@ -244,6 +244,7 @@
setDate(year, monthOfYear, dayOfMonth);
updateSpinners();
updateCalendarView();
+
mOnDateChangedListener = onDateChangedListener;
}
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index fe8916b..9ac4917 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -16,17 +16,15 @@
package android.widget;
-import java.util.ArrayList;
+import com.android.internal.R;
+import android.annotation.AttrRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StyleRes;
import android.content.Context;
-import android.content.res.ColorStateList;
import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.PorterDuff;
import android.graphics.Rect;
-import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
@@ -36,8 +34,7 @@
import android.view.ViewHierarchyEncoder;
import android.widget.RemoteViews.RemoteView;
-import com.android.internal.R;
-
+import java.util.ArrayList;
/**
* FrameLayout is designed to block out an area on the screen to display
@@ -75,31 +72,29 @@
@ViewDebug.ExportedProperty(category = "padding")
private int mForegroundPaddingBottom = 0;
- private final Rect mSelfBounds = new Rect();
- private final Rect mOverlayBounds = new Rect();
-
- private final ArrayList<View> mMatchParentChildren = new ArrayList<View>(1);
-
- public FrameLayout(Context context) {
+ private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1);
+
+ public FrameLayout(@NonNull Context context) {
super(context);
}
-
- public FrameLayout(Context context, @Nullable AttributeSet attrs) {
+
+ public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
- public FrameLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
- public FrameLayout(
- Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ public FrameLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+ @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
final TypedArray a = context.obtainStyledAttributes(
- attrs, com.android.internal.R.styleable.FrameLayout, defStyleAttr, defStyleRes);
-
- if (a.getBoolean(com.android.internal.R.styleable.FrameLayout_measureAllChildren, false)) {
+ attrs, R.styleable.FrameLayout, defStyleAttr, defStyleRes);
+
+ if (a.getBoolean(R.styleable.FrameLayout_measureAllChildren, false)) {
setMeasureAllChildren(true);
}
@@ -171,10 +166,6 @@
mPaddingBottom + mForegroundPaddingBottom;
}
-
- /**
- * {@inheritDoc}
- */
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
@@ -264,17 +255,13 @@
}
}
}
-
- /**
- * {@inheritDoc}
- */
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
- void layoutChildren(int left, int top, int right, int bottom,
- boolean forceLeftGravity) {
+ void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
final int count = getChildCount();
final int parentLeft = getPaddingLeftWithForeground();
@@ -378,12 +365,9 @@
return mMeasureAllChildren;
}
- /**
- * {@inheritDoc}
- */
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new FrameLayout.LayoutParams(getContext(), attrs);
+ return new FrameLayout.LayoutParams(getContext(), attrs);
}
@Override
@@ -391,9 +375,6 @@
return false;
}
- /**
- * {@inheritDoc}
- */
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
@@ -431,34 +412,30 @@
* Per-child layout information for layouts that support margins.
* See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes}
* for a list of all child view attributes that this class supports.
- *
+ *
* @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity
*/
public static class LayoutParams extends MarginLayoutParams {
/**
* The gravity to apply with the View to which these layout parameters
* are associated.
+ * <p>
+ * The default value is {@code Gravity.TOP | Gravity.START}
*
* @see android.view.Gravity
- *
* @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity
*/
- public int gravity = -1;
+ public int gravity = DEFAULT_CHILD_GRAVITY;
- /**
- * {@inheritDoc}
- */
- public LayoutParams(Context c, AttributeSet attrs) {
+ public LayoutParams(@NonNull Context c, @Nullable AttributeSet attrs) {
super(c, attrs);
- TypedArray a = c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.FrameLayout_Layout);
- gravity = a.getInt(com.android.internal.R.styleable.FrameLayout_Layout_layout_gravity, -1);
+ final TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.FrameLayout_Layout);
+ gravity = a.getInt(R.styleable.FrameLayout_Layout_layout_gravity,
+ DEFAULT_CHILD_GRAVITY);
a.recycle();
}
- /**
- * {@inheritDoc}
- */
public LayoutParams(int width, int height) {
super(width, height);
}
@@ -468,9 +445,9 @@
* and weight.
*
* @param width the width, either {@link #MATCH_PARENT},
- * {@link #WRAP_CONTENT} or a fixed size in pixels
+ * {@link #WRAP_CONTENT} or a fixed size in pixels
* @param height the height, either {@link #MATCH_PARENT},
- * {@link #WRAP_CONTENT} or a fixed size in pixels
+ * {@link #WRAP_CONTENT} or a fixed size in pixels
* @param gravity the gravity
*
* @see android.view.Gravity
@@ -480,17 +457,11 @@
this.gravity = gravity;
}
- /**
- * {@inheritDoc}
- */
- public LayoutParams(ViewGroup.LayoutParams source) {
+ public LayoutParams(@NonNull ViewGroup.LayoutParams source) {
super(source);
}
- /**
- * {@inheritDoc}
- */
- public LayoutParams(ViewGroup.MarginLayoutParams source) {
+ public LayoutParams(@NonNull ViewGroup.MarginLayoutParams source) {
super(source);
}
@@ -500,7 +471,7 @@
*
* @param source The layout params to copy from.
*/
- public LayoutParams(LayoutParams source) {
+ public LayoutParams(@NonNull LayoutParams source) {
super(source);
this.gravity = source.gravity;
diff --git a/core/jni/android_hardware_location_ContextHubService.cpp b/core/jni/android_hardware_location_ContextHubService.cpp
index 8724729..d05f82e 100644
--- a/core/jni/android_hardware_location_ContextHubService.cpp
+++ b/core/jni/android_hardware_location_ContextHubService.cpp
@@ -16,23 +16,39 @@
#include "context_hub.h"
+#define LOG_NDEBUG 0
+#define LOG_TAG "ContextHubService"
+
+#include <inttypes.h>
+#include <jni.h>
+#include <map>
+#include <queue>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
+#include <stdlib.h>
-#include <jni.h>
+#include <cutils/log.h>
+
#include "JNIHelp.h"
#include "core_jni_helpers.h"
-#include "stdint.h"
-#include "stdlib.h"
+
+static constexpr int OS_APP_ID=-1;
+
+static constexpr int MIN_APP_ID=1;
+static constexpr int MAX_APP_ID=128;
+
+static constexpr size_t MSG_HEADER_SIZE=4;
+static constexpr int HEADER_FIELD_MSG_TYPE=0;
+static constexpr int HEADER_FIELD_MSG_VERSION=1;
+static constexpr int HEADER_FIELD_HUB_HANDLE=2;
+static constexpr int HEADER_FIELD_APP_INSTANCE=3;
namespace android {
namespace {
-// TODO: We should share this array_length function widely around Android
-// code.
/*
* Finds the length of a statically-sized array using template trickery that
* also prevents it from being applied to the wrong type.
@@ -64,35 +80,207 @@
jmethodID contextHubInfoSetPeakPowerDrawMw;
jmethodID contextHubInfoSetSupportedSensors;
jmethodID contextHubInfoSetMemoryRegions;
+ jmethodID contextHubInfoSetMaxPacketLenBytes;
jmethodID contextHubServiceMsgReceiptCallback;
+ jmethodID contextHubServiceAddAppInstance;
};
struct context_hub_info_s {
- int cookie;
+ uint32_t *cookies;
int numHubs;
const struct context_hub_t *hubs;
struct context_hub_module_t *contextHubModule;
};
+struct app_instance_info_s {
+ uint32_t hubHandle; // Id of the hub this app is on
+ int instanceId; // systemwide unique instance id - assigned
+ struct hub_app_info appInfo; // returned from the HAL
+ uint64_t truncName; // Possibly truncated name - logging
+};
+
struct contextHubServiceDb_s {
int initialized;
context_hub_info_s hubInfo;
jniInfo_s jniInfo;
+ std::queue<int> freeIds;
+ std::map<int, app_instance_info_s *> appInstances;
};
} // unnamed namespace
static contextHubServiceDb_s db;
-int context_hub_callback(uint32_t hub_id, const struct hub_message_t *msg,
+int context_hub_callback(uint32_t hubId, const struct hub_message_t *msg,
void *cookie);
+const context_hub_t *get_hub_info(int hubHandle) {
+ if (hubHandle >= 0 && hubHandle < db.hubInfo.numHubs) {
+ return &db.hubInfo.hubs[hubHandle];
+ }
+ return nullptr;
+}
+
+static int send_msg_to_hub(const hub_message_t *msg, int hubHandle) {
+ const context_hub_t *info = get_hub_info(hubHandle);
+
+ if (info) {
+ return db.hubInfo.contextHubModule->send_message(info->hub_id, msg);
+ } else {
+ ALOGD("%s: Hub information is null for hubHandle %d", __FUNCTION__, hubHandle);
+ return -1;
+ }
+}
+
+static int set_os_app_as_destination(hub_message_t *msg, int hubHandle) {
+ const context_hub_t *info = get_hub_info(hubHandle);
+
+ if (info) {
+ msg->app = info->os_app_name;
+ return 0;
+ } else {
+ ALOGD("%s: Hub information is null for hubHandle %d", __FUNCTION__, hubHandle);
+ return -1;
+ }
+}
+
+static int get_hub_id_for_app_instance(int id) {
+ if (db.appInstances.find(id) == db.appInstances.end()) {
+ ALOGD("%s: Cannot find app for app instance %d", __FUNCTION__, id);
+ return -1;
+ }
+
+ int hubHandle = db.appInstances[id]->hubHandle;
+
+ return db.hubInfo.hubs[hubHandle].hub_id;
+}
+
+static int set_dest_app(hub_message_t *msg, int id) {
+ if (db.appInstances.find(id) == db.appInstances.end()) {
+ ALOGD("%s: Cannod find app for app instance %d", __FUNCTION__, id);
+ return -1;
+ }
+
+ msg->app = db.appInstances[id]->appInfo.name;
+ return 0;
+}
+
+static void send_query_for_apps() {
+ hub_message_t msg;
+
+ msg.message_type = CONTEXT_HUB_QUERY_APPS;
+ msg.message_len = 0;
+
+ for (int i = 0; i < db.hubInfo.numHubs; i++ ) {
+ ALOGD("Sending query for apps to hub %d", i);
+ set_os_app_as_destination(&msg, i);
+ if (send_msg_to_hub(&msg, i) != 0) {
+ ALOGW("Could not query hub %i for apps", i);
+ }
+ }
+}
+
+static int return_id(int id) {
+ // Note : This method is not thread safe.
+ // id returned is guarenteed to be in use
+ db.freeIds.push(id);
+ return 0;
+}
+
+static int generate_id(void) {
+ // Note : This method is not thread safe.
+ int retVal = -1;
+
+ if (!db.freeIds.empty()) {
+ retVal = db.freeIds.front();
+ db.freeIds.pop();
+ }
+
+ return retVal;
+}
+
+int add_app_instance(const hub_app_info *appInfo, uint32_t hubHandle, JNIEnv *env) {
+ // Not checking if the apps are indeed distinct
+
+ app_instance_info_s *entry;
+ void *appName;
+ hub_app_name_t *name;
+
+ assert(appInfo && appInfo->name && appInfo->name->app_name);
+
+ entry = (app_instance_info_s *) malloc(sizeof(app_instance_info_s));
+ appName = malloc(appInfo->name->app_name_len);
+ name = (hub_app_name_t *) malloc(sizeof(hub_app_name_t));
+
+ int appInstanceHandle = generate_id();
+
+ if (appInstanceHandle < 0 || !appName || !entry || !name) {
+ ALOGE("Cannot find resources to add app instance %d, %p, %p",
+ appInstanceHandle, appName, entry);
+
+ free(appName);
+ free(entry);
+ free(name);
+
+ if (appInstanceHandle >= 0) {
+ return_id(appInstanceHandle);
+ }
+
+ return -1;
+ }
+
+ memcpy(&(entry->appInfo), appInfo, sizeof(entry->appInfo));
+ memcpy(appName, appInfo->name->app_name, appInfo->name->app_name_len);
+ name->app_name = appName;
+ name->app_name_len = appInfo->name->app_name_len;
+ entry->appInfo.name = name;
+ entry->truncName = 0;
+ memcpy(&(entry->truncName), name->app_name,
+ sizeof(entry->truncName) < name->app_name_len ?
+ sizeof(entry->truncName) : name->app_name_len);
+
+ // Not checking for sanity of hubId
+ entry->hubHandle = hubHandle;
+ entry->instanceId = appInstanceHandle;
+ db.appInstances[appInstanceHandle] = entry;
+
+ // Finally - let the service know of this app instance
+ env->CallIntMethod(db.jniInfo.jContextHubService,
+ db.jniInfo.contextHubServiceAddAppInstance,
+ hubHandle, entry->instanceId, entry->truncName,
+ entry->appInfo.version);
+
+ ALOGW("Added App 0x%" PRIx64 " on hub Handle %" PRId32
+ " as appInstance %d, original name_length %" PRId32, entry->truncName,
+ entry->hubHandle, appInstanceHandle, name->app_name_len);
+
+ return appInstanceHandle;
+}
+
+int delete_app_instance(int id) {
+ if (db.appInstances.find(id) == db.appInstances.end()) {
+ return -1;
+ }
+
+ return_id(id);
+
+ if (db.appInstances[id]) {
+ // Losing the const cast below. This is intentional.
+ free((void *)db.appInstances[id]->appInfo.name->app_name);
+ free((void *)db.appInstances[id]->appInfo.name);
+ free(db.appInstances[id]);
+ db.appInstances.erase(id);
+ }
+
+ return 0;
+}
+
+
static void initContextHubService() {
int err = 0;
- db.hubInfo.hubs = NULL;
+ db.hubInfo.hubs = nullptr;
db.hubInfo.numHubs = 0;
- db.hubInfo.cookie = 0;
int i;
err = hw_get_module(CONTEXT_HUB_MODULE_ID,
@@ -103,26 +291,45 @@
strerror(-err));
}
- if (db.hubInfo.contextHubModule) {
- ALOGD("Fetching hub info");
- db.hubInfo.numHubs = db.hubInfo.contextHubModule->get_hubs(db.hubInfo.contextHubModule,
- &db.hubInfo.hubs);
+ // Prep for storing app info
+ for(i = MIN_APP_ID; i <= MAX_APP_ID; i++) {
+ db.freeIds.push(i);
+ }
- if (db.hubInfo.numHubs > 0) {
- for (i = 0; i < db.hubInfo.numHubs; i++) {
- // TODO : Event though one cookie is OK for now, lets change
- // this to be one per hub
- db.hubInfo.contextHubModule->subscribe_messages(db.hubInfo.hubs[i].hub_id,
- context_hub_callback,
- &db.hubInfo.cookie);
+ if (db.hubInfo.contextHubModule) {
+ int retNumHubs = db.hubInfo.contextHubModule->get_hubs(db.hubInfo.contextHubModule,
+ &db.hubInfo.hubs);
+ ALOGD("ContextHubModule returned %d hubs ", retNumHubs);
+ db.hubInfo.numHubs = retNumHubs;
+
+ if (db.hubInfo.numHubs > 0) {
+ db.hubInfo.numHubs = retNumHubs;
+ db.hubInfo.cookies = (uint32_t *)malloc(sizeof(uint32_t) * db.hubInfo.numHubs);
+
+ if (!db.hubInfo.cookies) {
+ ALOGW("Ran out of memory allocating cookies, bailing");
+ return;
+ }
+
+ for (i = 0; i < db.hubInfo.numHubs; i++) {
+ db.hubInfo.cookies[i] = db.hubInfo.hubs[i].hub_id;
+ if (db.hubInfo.contextHubModule->subscribe_messages(db.hubInfo.hubs[i].hub_id,
+ context_hub_callback,
+ &db.hubInfo.cookies[i]) == 0) {
+ }
+ }
}
- }
+
+ send_query_for_apps();
+ } else {
+ ALOGW("No Context Hub Module present");
}
}
static int onMessageReceipt(int *header, int headerLen, char *msg, int msgLen) {
JNIEnv *env;
- if ((db.jniInfo.vm)->AttachCurrentThread(&env, NULL) != JNI_OK) {
+
+ if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
return -1;
}
@@ -132,28 +339,130 @@
env->SetByteArrayRegion(jmsg, 0, msgLen, (jbyte *)msg);
env->SetIntArrayRegion(jheader, 0, headerLen, (jint *)header);
-
- return env->CallIntMethod(db.jniInfo.jContextHubService,
+ return (env->CallIntMethod(db.jniInfo.jContextHubService,
db.jniInfo.contextHubServiceMsgReceiptCallback,
- jheader, jmsg);
+ jheader, jmsg) != 0);
}
-int context_hub_callback(uint32_t hub_id, const struct hub_message_t *msg,
+int handle_query_apps_response(char *msg, int msgLen, uint32_t hubHandle) {
+ int i;
+ JNIEnv *env;
+ if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
+ return -1;
+ }
+
+ int numApps = msgLen/sizeof(hub_app_info);
+ hub_app_info *info = (hub_app_info *)malloc(msgLen); // handle possible alignment
+
+ if (!info) {
+ return -1;
+ }
+
+ memcpy(info, msg, msgLen);
+ for (i = 0; i < numApps; i++) {
+ add_app_instance(&info[i], hubHandle, env);
+ }
+
+ free(info);
+
+ return 0;
+}
+
+
+int handle_os_message(uint32_t msgType, uint32_t hubHandle,
+ char *msg, int msgLen) {
+ int retVal;
+
+ switch(msgType) {
+ case CONTEXT_HUB_APPS_ENABLE:
+ retVal = 0;
+ break;
+
+ case CONTEXT_HUB_APPS_DISABLE:
+ retVal = 0;
+ break;
+
+ case CONTEXT_HUB_LOAD_APP:
+ retVal = 0;
+ break;
+
+ case CONTEXT_HUB_UNLOAD_APP:
+ retVal = 0;
+ break;
+
+ case CONTEXT_HUB_QUERY_APPS:
+ retVal = handle_query_apps_response(msg, msgLen, hubHandle);
+ break;
+
+ case CONTEXT_HUB_QUERY_MEMORY:
+ retVal = 0;
+ break;
+
+ case CONTEXT_HUB_LOAD_OS:
+ retVal = 0;
+ break;
+
+ default:
+ retVal = -1;
+ break;
+
+ }
+
+ return retVal;
+}
+
+static bool sanity_check_cookie(void *cookie, uint32_t hub_id) {
+ int *ptr = (int *)cookie;
+
+ if (!ptr || *ptr >= db.hubInfo.numHubs) {
+ return false;
+ }
+
+ if (db.hubInfo.hubs[*ptr].hub_id != hub_id) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+int context_hub_callback(uint32_t hubId,
+ const struct hub_message_t *msg,
void *cookie) {
- int msgHeader[4];
+ int msgHeader[MSG_HEADER_SIZE];
- msgHeader[0] = msg->message_type;
- msgHeader[1] = 0; // TODO : HAL does not have a version field
- msgHeader[2] = hub_id;
+ if (!msg) {
+ return -1;
+ }
- onMessageReceipt(msgHeader, sizeof(msgHeader), (char *)msg->message, msg->message_len); // TODO : Populate this
- return 0;
+ msgHeader[HEADER_FIELD_MSG_TYPE] = msg->message_type;
+
+ if (!sanity_check_cookie(cookie, hubId)) {
+ ALOGW("Incorrect cookie %" PRId32 " for cookie %p! Bailing",
+ hubId, cookie);
+
+ return -1;
+ }
+
+ msgHeader[HEADER_FIELD_HUB_HANDLE] = *(uint32_t*)cookie;
+
+ if (msgHeader[HEADER_FIELD_MSG_TYPE] < CONTEXT_HUB_TYPE_PRIVATE_MSG_BASE &&
+ msgHeader[HEADER_FIELD_MSG_TYPE] != 0 ) {
+ handle_os_message(msgHeader[HEADER_FIELD_MSG_TYPE],
+ msgHeader[HEADER_FIELD_HUB_HANDLE],
+ (char *)msg->message,
+ msg->message_len);
+ } else {
+ onMessageReceipt(msgHeader, sizeof(msgHeader),
+ (char *)msg->message, msg->message_len);
+ }
+
+ return 0;
}
static int init_jni(JNIEnv *env, jobject instance) {
if (env->GetJavaVM(&db.jniInfo.vm) != JNI_OK) {
- return -1;
+ return -1;
}
db.jniInfo.jContextHubService = env->NewGlobalRef(instance);
@@ -167,7 +476,6 @@
db.jniInfo.memoryRegionsClass =
env->FindClass("android/hardware/location/MemoryRegion");
- //TODO :: Add error checking
db.jniInfo.contextHubInfoCtor =
env->GetMethodID(db.jniInfo.contextHubInfoClass, "<init>", "()V");
db.jniInfo.contextHubInfoSetId =
@@ -209,6 +517,9 @@
db.jniInfo.contextHubInfoSetMemoryRegions =
env->GetMethodID(db.jniInfo.contextHubInfoClass,
"setMemoryRegions", "([Landroid/hardware/location/MemoryRegion;)V");
+ db.jniInfo.contextHubInfoSetMaxPacketLenBytes =
+ env->GetMethodID(db.jniInfo.contextHubInfoClass,
+ "setMaxPacketLenBytes", "(I)V");
db.jniInfo.contextHubServiceMsgReceiptCallback =
@@ -218,6 +529,11 @@
env->GetMethodID(db.jniInfo.contextHubInfoClass, "setName",
"(Ljava/lang/String;)V");
+ db.jniInfo.contextHubServiceAddAppInstance =
+ env->GetMethodID(db.jniInfo.contextHubServiceClass,
+ "addAppInstance", "(IIJI)I");
+
+
return 0;
}
@@ -245,20 +561,28 @@
env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPlatformVersion, hub->platform_version);
env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetToolchainVersion, hub->toolchain_version);
env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakMips, hub->peak_mips);
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetStoppedPowerDrawMw, hub->stopped_power_draw_mw);
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSleepPowerDrawMw, hub->sleep_power_draw_mw);
- env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakPowerDrawMw, hub->peak_power_draw_mw);
+ env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetStoppedPowerDrawMw,
+ hub->stopped_power_draw_mw);
+ env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSleepPowerDrawMw,
+ hub->sleep_power_draw_mw);
+ env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakPowerDrawMw,
+ hub->peak_power_draw_mw);
+ env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetMaxPacketLenBytes,
+ hub->max_supported_msg_len);
+
// TODO : jintBuf = env->NewIntArray(hub->num_connected_sensors);
- // TODO : env->SetIntArrayRegion(jintBuf, 0, hub->num_connected_sensors, hub->connected_sensors);
+ // TODO : env->SetIntArrayRegion(jintBuf, 0, hub->num_connected_sensors,
+ // hub->connected_sensors);
jintBuf = env->NewIntArray(array_length(dummyConnectedSensors));
env->SetIntArrayRegion(jintBuf, 0, hub->num_connected_sensors, dummyConnectedSensors);
// We are not getting the memory regions from the CH Hal - change this when it is available
- jmemBuf = env->NewObjectArray(0, db.jniInfo.memoryRegionsClass, NULL);
+ jmemBuf = env->NewObjectArray(0, db.jniInfo.memoryRegionsClass, nullptr);
// Note the zero size above. We do not need to set any elements
env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetMemoryRegions, jmemBuf);
+
return jHub;
}
@@ -267,18 +591,18 @@
jobject hub;
jobjectArray retArray;
- initContextHubService();
-
if (init_jni(env, instance) < 0) {
- return NULL;
+ return nullptr;
}
- // Note : The service is clamping the number of hubs to 1
- db.hubInfo.numHubs = 1;
-
initContextHubService();
- retArray = env->NewObjectArray(db.hubInfo.numHubs, db.jniInfo.contextHubInfoClass, NULL);
+ if (db.hubInfo.numHubs > 1) {
+ ALOGW("Clamping the number of hubs to 1");
+ db.hubInfo.numHubs = 1;
+ }
+
+ retArray = env->NewObjectArray(db.hubInfo.numHubs, db.jniInfo.contextHubInfoClass, nullptr);
for(int i = 0; i < db.hubInfo.numHubs; i++) {
hub = constructJContextHubInfo(env, &db.hubInfo.hubs[i]);
@@ -291,28 +615,27 @@
static jint nativeSendMessage(JNIEnv *env, jobject instance, jintArray header_,
jbyteArray data_) {
hub_message_t msg;
- hub_app_name_t dest;
- uint8_t os_name[8];
-
- memset(os_name, 0, sizeof(os_name));
+ jint retVal = -1; // Default to failure
jint *header = env->GetIntArrayElements(header_, 0);
- //int numHeaderElements = env->GetArrayLength(header_);
+ unsigned int numHeaderElements = env->GetArrayLength(header_);
jbyte *data = env->GetByteArrayElements(data_, 0);
int dataBufferLength = env->GetArrayLength(data_);
- /* Assume an int - thats all we understand */
- dest.app_name_len = array_length(os_name); // TODO : Check this
- //dest.app_name = &header[1];
- dest.app_name = os_name;
-
- msg.app = &dest;
-
- msg.message_type = header[3];
- msg.message_len = dataBufferLength;
- msg.message = data;
-
- jint retVal = db.hubInfo.contextHubModule->send_message(header[0], &msg);
+ if (numHeaderElements >= MSG_HEADER_SIZE) {
+ if (set_dest_app(&msg, header[HEADER_FIELD_APP_INSTANCE]) == 0) {
+ msg.message_type = header[HEADER_FIELD_MSG_TYPE];
+ msg.message_len = dataBufferLength;
+ msg.message = data;
+ retVal = db.hubInfo.contextHubModule->send_message(
+ get_hub_id_for_app_instance(header[HEADER_FIELD_APP_INSTANCE]),
+ &msg);
+ } else {
+ ALOGD("Could not find app instance %d", header[HEADER_FIELD_APP_INSTANCE]);
+ }
+ } else {
+ ALOGD("Malformed header len");
+ }
env->ReleaseIntArrayElements(header_, header, 0);
env->ReleaseByteArrayElements(data_, data, 0);
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index b1c1b79..2bd9781 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -2666,12 +2666,13 @@
public static final int HEVCHighTierLevel62 = 0x2000000;
// from OMX_VIDEO_DOLBYVISIONPROFILETYPE
- public static final int DolbyVisionProfileDvavDer = 0x1;
- public static final int DolbyVisionProfileDvavDen = 0x2;
- public static final int DolbyVisionProfileDvheDer = 0x3;
- public static final int DolbyVisionProfileDvheDen = 0x4;
- public static final int DolbyVisionProfileDvheDtr = 0x5;
- public static final int DolbyVisionProfileDvheStn = 0x6;
+ public static final int DolbyVisionProfileDvavPer = 0x1;
+ public static final int DolbyVisionProfileDvavPen = 0x2;
+ public static final int DolbyVisionProfileDvheDer = 0x4;
+ public static final int DolbyVisionProfileDvheDen = 0x8;
+ public static final int DolbyVisionProfileDvheDtr = 0x10;
+ public static final int DolbyVisionProfileDvheStn = 0x20;
+ public static final int DolbyVisionProfileDvheDth = 0x40;
// from OMX_VIDEO_DOLBYVISIONLEVELTYPE
public static final int DolbyVisionLevelHd24 = 0x1;
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index acf94f4c..03dc699 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -116,10 +116,10 @@
private final int mType;
private final boolean mIsHardwareInput;
- // TODO: Remove mLabel and mIconUri when createTvInputInfo() is removed.
- private String mLabel;
+ // TODO: Remove mIconUri when createTvInputInfo() is removed.
private Uri mIconUri;
+ private final CharSequence mLabel;
private final int mLabelResId;
private final Icon mIcon;
private final Icon mIconStandby;
@@ -161,8 +161,8 @@
TvInputInfo info = new TvInputInfo.Builder(context, service)
.setHdmiDeviceInfo(hdmiDeviceInfo)
.setParentId(parentId)
+ .setLabel(label)
.build();
- info.mLabel = label;
info.mIconUri = iconUri;
return info;
}
@@ -215,8 +215,8 @@
throws XmlPullParserException, IOException {
TvInputInfo info = new TvInputInfo.Builder(context, service)
.setTvInputHardwareInfo(hardwareInfo)
+ .setLabel(label)
.build();
- info.mLabel = label;
info.mIconUri = iconUri;
return info;
}
@@ -247,7 +247,7 @@
}
private TvInputInfo(ResolveInfo service, String id, int type, boolean isHardwareInput,
- int labelResId, Icon icon, Icon iconStandby, Icon iconDisconnected,
+ CharSequence label, int labelResId, Icon icon, Icon iconStandby, Icon iconDisconnected,
String setupActivity, String settingsActivity, boolean canRecord, int tunerCount,
HdmiDeviceInfo hdmiDeviceInfo, boolean isConnectedToHdmiSwitch, String parentId,
Bundle extras) {
@@ -255,6 +255,7 @@
mId = id;
mType = type;
mIsHardwareInput = isHardwareInput;
+ mLabel = label;
mLabelResId = labelResId;
mIcon = icon;
mIconStandby = iconStandby;
@@ -570,7 +571,7 @@
dest.writeString(mId);
dest.writeInt(mType);
dest.writeByte(mIsHardwareInput ? (byte) 1 : 0);
- dest.writeString(mLabel);
+ TextUtils.writeToParcel(mLabel, dest, flags);
dest.writeParcelable(mIconUri, flags);
dest.writeInt(mLabelResId);
dest.writeParcelable(mIcon, flags);
@@ -612,7 +613,7 @@
mId = in.readString();
mType = in.readInt();
mIsHardwareInput = in.readByte() == 1;
- mLabel = in.readString();
+ mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mIconUri = in.readParcelable(null);
mLabelResId = in.readInt();
mIcon = in.readParcelable(null);
@@ -660,6 +661,7 @@
private final Context mContext;
private final ResolveInfo mResolveInfo;
+ private CharSequence mLabel;
private int mLabelResId;
private Icon mIcon;
private Icon mIconStandby;
@@ -746,12 +748,31 @@
/**
* Sets the label.
*
+ * @param label The text to be used as label.
+ * @return This Builder object to allow for chaining of calls to builder methods.
+ * @hide
+ */
+ @SystemApi
+ public Builder setLabel(CharSequence label) {
+ if (mLabelResId != 0) {
+ throw new IllegalStateException("Resource ID for label is already set.");
+ }
+ this.mLabel = label;
+ return this;
+ }
+
+ /**
+ * Sets the label.
+ *
* @param resId The resource ID of the text to use.
* @return This Builder object to allow for chaining of calls to builder methods.
* @hide
*/
@SystemApi
public Builder setLabel(int resId) {
+ if (mLabel != null) {
+ throw new IllegalStateException("Label text is already set.");
+ }
this.mLabelResId = resId;
return this;
}
@@ -868,8 +889,8 @@
type = TYPE_TUNER;
}
parseServiceMetadata(type);
- return new TvInputInfo(mResolveInfo, id, type, isHardwareInput, mLabelResId, mIcon,
- mIconStandby, mIconDisconnected, mSetupActivity, mSettingsActivity,
+ return new TvInputInfo(mResolveInfo, id, type, isHardwareInput, mLabel, mLabelResId,
+ mIcon, mIconStandby, mIconDisconnected, mSetupActivity, mSettingsActivity,
mCanRecord == null ? false : mCanRecord, mTunerCount == null ? 0 : mTunerCount,
mHdmiDeviceInfo, isConnectedToHdmiSwitch, mParentId, mExtras);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 70b478a..9606eab54 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -272,6 +272,7 @@
@Override
public boolean onOptionsItemSelected(MenuItem item) {
+ Metrics.logMenuAction(this, item.getItemId());
switch (item.getItemId()) {
case android.R.id.home:
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index 68c0c2a..e7d7ec4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -212,19 +212,22 @@
case R.id.menu_create_dir:
assert(canCreateDirectory());
showCreateDirectoryDialog();
- return true;
+ break;
case R.id.menu_new_window:
createNewWindow();
- return true;
+ break;
case R.id.menu_paste_from_clipboard:
DirectoryFragment dir = getDirectoryFragment();
if (dir != null) {
dir.pasteFromClipboard();
}
- return true;
+ break;
+ default:
+ return super.onOptionsItemSelected(item);
}
- return super.onOptionsItemSelected(item);
+ Metrics.logMenuAction(this, item.getItemId());
+ return true;
}
private void createNewWindow() {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
index 99663e3..1e01c22 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
@@ -69,6 +69,7 @@
private static final String COUNT_DRAWER_OPENED = "docsui_drawer_opened";
private static final String COUNT_DRAG_N_DROP = "docsui_drag_n_drop";
private static final String COUNT_SEARCH = "docsui_search";
+ private static final String COUNT_MENU_ACTION = "docsui_menu_action";
// Indices for bucketing roots in the roots histogram. "Other" is the catch-all index for any
// root that is not explicitly recognized by the Metrics code (see {@link
@@ -198,8 +199,71 @@
@Retention(RetentionPolicy.SOURCE)
public @interface MetricsOpType {}
- // Codes representing different launch actions. These are used for bucketing stats in the
- // COUNT_LAUNCH_ACTION histogram.
+ // Codes representing different provider types. Used for sorting file operations when logging.
+ private static final int PROVIDER_INTRA = 0;
+ private static final int PROVIDER_SYSTEM = 1;
+ private static final int PROVIDER_EXTERNAL = 2;
+
+ @IntDef(flag = false, value = {
+ PROVIDER_INTRA,
+ PROVIDER_SYSTEM,
+ PROVIDER_EXTERNAL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Provider {}
+
+
+ // Codes representing different menu actions. These are used for bucketing stats in the
+ // COUNT_MENU_ACTION histogram.
+ // Both regular toolbar menu and action mode menu operations are included.
+ // Do not change or rearrange these values, that will break historical data. Only add to the
+ // list.
+ // Do not use negative numbers or zero; clearcut only handles positive integers.
+ private static final int ACTION_MENU_OTHER = 1;
+ private static final int ACTION_MENU_GRID = 2;
+ private static final int ACTION_MENU_LIST = 3;
+ private static final int ACTION_MENU_SORT = 4;
+ private static final int ACTION_MENU_SORT_NAME = 5;
+ private static final int ACTION_MENU_SORT_DATE = 6;
+ private static final int ACTION_MENU_SORT_SIZE = 7;
+ private static final int ACTION_MENU_SEARCH = 8;
+ private static final int ACTION_MENU_SHOW_SIZE = 9;
+ private static final int ACTION_MENU_SETTINGS = 10;
+ private static final int ACTION_MENU_COPY_TO = 11;
+ private static final int ACTION_MENU_MOVE_TO = 12;
+ private static final int ACTION_MENU_DELETE = 13;
+ private static final int ACTION_MENU_RENAME = 14;
+ private static final int ACTION_MENU_CREATE_DIR = 15;
+ private static final int ACTION_MENU_SELECT_ALL = 16;
+ private static final int ACTION_MENU_SHARE = 17;
+ private static final int ACTION_MENU_OPEN = 18;
+ private static final int ACTION_MENU_ADVANCED = 19;
+
+ @IntDef(flag = false, value = {
+ ACTION_MENU_OTHER,
+ ACTION_MENU_GRID,
+ ACTION_MENU_LIST,
+ ACTION_MENU_SORT,
+ ACTION_MENU_SORT_NAME,
+ ACTION_MENU_SORT_DATE,
+ ACTION_MENU_SORT_SIZE,
+ ACTION_MENU_SHOW_SIZE,
+ ACTION_MENU_SETTINGS,
+ ACTION_MENU_COPY_TO,
+ ACTION_MENU_MOVE_TO,
+ ACTION_MENU_DELETE,
+ ACTION_MENU_RENAME,
+ ACTION_MENU_CREATE_DIR,
+ ACTION_MENU_SELECT_ALL,
+ ACTION_MENU_SHARE,
+ ACTION_MENU_OPEN,
+ ACTION_MENU_ADVANCED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MenuAction {}
+
+ // Codes representing different menu actions. These are used for bucketing stats in the
+ // COUNT_MENU_ACTION histogram.
// Do not change or rearrange these values, that will break historical data. Only add to the
// list.
// Do not use negative numbers or zero; clearcut only handles positive integers.
@@ -225,19 +289,6 @@
@Retention(RetentionPolicy.SOURCE)
public @interface MetricsAction {}
- // Codes representing different provider types. Used for sorting file operations when logging.
- private static final int PROVIDER_INTRA = 0;
- private static final int PROVIDER_SYSTEM = 1;
- private static final int PROVIDER_EXTERNAL = 2;
-
- @IntDef(flag = true, value = {
- PROVIDER_INTRA,
- PROVIDER_SYSTEM,
- PROVIDER_EXTERNAL
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface Provider {}
-
// Codes representing different actions to open the drawer. They are used for bucketing stats in
// the COUNT_DRAWER_OPENED histogram.
// Do not change or rearrange these values, that will break historical data. Only add to the
@@ -589,6 +640,74 @@
}
/**
+ * Logs menu action that was selected by user.
+ * @param context
+ * @param id Resource id of the menu item.
+ */
+ public static void logMenuAction(Context context, int id) {
+ @MenuAction int menuAction = ACTION_MENU_OTHER;
+ switch (id) {
+ case R.id.menu_grid:
+ menuAction = ACTION_MENU_GRID;
+ break;
+ case R.id.menu_list:
+ menuAction = ACTION_MENU_LIST;
+ break;
+ case R.id.menu_sort:
+ menuAction = ACTION_MENU_SORT;
+ break;
+ case R.id.menu_sort_name:
+ menuAction = ACTION_MENU_SORT_NAME;
+ break;
+ case R.id.menu_sort_date:
+ menuAction = ACTION_MENU_SORT_DATE;
+ break;
+ case R.id.menu_sort_size:
+ menuAction = ACTION_MENU_SORT_SIZE;
+ break;
+ case R.id.menu_search:
+ menuAction = ACTION_MENU_SEARCH;
+ break;
+ case R.id.menu_file_size:
+ menuAction = ACTION_MENU_SHOW_SIZE;
+ break;
+ case R.id.menu_settings:
+ menuAction = ACTION_MENU_SETTINGS;
+ break;
+ case R.id.menu_copy_to:
+ menuAction = ACTION_MENU_COPY_TO;
+ break;
+ case R.id.menu_move_to:
+ menuAction = ACTION_MENU_MOVE_TO;
+ break;
+ case R.id.menu_delete:
+ menuAction = ACTION_MENU_DELETE;
+ break;
+ case R.id.menu_rename:
+ menuAction = ACTION_MENU_RENAME;
+ break;
+ case R.id.menu_create_dir:
+ menuAction = ACTION_MENU_CREATE_DIR;
+ break;
+ case R.id.menu_select_all:
+ menuAction = ACTION_MENU_SELECT_ALL;
+ break;
+ case R.id.menu_share:
+ menuAction = ACTION_MENU_SHARE;
+ break;
+ case R.id.menu_open:
+ menuAction = ACTION_MENU_OPEN;
+ break;
+ case R.id.menu_advanced:
+ menuAction = ACTION_MENU_ADVANCED;
+ break;
+ default:
+ break;
+ }
+ logHistogram(context, COUNT_MENU_ACTION, menuAction);
+ }
+
+ /**
* Internal method for making a MetricsLogger.count call. Increments the given counter by 1.
*
* @param context
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java b/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
index 11b8891..945ed34 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
@@ -185,6 +185,9 @@
if(mFullBar) {
Menu menu = mActionBar.getMenu();
menu.setGroupVisible(R.id.group_hide_when_searching, false);
+ } else {
+ // If search in full-bar mode it will be logged in FilesActivity#onOptionsItemSelected
+ Metrics.logMenuAction(mActionBar.getContext(), R.id.menu_search);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 02f72b4..d70a899 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -595,6 +595,7 @@
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ Metrics.logMenuAction(getContext(), item.getItemId());
Selection selection = mSelectionManager.getSelection(new Selection());
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 43d627d..ec873ec 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -794,6 +794,9 @@
synchronized (mHeaderBarLock) {
int toHeaderWidth = (int) toTransform.rect.width();
int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
+ if (toHeaderWidth <= 0 || toHeaderHeight <= 0) {
+ return null;
+ }
boolean disabledInSafeMode = !toTask.isSystemApp && ssp.isInSafeMode();
mHeaderBar.onTaskViewSizeChanged((int) toTransform.rect.width(),
(int) toTransform.rect.height());
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 2508304..1cddde7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -803,8 +803,9 @@
public TaskViewTransform getStackTransformScreenCoordinates(Task task, float stackScroll,
TaskViewTransform transformOut, TaskViewTransform frontTransform) {
Rect windowRect = Recents.getSystemServices().getWindowRect();
- TaskViewTransform transform = getStackTransform(task, stackScroll, transformOut,
- frontTransform);
+ TaskViewTransform transform = getStackTransform(task, stackScroll, mFocusState,
+ transformOut, frontTransform, true /* forceUpdate */,
+ false /* ignoreTaskOverrides */);
transform.rect.offset(windowRect.left, windowRect.top);
return transform;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 0d70e99..811b48f 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2738,12 +2738,21 @@
// Called when WindowManager has finished animating the launchingBehind activity to the back.
void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
- r.mLaunchTaskBehind = false;
final TaskRecord task = r.task;
- task.setLastThumbnailLocked(task.stack.screenshotActivitiesLocked(r));
+ final ActivityStack stack = task.stack;
+
+ r.mLaunchTaskBehind = false;
+ task.setLastThumbnailLocked(stack.screenshotActivitiesLocked(r));
mRecentTasks.addLocked(task);
mService.notifyTaskStackChangedLocked();
mWindowManager.setAppVisibility(r.appToken, false);
+
+ // When launching tasks behind, update the last active time of the top task after the new
+ // task has been shown briefly
+ final ActivityRecord top = stack.topActivity();
+ if (top != null) {
+ top.task.touchActiveTime();
+ }
}
void scheduleLaunchTaskBehindComplete(IBinder token) {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index b157070..e32d1d1 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -285,6 +285,7 @@
mCallingPackage = info.packageName;
setIntent(_intent, info);
setMinDimensions(info);
+ touchActiveTime();
}
TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
@@ -315,6 +316,7 @@
taskType = APPLICATION_ACTIVITY_TYPE;
mTaskToReturnTo = HOME_ACTIVITY_TYPE;
lastTaskDescription = _taskDescription;
+ touchActiveTime();
}
private TaskRecord(ActivityManagerService service, int _taskId, Intent _intent,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 022b10f..124d7f1 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -120,6 +120,7 @@
import android.widget.Toast;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
@@ -226,7 +227,7 @@
private VrManagerInternal mVrManagerInternal;
final IBinder mForegroundToken = new Binder();
- private WorkerHandler mHandler;
+ private Handler mHandler;
private final HandlerThread mRankingThread = new HandlerThread("ranker",
Process.THREAD_PRIORITY_BACKGROUND);
@@ -572,33 +573,9 @@
public void clearEffects() {
synchronized (mNotificationList) {
if (DBG) Slog.d(TAG, "clearEffects");
-
- // sound
- mSoundNotificationKey = null;
-
- long identity = Binder.clearCallingIdentity();
- try {
- final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
- if (player != null) {
- player.stopAsync();
- }
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
-
- // vibrate
- mVibrateNotificationKey = null;
- identity = Binder.clearCallingIdentity();
- try {
- mVibrator.cancel();
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
-
- // light
- mLights.clear();
- updateLightsLocked();
+ clearSoundLocked();
+ clearVibrateLocked();
+ clearLightsLocked();
}
}
@@ -658,6 +635,36 @@
}
};
+ private void clearSoundLocked() {
+ mSoundNotificationKey = null;
+ long identity = Binder.clearCallingIdentity();
+ try {
+ final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
+ if (player != null) {
+ player.stopAsync();
+ }
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private void clearVibrateLocked() {
+ mVibrateNotificationKey = null;
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mVibrator.cancel();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ private void clearLightsLocked() {
+ // light
+ mLights.clear();
+ updateLightsLocked();
+ }
+
private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -863,6 +870,26 @@
super(context);
}
+ @VisibleForTesting
+ void setAudioManager(AudioManager audioMananger) {
+ mAudioManager = audioMananger;
+ }
+
+ @VisibleForTesting
+ void setVibrator(Vibrator vibrator) {
+ mVibrator = vibrator;
+ }
+
+ @VisibleForTesting
+ void setSystemReady(boolean systemReady) {
+ mSystemReady = systemReady;
+ }
+
+ @VisibleForTesting
+ void setHandler(Handler handler) {
+ mHandler = handler;
+ }
+
@Override
public void onStart() {
Resources resources = getContext().getResources();
@@ -2492,12 +2519,14 @@
return false;
}
- private void buzzBeepBlinkLocked(NotificationRecord record) {
+ @VisibleForTesting
+ void buzzBeepBlinkLocked(NotificationRecord record) {
boolean buzz = false;
boolean beep = false;
boolean blink = false;
final Notification notification = record.sbn.getNotification();
+ final String key = record.getKey();
// Should this notification make noise, vibe, or use the LED?
final boolean aboveThreshold = record.getImportance() >= IMPORTANCE_DEFAULT;
@@ -2521,9 +2550,15 @@
if (disableEffects != null) {
ZenLog.traceDisableEffects(record, disableEffects);
}
+
+ // Remember if this notification already owns the notification channels.
+ boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
+ boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
+
+ // These are set inside the conditional if the notification is allowed to make noise.
+ boolean hasValidVibrate = false;
+ boolean hasValidSound = false;
if (disableEffects == null
- && (!(record.isUpdate
- && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
&& (record.getUserId() == UserHandle.USER_ALL ||
record.getUserId() == currentUser ||
mUserProfiles.isCurrentProfile(record.getUserId()))
@@ -2532,10 +2567,6 @@
&& mAudioManager != null) {
if (DBG) Slog.v(TAG, "Interrupting!");
- sendAccessibilityEvent(notification, record.sbn.getPackageName());
-
- // sound
-
// should we use the default notification sound? (indicated either by
// DEFAULT_SOUND or because notification.sound is pointing at
// Settings.System.NOTIFICATION_SOUND)
@@ -2545,8 +2576,6 @@
.equals(notification.sound);
Uri soundUri = null;
- boolean hasValidSound = false;
-
if (useDefaultSound) {
soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
@@ -2559,88 +2588,105 @@
hasValidSound = (soundUri != null);
}
- if (hasValidSound) {
- boolean looping =
- (notification.flags & Notification.FLAG_INSISTENT) != 0;
- AudioAttributes audioAttributes = audioAttributesForNotification(notification);
- mSoundNotificationKey = record.getKey();
- // do not play notifications if stream volume is 0 (typically because
- // ringer mode is silent) or if there is a user of exclusive audio focus
- if ((mAudioManager.getStreamVolume(
- AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
- && !mAudioManager.isAudioFocusExclusive()) {
- final long identity = Binder.clearCallingIdentity();
- try {
- final IRingtonePlayer player =
- mAudioManager.getRingtonePlayer();
- if (player != null) {
- if (DBG) Slog.v(TAG, "Playing sound " + soundUri
- + " with attributes " + audioAttributes);
- player.playAsync(soundUri, record.sbn.getUser(), looping,
- audioAttributes);
- beep = true;
- }
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
-
- // vibrate
// Does the notification want to specify its own vibration?
final boolean hasCustomVibrate = notification.vibrate != null;
// new in 4.2: if there was supposed to be a sound and we're in vibrate
// mode, and no other vibration is specified, we fall back to vibration
final boolean convertSoundToVibration =
- !hasCustomVibrate
- && hasValidSound
- && (mAudioManager.getRingerModeInternal()
- == AudioManager.RINGER_MODE_VIBRATE);
+ !hasCustomVibrate
+ && hasValidSound
+ && (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE);
// The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
final boolean useDefaultVibrate =
(notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
- if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
- && !(mAudioManager.getRingerModeInternal()
- == AudioManager.RINGER_MODE_SILENT)) {
- mVibrateNotificationKey = record.getKey();
+ hasValidVibrate = useDefaultVibrate || convertSoundToVibration ||
+ hasCustomVibrate;
- if (useDefaultVibrate || convertSoundToVibration) {
- // Escalate privileges so we can use the vibrator even if the
- // notifying app does not have the VIBRATE permission.
- long identity = Binder.clearCallingIdentity();
- try {
- mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
- useDefaultVibrate ? mDefaultVibrationPattern
- : mFallbackVibrationPattern,
- ((notification.flags & Notification.FLAG_INSISTENT) != 0)
- ? 0: -1, audioAttributesForNotification(notification));
- buzz = true;
- } finally {
- Binder.restoreCallingIdentity(identity);
+ // We can alert, and we're allowed to alert, but if the developer asked us to only do
+ // it once, and we already have, then don't.
+ if (!(record.isUpdate
+ && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0)) {
+
+ sendAccessibilityEvent(notification, record.sbn.getPackageName());
+
+ if (hasValidSound) {
+ boolean looping =
+ (notification.flags & Notification.FLAG_INSISTENT) != 0;
+ AudioAttributes audioAttributes = audioAttributesForNotification(notification);
+ mSoundNotificationKey = key;
+ // do not play notifications if stream volume is 0 (typically because
+ // ringer mode is silent) or if there is a user of exclusive audio focus
+ if ((mAudioManager.getStreamVolume(
+ AudioAttributes.toLegacyStreamType(audioAttributes)) != 0)
+ && !mAudioManager.isAudioFocusExclusive()) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final IRingtonePlayer player =
+ mAudioManager.getRingtonePlayer();
+ if (player != null) {
+ if (DBG) Slog.v(TAG, "Playing sound " + soundUri
+ + " with attributes " + audioAttributes);
+ player.playAsync(soundUri, record.sbn.getUser(), looping,
+ audioAttributes);
+ beep = true;
+ }
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
- } else if (notification.vibrate.length > 1) {
- // If you want your own vibration pattern, you need the VIBRATE
- // permission
- mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
- notification.vibrate,
- ((notification.flags & Notification.FLAG_INSISTENT) != 0)
- ? 0: -1, audioAttributesForNotification(notification));
- buzz = true;
+ }
+
+ if (hasValidVibrate && !(mAudioManager.getRingerModeInternal()
+ == AudioManager.RINGER_MODE_SILENT)) {
+ mVibrateNotificationKey = key;
+
+ if (useDefaultVibrate || convertSoundToVibration) {
+ // Escalate privileges so we can use the vibrator even if the
+ // notifying app does not have the VIBRATE permission.
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
+ useDefaultVibrate ? mDefaultVibrationPattern
+ : mFallbackVibrationPattern,
+ ((notification.flags & Notification.FLAG_INSISTENT) != 0)
+ ? 0: -1, audioAttributesForNotification(notification));
+ buzz = true;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } else if (notification.vibrate.length > 1) {
+ // If you want your own vibration pattern, you need the VIBRATE
+ // permission
+ mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
+ notification.vibrate,
+ ((notification.flags & Notification.FLAG_INSISTENT) != 0)
+ ? 0: -1, audioAttributesForNotification(notification));
+ buzz = true;
+ }
}
}
+
+ }
+ // If a notification is updated to remove the actively playing sound or vibrate,
+ // cancel that feedback now
+ if (wasBeep && !hasValidSound) {
+ clearSoundLocked();
+ }
+ if (wasBuzz && !hasValidVibrate) {
+ clearVibrateLocked();
}
// light
// release the light
- boolean wasShowLights = mLights.remove(record.getKey());
+ boolean wasShowLights = mLights.remove(key);
if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold
&& ((record.getSuppressedVisualEffects()
& NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) == 0)) {
- mLights.add(record.getKey());
+ mLights.add(key);
updateLightsLocked();
if (mUseAttentionLight) {
mAttentionLight.pulse();
@@ -2654,7 +2700,7 @@
& NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF) != 0)) {
if (DBG) Slog.v(TAG, "Suppressed SystemUI from triggering screen on");
} else {
- EventLogTags.writeNotificationAlert(record.getKey(),
+ EventLogTags.writeNotificationAlert(key,
buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);
mHandler.post(mBuzzBeepBlinked);
}
diff --git a/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
new file mode 100644
index 0000000..83a59fd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -0,0 +1,541 @@
+/*
+ * Copyright (C) 2016 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.android.server.notification;
+
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.Notification.Builder;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.Vibrator;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.StatusBarNotification;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class BuzzBeepBlinkTest extends AndroidTestCase {
+
+ @Mock AudioManager mAudioManager;
+ @Mock Vibrator mVibrator;
+ @Mock android.media.IRingtonePlayer mRingtonePlayer;
+ @Mock Handler mHandler;
+
+ private NotificationManagerService mService;
+ private String mPkg = "com.android.server.notification";
+ private int mId = 1001;
+ private int mOtherId = 1002;
+ private String mTag = null;
+ private int mUid = 1000;
+ private int mPid = 2000;
+ private int mScore = 10;
+ private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
+
+ @Override
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mAudioManager.isAudioFocusExclusive()).thenReturn(false);
+ when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer);
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10);
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
+
+ mService = new NotificationManagerService(getContext());
+ mService.setAudioManager(mAudioManager);
+ mService.setVibrator(mVibrator);
+ mService.setSystemReady(true);
+ mService.setHandler(mHandler);
+ }
+
+ //
+ // Convenience functions for creating notification records
+ //
+
+ private NotificationRecord getNoisyOtherNotification() {
+ return getNotificationRecord(mOtherId, false /* insistent */, false /* once */,
+ true /* noisy */, true /* buzzy*/);
+ }
+
+ private NotificationRecord getBeepyNotification() {
+ return getNotificationRecord(mId, false /* insistent */, false /* once */,
+ true /* noisy */, false /* buzzy*/);
+ }
+
+ private NotificationRecord getBeepyOnceNotification() {
+ return getNotificationRecord(mId, false /* insistent */, true /* once */,
+ true /* noisy */, false /* buzzy*/);
+ }
+
+ private NotificationRecord getQuietNotification() {
+ return getNotificationRecord(mId, false /* insistent */, false /* once */,
+ false /* noisy */, false /* buzzy*/);
+ }
+
+ private NotificationRecord getQuietOtherNotification() {
+ return getNotificationRecord(mOtherId, false /* insistent */, false /* once */,
+ false /* noisy */, false /* buzzy*/);
+ }
+
+ private NotificationRecord getQuietOnceNotification() {
+ return getNotificationRecord(mId, false /* insistent */, true /* once */,
+ false /* noisy */, false /* buzzy*/);
+ }
+
+ private NotificationRecord getInsistentBeepyNotification() {
+ return getNotificationRecord(mId, true /* insistent */, false /* once */,
+ true /* noisy */, false /* buzzy*/);
+ }
+
+ private NotificationRecord getBuzzyNotification() {
+ return getNotificationRecord(mId, false /* insistent */, false /* once */,
+ false /* noisy */, true /* buzzy*/);
+ }
+
+ private NotificationRecord getBuzzyOnceNotification() {
+ return getNotificationRecord(mId, false /* insistent */, true /* once */,
+ false /* noisy */, true /* buzzy*/);
+ }
+
+ private NotificationRecord getInsistentBuzzyNotification() {
+ return getNotificationRecord(mId, true /* insistent */, false /* once */,
+ false /* noisy */, true /* buzzy*/);
+ }
+
+ private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
+ boolean noisy, boolean buzzy) {
+ final Builder builder = new Builder(getContext())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setPriority(Notification.PRIORITY_HIGH)
+ .setOnlyAlertOnce(once);
+
+ int defaults = 0;
+ if (noisy) {
+ defaults |= Notification.DEFAULT_SOUND;
+ }
+ if (buzzy) {
+ defaults |= Notification.DEFAULT_VIBRATE;
+ }
+ builder.setDefaults(defaults);
+
+ Notification n = builder.build();
+ if (insistent) {
+ n.flags |= Notification.FLAG_INSISTENT;
+ }
+ StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid, mPid,
+ mScore, n, mUser, System.currentTimeMillis());
+ return new NotificationRecord(getContext(), sbn);
+ }
+
+ //
+ // Convenience functions for interacting with mocks
+ //
+
+ private void verifyNeverBeep() throws RemoteException {
+ verify(mRingtonePlayer, never()).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
+ anyBoolean(), (AudioAttributes) anyObject());
+ }
+
+ private void verifyBeep() throws RemoteException {
+ verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
+ eq(true), (AudioAttributes) anyObject());
+ }
+
+ private void verifyBeepLooped() throws RemoteException {
+ verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
+ eq(false), (AudioAttributes) anyObject());
+ }
+
+ private void verifyNeverStopAudio() throws RemoteException {
+ verify(mRingtonePlayer, never()).stopAsync();
+ }
+
+ private void verifyStopAudio() throws RemoteException {
+ verify(mRingtonePlayer, times(1)).stopAsync();
+ }
+
+ private void verifyNeverVibrate() {
+ verify(mVibrator, never()).vibrate(anyInt(), anyString(), (long[]) anyObject(),
+ anyInt(), (AudioAttributes) anyObject());
+ }
+
+ private void verifyVibrate() {
+ verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), (long[]) anyObject(),
+ eq(-1), (AudioAttributes) anyObject());
+ }
+
+ private void verifyVibrateLooped() {
+ verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), (long[]) anyObject(),
+ eq(0), (AudioAttributes) anyObject());
+ }
+
+ private void verifyStopVibrate() {
+ verify(mVibrator, times(1)).cancel();
+ }
+
+ private void verifyNeverStopVibrate() throws RemoteException {
+ verify(mVibrator, never()).cancel();
+ }
+
+ @SmallTest
+ public void testBeep() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyBeepLooped();
+ verifyNeverVibrate();
+ }
+
+ //
+ // Tests
+ //
+
+ @SmallTest
+ public void testBeepInsistently() throws Exception {
+ NotificationRecord r = getInsistentBeepyNotification();
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyBeep();
+ }
+
+ @SmallTest
+ public void testNoInterruptionForMin() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setImportance(Ranking.IMPORTANCE_MIN, "foo");
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyNeverBeep();
+ verifyNeverVibrate();
+ }
+
+ @SmallTest
+ public void testNoInterruptionForIntercepted() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ r.setIntercepted(true);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyNeverBeep();
+ verifyNeverVibrate();
+ }
+
+ @SmallTest
+ public void testBeepTwice() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ Mockito.reset(mRingtonePlayer);
+
+ // update should beep
+ r.isUpdate = true;
+ mService.buzzBeepBlinkLocked(r);
+ verifyBeepLooped();
+ }
+
+ @SmallTest
+ public void testHonorAlertOnlyOnceForBeep() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord s = getBeepyOnceNotification();
+ s.isUpdate = true;
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ Mockito.reset(mRingtonePlayer);
+
+ // update should not beep
+ mService.buzzBeepBlinkLocked(s);
+ verifyNeverBeep();
+ }
+
+ @SmallTest
+ public void testNoisyUpdateDoesNotCancelAudio() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+
+ mService.buzzBeepBlinkLocked(r);
+ r.isUpdate = true;
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyNeverStopAudio();
+ }
+
+ @SmallTest
+ public void testNoisyOnceUpdateDoesNotCancelAudio() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord s = getBeepyOnceNotification();
+ s.isUpdate = true;
+
+ mService.buzzBeepBlinkLocked(r);
+ mService.buzzBeepBlinkLocked(s);
+
+ verifyNeverStopAudio();
+ }
+
+ @SmallTest
+ public void testQuietUpdateDoesNotCancelAudioFromOther() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord s = getQuietNotification();
+ s.isUpdate = true;
+ NotificationRecord other = getNoisyOtherNotification();
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ mService.buzzBeepBlinkLocked(other); // this takes the audio stream
+ Mockito.reset(mRingtonePlayer);
+
+ // should not stop noise, since we no longer own it
+ mService.buzzBeepBlinkLocked(s); // this no longer owns the stream
+ verifyNeverStopAudio();
+ }
+
+ @SmallTest
+ public void testQuietInterloperDoesNotCancelAudio() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord other = getQuietOtherNotification();
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ Mockito.reset(mRingtonePlayer);
+
+ // should not stop noise, since it does not own it
+ mService.buzzBeepBlinkLocked(other);
+ verifyNeverStopAudio();
+ }
+
+ @SmallTest
+ public void testQuietUpdateCancelsAudio() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord s = getQuietNotification();
+ s.isUpdate = true;
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ Mockito.reset(mRingtonePlayer);
+
+ // quiet update should stop making noise
+ mService.buzzBeepBlinkLocked(s);
+ verifyStopAudio();
+ }
+
+ @SmallTest
+ public void testQuietOnceUpdateCancelsAudio() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord s = getQuietOnceNotification();
+ s.isUpdate = true;
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ Mockito.reset(mRingtonePlayer);
+
+ // stop making noise - this is a weird corner case, but quiet should override once
+ mService.buzzBeepBlinkLocked(s);
+ verifyStopAudio();
+ }
+
+ @SmallTest
+ public void testDemoteSoundToVibrate() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+
+ // the phone is quiet
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyNeverBeep();
+ verifyVibrate();
+ }
+
+ @SmallTest
+ public void testDemotInsistenteSoundToVibrate() throws Exception {
+ NotificationRecord r = getInsistentBeepyNotification();
+
+ // the phone is quiet
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyVibrateLooped();
+ }
+
+ @SmallTest
+ public void testVibrate() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyNeverBeep();
+ verifyVibrate();
+ }
+
+ @SmallTest
+ public void testInsistenteVibrate() throws Exception {
+ NotificationRecord r = getInsistentBuzzyNotification();
+
+ mService.buzzBeepBlinkLocked(r);
+ verifyVibrateLooped();
+ }
+
+ @SmallTest
+ public void testVibratTwice() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ Mockito.reset(mVibrator);
+
+ // update should vibrate
+ r.isUpdate = true;
+ mService.buzzBeepBlinkLocked(r);
+ verifyVibrate();
+ }
+
+ @SmallTest
+ public void testHonorAlertOnlyOnceForBuzz() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+ NotificationRecord s = getBuzzyOnceNotification();
+ s.isUpdate = true;
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ Mockito.reset(mVibrator);
+
+ // update should not beep
+ mService.buzzBeepBlinkLocked(s);
+ verifyNeverVibrate();
+ }
+
+ @SmallTest
+ public void testNoisyUpdateDoesNotCancelVibrate() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+
+ mService.buzzBeepBlinkLocked(r);
+ r.isUpdate = true;
+ mService.buzzBeepBlinkLocked(r);
+
+ verifyNeverStopVibrate();
+ }
+
+ @SmallTest
+ public void testNoisyOnceUpdateDoesNotCancelVibrate() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+ NotificationRecord s = getBuzzyOnceNotification();
+ s.isUpdate = true;
+
+ mService.buzzBeepBlinkLocked(r);
+ mService.buzzBeepBlinkLocked(s);
+
+ verifyNeverStopVibrate();
+ }
+
+ @SmallTest
+ public void testQuietUpdateDoesNotCancelVibrateFromOther() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+ NotificationRecord s = getQuietNotification();
+ s.isUpdate = true;
+ NotificationRecord other = getNoisyOtherNotification();
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ mService.buzzBeepBlinkLocked(other); // this takes the vibrate stream
+ Mockito.reset(mVibrator);
+
+ // should not stop vibrate, since we no longer own it
+ mService.buzzBeepBlinkLocked(s); // this no longer owns the stream
+ verifyNeverStopVibrate();
+ }
+
+ @SmallTest
+ public void testQuietInterloperDoesNotCancelVibrate() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+ NotificationRecord other = getQuietOtherNotification();
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ Mockito.reset(mVibrator);
+
+ // should not stop noise, since it does not own it
+ mService.buzzBeepBlinkLocked(other);
+ verifyNeverStopVibrate();
+ }
+
+ @SmallTest
+ public void testQuietUpdateCancelsVibrate() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+ NotificationRecord s = getQuietNotification();
+ s.isUpdate = true;
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+
+ // quiet update should stop making noise
+ mService.buzzBeepBlinkLocked(s);
+ verifyStopVibrate();
+ }
+
+ @SmallTest
+ public void testQuietOnceUpdateCancelsvibrate() throws Exception {
+ NotificationRecord r = getBuzzyNotification();
+ NotificationRecord s = getQuietOnceNotification();
+ s.isUpdate = true;
+
+ // set up internal state
+ mService.buzzBeepBlinkLocked(r);
+ Mockito.reset(mVibrator);
+
+ // stop making noise - this is a weird corner case, but quiet should override once
+ mService.buzzBeepBlinkLocked(s);
+ verifyStopVibrate();
+ }
+
+ @SmallTest
+ public void testQuietUpdateCancelsDemotedVibrate() throws Exception {
+ NotificationRecord r = getBeepyNotification();
+ NotificationRecord s = getQuietNotification();
+
+ // the phone is quiet
+ when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0);
+ when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
+
+ mService.buzzBeepBlinkLocked(r);
+
+ // quiet update should stop making noise
+ mService.buzzBeepBlinkLocked(s);
+ verifyStopVibrate();
+ }
+}
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index ed7351f8..033312b 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -62,7 +62,7 @@
private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts";
private static final String WEARABLE_ACTION_GOOGLE =
"com.google.android.wearable.action.GOOGLE";
- private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 7500; //7.5s to allow app to idle
+ private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 60000; //60s to allow app to idle
private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; //750ms idle for non initial launches
private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 2000; //2s between launching apps