Merge "Fix NPE when the vr manager isn't around at first." into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 8d1b450..04e1005 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8615,6 +8615,7 @@
field public static final java.lang.String ACTION_SENDTO = "android.intent.action.SENDTO";
field public static final java.lang.String ACTION_SEND_MULTIPLE = "android.intent.action.SEND_MULTIPLE";
field public static final java.lang.String ACTION_SET_WALLPAPER = "android.intent.action.SET_WALLPAPER";
+ field public static final java.lang.String ACTION_SHOW_APP_INFO = "android.intent.action.SHOW_APP_INFO";
field public static final java.lang.String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN";
field public static final java.lang.String ACTION_SYNC = "android.intent.action.SYNC";
field public static final java.lang.String ACTION_SYSTEM_TUTORIAL = "android.intent.action.SYSTEM_TUTORIAL";
@@ -8709,6 +8710,7 @@
field public static final java.lang.String EXTRA_MIME_TYPES = "android.intent.extra.MIME_TYPES";
field public static final java.lang.String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE";
field public static final java.lang.String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI";
+ field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
field public static final java.lang.String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
field public static final java.lang.String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
field public static final java.lang.String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
diff --git a/api/system-current.txt b/api/system-current.txt
index b3bac02..f284262 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -8934,6 +8934,7 @@
field public static final java.lang.String ACTION_SENDTO = "android.intent.action.SENDTO";
field public static final java.lang.String ACTION_SEND_MULTIPLE = "android.intent.action.SEND_MULTIPLE";
field public static final java.lang.String ACTION_SET_WALLPAPER = "android.intent.action.SET_WALLPAPER";
+ field public static final java.lang.String ACTION_SHOW_APP_INFO = "android.intent.action.SHOW_APP_INFO";
field public static final java.lang.String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN";
field public static final java.lang.String ACTION_SYNC = "android.intent.action.SYNC";
field public static final java.lang.String ACTION_SYSTEM_TUTORIAL = "android.intent.action.SYSTEM_TUTORIAL";
diff --git a/api/test-current.txt b/api/test-current.txt
index fcfe489..a19a120 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -8622,6 +8622,7 @@
field public static final java.lang.String ACTION_SENDTO = "android.intent.action.SENDTO";
field public static final java.lang.String ACTION_SEND_MULTIPLE = "android.intent.action.SEND_MULTIPLE";
field public static final java.lang.String ACTION_SET_WALLPAPER = "android.intent.action.SET_WALLPAPER";
+ field public static final java.lang.String ACTION_SHOW_APP_INFO = "android.intent.action.SHOW_APP_INFO";
field public static final java.lang.String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN";
field public static final java.lang.String ACTION_SYNC = "android.intent.action.SYNC";
field public static final java.lang.String ACTION_SYSTEM_TUTORIAL = "android.intent.action.SYSTEM_TUTORIAL";
@@ -8716,6 +8717,7 @@
field public static final java.lang.String EXTRA_MIME_TYPES = "android.intent.extra.MIME_TYPES";
field public static final java.lang.String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE";
field public static final java.lang.String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI";
+ field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
field public static final java.lang.String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
field public static final java.lang.String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
field public static final java.lang.String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index d5d4ca7..cd6e572 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -532,7 +532,8 @@
public WifiScanner createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(Context.WIFI_SCANNING_SERVICE);
IWifiScanner service = IWifiScanner.Stub.asInterface(b);
- return new WifiScanner(ctx.getOuterContext(), service);
+ return new WifiScanner(ctx.getOuterContext(), service,
+ ConnectivityThread.getInstanceLooper());
}});
registerService(Context.WIFI_RTT_SERVICE, RttManager.class,
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 9b4f43a..09050b6 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -138,6 +138,20 @@
*/
public static final int PRIORITY_TOP_APP = 40;
+ /**
+ * Adjustment of {@link #getPriority} if the app has often (50% or more of the time)
+ * been running jobs.
+ * @hide
+ */
+ public static final int PRIORITY_ADJ_OFTEN_RUNNING = -40;
+
+ /**
+ * Adjustment of {@link #getPriority} if the app has always (90% or more of the time)
+ * been running jobs.
+ * @hide
+ */
+ public static final int PRIORITY_ADJ_ALWAYS_RUNNING = -80;
+
private final int jobId;
private final PersistableBundle extras;
private final ComponentName service;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7e67e8d..831de4a 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -765,6 +765,19 @@
= "android.intent.action.APPLICATION_PREFERENCES";
/**
+ * Activity Action: Launch an activity showing the app information.
+ * For applications which install other applications (such as app stores), it is recommended
+ * to handle this action for providing the app information to the user.
+ *
+ * <p>Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose information needs
+ * to be displayed.
+ * <p>Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_SHOW_APP_INFO
+ = "android.intent.action.SHOW_APP_INFO";
+
+ /**
* Represents a shortcut/live folder icon resource.
*
* @see Intent#ACTION_CREATE_SHORTCUT
@@ -1683,9 +1696,7 @@
* Type: String
* </p>
*
- * @hide
*/
- @SystemApi
public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
/**
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 94ce57a..239f2d0 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -683,7 +683,7 @@
// interface.
int leftLen = mLeftIndents == null ? 0 : mLeftIndents.length;
int rightLen = mRightIndents == null ? 0 : mRightIndents.length;
- int indentsLen = Math.max(1, Math.min(leftLen, rightLen) - mLineCount);
+ int indentsLen = Math.max(1, Math.max(leftLen, rightLen) - mLineCount);
int[] indents = new int[indentsLen];
for (int i = 0; i < indentsLen; i++) {
int leftMargin = mLeftIndents == null ? 0 :
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 12f2e20..af46756 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1579,7 +1579,7 @@
return true;
}
- final int spaceAbove = displayFrameTop + anchorTopInScreen - anchorHeight;
+ final int spaceAbove = anchorTopInScreen - anchorHeight - displayFrameTop;
if (height <= spaceAbove) {
// Move everything up.
if (mOverlapAnchor) {
diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/core/jni/android_graphics_drawable_VectorDrawable.cpp
index 50d86ff..0de6549 100644
--- a/core/jni/android_graphics_drawable_VectorDrawable.cpp
+++ b/core/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -175,7 +175,7 @@
PathParser::ParseResult result;
PathData data;
- PathParser::getPathDataFromString(&data, &result, pathString, stringLength);
+ PathParser::getPathDataFromAsciiString(&data, &result, pathString, stringLength);
if (result.failureOccurred) {
doThrowIAE(env, result.failureMessage.c_str());
}
diff --git a/core/jni/android_util_PathParser.cpp b/core/jni/android_util_PathParser.cpp
index 0c867f1..53669a8 100644
--- a/core/jni/android_util_PathParser.cpp
+++ b/core/jni/android_util_PathParser.cpp
@@ -34,7 +34,7 @@
SkPath* skPath = reinterpret_cast<SkPath*>(skPathHandle);
PathParser::ParseResult result;
- PathParser::parseStringForSkPath(skPath, &result, pathString, strLength);
+ PathParser::parseAsciiStringForSkPath(skPath, &result, pathString, strLength);
env->ReleaseStringUTFChars(inputPathStr, pathString);
if (result.failureOccurred) {
doThrowIAE(env, result.failureMessage.c_str());
@@ -56,7 +56,7 @@
const char* pathString = env->GetStringUTFChars(inputStr, NULL);
PathData* pathData = new PathData();
PathParser::ParseResult result;
- PathParser::getPathDataFromString(pathData, &result, pathString, strLength);
+ PathParser::getPathDataFromAsciiString(pathData, &result, pathString, strLength);
env->ReleaseStringUTFChars(inputStr, pathString);
if (!result.failureOccurred) {
return reinterpret_cast<jlong>(pathData);
diff --git a/libs/hwui/PathParser.cpp b/libs/hwui/PathParser.cpp
index 7e85333..2179f14 100644
--- a/libs/hwui/PathParser.cpp
+++ b/libs/hwui/PathParser.cpp
@@ -162,7 +162,7 @@
|| verb == 's' || verb == 't' || verb == 'v' || verb == 'z';
}
-void PathParser::getPathDataFromString(PathData* data, ParseResult* result,
+void PathParser::getPathDataFromAsciiString(PathData* data, ParseResult* result,
const char* pathStr, size_t strLen) {
if (pathStr == NULL) {
result->failureOccurred = true;
@@ -171,7 +171,16 @@
}
size_t start = 0;
- size_t end = 1;
+ // Skip leading spaces.
+ while (isspace(pathStr[start]) && start < strLen) {
+ start++;
+ }
+ if (start == strLen) {
+ result->failureOccurred = true;
+ result->failureMessage = "Path string cannot be empty.";
+ return;
+ }
+ size_t end = start + 1;
while (end < strLen) {
end = nextStart(pathStr, strLen, end);
@@ -226,9 +235,9 @@
ALOGD("points are : %s", os.str().c_str());
}
-void PathParser::parseStringForSkPath(SkPath* skPath, ParseResult* result, const char* pathStr, size_t strLen) {
+void PathParser::parseAsciiStringForSkPath(SkPath* skPath, ParseResult* result, const char* pathStr, size_t strLen) {
PathData pathData;
- getPathDataFromString(&pathData, result, pathStr, strLen);
+ getPathDataFromAsciiString(&pathData, result, pathStr, strLen);
if (result->failureOccurred) {
return;
}
diff --git a/libs/hwui/PathParser.h b/libs/hwui/PathParser.h
index 180a7a3..5578e8d 100644
--- a/libs/hwui/PathParser.h
+++ b/libs/hwui/PathParser.h
@@ -39,9 +39,9 @@
/**
* Parse the string literal and create a Skia Path. Return true on success.
*/
- ANDROID_API static void parseStringForSkPath(SkPath* outPath, ParseResult* result,
+ ANDROID_API static void parseAsciiStringForSkPath(SkPath* outPath, ParseResult* result,
const char* pathStr, size_t strLength);
- ANDROID_API static void getPathDataFromString(PathData* outData, ParseResult* result,
+ ANDROID_API static void getPathDataFromAsciiString(PathData* outData, ParseResult* result,
const char* pathStr, size_t strLength);
static void dump(const PathData& data);
static bool isVerbValid(char verb);
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index adfe45c..ac17ed2 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -96,7 +96,7 @@
Path::Path(const char* pathStr, size_t strLength) {
PathParser::ParseResult result;
Data data;
- PathParser::getPathDataFromString(&data, &result, pathStr, strLength);
+ PathParser::getPathDataFromAsciiString(&data, &result, pathStr, strLength);
mStagingProperties.setData(data);
}
diff --git a/libs/hwui/tests/microbench/PathParserBench.cpp b/libs/hwui/tests/microbench/PathParserBench.cpp
index 4186539..b43c4c3 100644
--- a/libs/hwui/tests/microbench/PathParserBench.cpp
+++ b/libs/hwui/tests/microbench/PathParserBench.cpp
@@ -31,7 +31,7 @@
size_t length = strlen(sPathString);
PathParser::ParseResult result;
while (state.KeepRunning()) {
- PathParser::parseStringForSkPath(&skPath, &result, sPathString, length);
+ PathParser::parseAsciiStringForSkPath(&skPath, &result, sPathString, length);
benchmark::DoNotOptimize(&result);
benchmark::DoNotOptimize(&skPath);
}
@@ -43,7 +43,7 @@
PathData outData;
PathParser::ParseResult result;
while (state.KeepRunning()) {
- PathParser::getPathDataFromString(&outData, &result, sPathString, length);
+ PathParser::getPathDataFromAsciiString(&outData, &result, sPathString, length);
benchmark::DoNotOptimize(&result);
benchmark::DoNotOptimize(&outData);
}
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index 332a9a5..83b485f 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -234,9 +234,10 @@
{"3e...3", false}, // Not starting with a verb and ill-formatted float
{"L.M.F.A.O", false}, // No floats following verbs
{"m 1 1", true}, // Valid path data
- {"z", true}, // Valid path data
+ {"\n \t z", true}, // Valid path data with leading spaces
{"1-2e34567", false}, // Not starting with a verb and ill-formatted float
- {"f 4 5", false} // Invalid verb
+ {"f 4 5", false}, // Invalid verb
+ {"\r ", false} // Empty string
};
@@ -250,7 +251,7 @@
// Test generated path data against the given data.
PathData pathData;
size_t length = strlen(testData.pathString);
- PathParser::getPathDataFromString(&pathData, &result, testData.pathString, length);
+ PathParser::getPathDataFromAsciiString(&pathData, &result, testData.pathString, length);
EXPECT_EQ(testData.pathData, pathData);
}
@@ -258,7 +259,7 @@
PathParser::ParseResult result;
PathData pathData;
SkPath skPath;
- PathParser::getPathDataFromString(&pathData, &result,
+ PathParser::getPathDataFromAsciiString(&pathData, &result,
stringPath.stringPath, strlen(stringPath.stringPath));
EXPECT_EQ(stringPath.isValid, !result.failureOccurred);
}
@@ -274,13 +275,13 @@
}
}
-TEST(PathParser, parseStringForSkPath) {
+TEST(PathParser, parseAsciiStringForSkPath) {
for (TestData testData: sTestDataSet) {
PathParser::ParseResult result;
size_t length = strlen(testData.pathString);
// Check the return value as well as the SkPath generated.
SkPath actualPath;
- PathParser::parseStringForSkPath(&actualPath, &result, testData.pathString, length);
+ PathParser::parseAsciiStringForSkPath(&actualPath, &result, testData.pathString, length);
bool hasValidData = !result.failureOccurred;
EXPECT_EQ(hasValidData, testData.pathData.verbs.size() > 0);
SkPath expectedPath;
@@ -291,7 +292,7 @@
for (StringPath stringPath : sStringPaths) {
PathParser::ParseResult result;
SkPath skPath;
- PathParser::parseStringForSkPath(&skPath, &result, stringPath.stringPath,
+ PathParser::parseAsciiStringForSkPath(&skPath, &result, stringPath.stringPath,
strlen(stringPath.stringPath));
EXPECT_EQ(stringPath.isValid, !result.failureOccurred);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index e7b2ed1..fba25ab 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -1110,7 +1110,7 @@
List<String> enabled = new ArrayList<String>();
for (String id : mAdapter.getModelIds()) {
Cursor cursor = getModel().getItem(id);
- if (cursor != null) {
+ if (cursor == null) {
Log.w(TAG, "Skipping selection. Can't obtain cursor for modeId: " + id);
continue;
}
@@ -1202,7 +1202,7 @@
String id = getModelId(v);
if (id != null) {
Cursor dstCursor = mModel.getItem(id);
- if (dstCursor != null) {
+ if (dstCursor == null) {
Log.w(TAG, "Invalid destination. Can't obtain cursor for modelId: " + id);
return null;
}
diff --git a/packages/Shell/res/layout/confirm_repeat.xml b/packages/Shell/res/layout/confirm_repeat.xml
index d12f467..ad90af1 100644
--- a/packages/Shell/res/layout/confirm_repeat.xml
+++ b/packages/Shell/res/layout/confirm_repeat.xml
@@ -38,5 +38,5 @@
android:id="@android:id/checkbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/bugreport_confirm_repeat" />
+ android:text="@string/bugreport_confirm_dont_repeat" />
</LinearLayout>
diff --git a/packages/Shell/res/values/strings.xml b/packages/Shell/res/values/strings.xml
index bee15dd..95e36fd 100644
--- a/packages/Shell/res/values/strings.xml
+++ b/packages/Shell/res/values/strings.xml
@@ -35,9 +35,9 @@
<string name="bugreport_finished_pending_screenshot_text" product="default">Tap to share your bug report without a screenshot or wait for the screenshot to finish</string>
<!-- Body of dialog informing user about contents of a bugreport. [CHAR LIMIT=NONE] -->
- <string name="bugreport_confirm">Bug reports contain data from the system\'s various log files, including personal and private information. Only share bug reports with apps and people you trust.</string>
- <!-- Checkbox that indicates this dialog should be shown again when the next bugreport is taken. [CHAR LIMIT=50] -->
- <string name="bugreport_confirm_repeat">Show this message next time</string>
+ <string name="bugreport_confirm">Bug reports contain data from the system\'s various log files, which may include data you consider sensitive (such as app-usage and location data). Only share bug reports with people and apps you trust.</string>
+ <!-- Checkbox that indicates this dialog should not be shown again when the next bugreport is taken. [CHAR LIMIT=50] -->
+ <string name="bugreport_confirm_dont_repeat">Don\'t show again</string>
<!-- Title for documents backend that offers bugreports. -->
<string name="bugreport_storage_title">Bug reports</string>
diff --git a/packages/Shell/src/com/android/shell/BugreportPrefs.java b/packages/Shell/src/com/android/shell/BugreportPrefs.java
index 3748e89..93690d4 100644
--- a/packages/Shell/src/com/android/shell/BugreportPrefs.java
+++ b/packages/Shell/src/com/android/shell/BugreportPrefs.java
@@ -22,22 +22,24 @@
/**
* Preferences related to bug reports.
*/
-public class BugreportPrefs {
- private static final String PREFS_BUGREPORT = "bugreports";
+final class BugreportPrefs {
+ static final String PREFS_BUGREPORT = "bugreports";
private static final String KEY_WARNING_STATE = "warning-state";
- public static final int STATE_UNKNOWN = 0;
- public static final int STATE_SHOW = 1;
- public static final int STATE_HIDE = 2;
+ static final int STATE_UNKNOWN = 0;
+ // Shows the warning dialog.
+ static final int STATE_SHOW = 1;
+ // Skips the warning dialog.
+ static final int STATE_HIDE = 2;
- public static int getWarningState(Context context, int def) {
+ static int getWarningState(Context context, int def) {
final SharedPreferences prefs = context.getSharedPreferences(
PREFS_BUGREPORT, Context.MODE_PRIVATE);
return prefs.getInt(KEY_WARNING_STATE, def);
}
- public static void setWarningState(Context context, int value) {
+ static void setWarningState(Context context, int value) {
final SharedPreferences prefs = context.getSharedPreferences(
PREFS_BUGREPORT, Context.MODE_PRIVATE);
prefs.edit().putInt(KEY_WARNING_STATE, value).apply();
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index f0ddcb9..502eed1 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -17,7 +17,8 @@
package com.android.shell;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
-import static com.android.shell.BugreportPrefs.STATE_SHOW;
+import static com.android.shell.BugreportPrefs.STATE_HIDE;
+import static com.android.shell.BugreportPrefs.STATE_UNKNOWN;
import static com.android.shell.BugreportPrefs.getWarningState;
import java.io.BufferedOutputStream;
@@ -927,7 +928,7 @@
final Intent notifIntent;
// Send through warning dialog by default
- if (getWarningState(mContext, STATE_SHOW) == STATE_SHOW) {
+ if (getWarningState(mContext, STATE_UNKNOWN) != STATE_HIDE) {
notifIntent = buildWarningIntent(mContext, sendIntent);
} else {
notifIntent = sendIntent;
diff --git a/packages/Shell/src/com/android/shell/BugreportWarningActivity.java b/packages/Shell/src/com/android/shell/BugreportWarningActivity.java
index a1d879a..da33a65 100644
--- a/packages/Shell/src/com/android/shell/BugreportWarningActivity.java
+++ b/packages/Shell/src/com/android/shell/BugreportWarningActivity.java
@@ -59,7 +59,7 @@
ap.mNegativeButtonListener = this;
mConfirmRepeat = (CheckBox) ap.mView.findViewById(android.R.id.checkbox);
- mConfirmRepeat.setChecked(getWarningState(this, STATE_UNKNOWN) == STATE_SHOW);
+ mConfirmRepeat.setChecked(getWarningState(this, STATE_UNKNOWN) != STATE_SHOW);
setupAlert();
}
@@ -68,7 +68,7 @@
public void onClick(DialogInterface dialog, int which) {
if (which == AlertDialog.BUTTON_POSITIVE) {
// Remember confirm state, and launch target
- setWarningState(this, mConfirmRepeat.isChecked() ? STATE_SHOW : STATE_HIDE);
+ setWarningState(this, mConfirmRepeat.isChecked() ? STATE_HIDE : STATE_SHOW);
startActivity(mSendIntent);
}
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index 3eb7754..537e4c5 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -17,7 +17,14 @@
package com.android.shell;
import static android.test.MoreAsserts.assertContainsRegex;
+
import static com.android.shell.ActionSendMultipleConsumerActivity.UI_NAME;
+import static com.android.shell.BugreportPrefs.PREFS_BUGREPORT;
+import static com.android.shell.BugreportPrefs.STATE_HIDE;
+import static com.android.shell.BugreportPrefs.STATE_SHOW;
+import static com.android.shell.BugreportPrefs.STATE_UNKNOWN;
+import static com.android.shell.BugreportPrefs.getWarningState;
+import static com.android.shell.BugreportPrefs.setWarningState;
import static com.android.shell.BugreportProgressService.EXTRA_BUGREPORT;
import static com.android.shell.BugreportProgressService.EXTRA_ID;
import static com.android.shell.BugreportProgressService.EXTRA_MAX;
@@ -48,12 +55,12 @@
import java.util.zip.ZipOutputStream;
import libcore.io.Streams;
+
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.app.Instrumentation;
import android.app.NotificationManager;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@@ -62,7 +69,6 @@
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.support.test.uiautomator.UiSelector;
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.text.TextUtils;
@@ -70,7 +76,6 @@
import android.util.Log;
import com.android.shell.ActionSendMultipleConsumerActivity.CustomActionSendMultipleListener;
-import com.android.shell.BugreportProgressService;
/**
* Integration tests for {@link BugreportReceiver}.
@@ -168,7 +173,7 @@
}
mDescription = sb.toString();
- BugreportPrefs.setWarningState(mContext, BugreportPrefs.STATE_HIDE);
+ setWarningState(mContext, STATE_HIDE);
}
public void testProgress() throws Exception {
@@ -500,9 +505,29 @@
assertServiceNotRunning();
}
- public void testBugreportFinished_withWarning() throws Exception {
- // Explicitly shows the warning.
- BugreportPrefs.setWarningState(mContext, BugreportPrefs.STATE_SHOW);
+ public void testBugreportFinished_withWarningFirstTime() throws Exception {
+ bugreportFinishedWithWarningTest(null);
+ }
+
+ public void testBugreportFinished_withWarningUnknownState() throws Exception {
+ bugreportFinishedWithWarningTest(STATE_UNKNOWN);
+ }
+
+ public void testBugreportFinished_withWarningShowAgain() throws Exception {
+ bugreportFinishedWithWarningTest(STATE_SHOW);
+ }
+
+ private void bugreportFinishedWithWarningTest(Integer propertyState) throws Exception {
+ if (propertyState == null) {
+ // Clear properties
+ mContext.getSharedPreferences(PREFS_BUGREPORT, Context.MODE_PRIVATE)
+ .edit().clear().commit();
+ // Sanity check...
+ assertEquals("Did not reset properties", STATE_UNKNOWN,
+ getWarningState(mContext, STATE_UNKNOWN));
+ } else {
+ setWarningState(mContext, propertyState);
+ }
// Send notification and click on share.
sendBugreportFinished(NO_ID, mPlainTextPath, null);
@@ -510,10 +535,16 @@
// Handle the warning
mUiBot.getVisibleObject(mContext.getString(R.string.bugreport_confirm));
- // TODO: get ok and showMessageAgain from the dialog reference above
- UiObject showMessageAgain =
- mUiBot.getVisibleObject(mContext.getString(R.string.bugreport_confirm_repeat));
- mUiBot.click(showMessageAgain, "show-message-again");
+ // TODO: get ok and dontShowAgain from the dialog reference above
+ UiObject dontShowAgain =
+ mUiBot.getVisibleObject(mContext.getString(R.string.bugreport_confirm_dont_repeat));
+ final boolean firstTime = propertyState == null || propertyState == STATE_UNKNOWN;
+ if (firstTime) {
+ assertTrue("Checkbox should be checked by default", dontShowAgain.isChecked());
+ } else {
+ assertFalse("Checkbox should not be checked", dontShowAgain.isChecked());
+ mUiBot.click(dontShowAgain, "dont-show-again");
+ }
UiObject ok = mUiBot.getVisibleObject(mContext.getString(com.android.internal.R.string.ok));
mUiBot.click(ok, "ok");
@@ -523,8 +554,8 @@
assertActionSendMultiple(extras, BUGREPORT_CONTENT, NO_SCREENSHOT);
// Make sure it's hidden now.
- int newState = BugreportPrefs.getWarningState(mContext, BugreportPrefs.STATE_UNKNOWN);
- assertEquals("Didn't change state", BugreportPrefs.STATE_HIDE, newState);
+ int newState = getWarningState(mContext, STATE_UNKNOWN);
+ assertEquals("Didn't change state", STATE_HIDE, newState);
}
public void testShareBugreportAfterServiceDies() throws Exception {
@@ -876,8 +907,8 @@
}
private String getPath(String file) {
- File rootDir = new ContextWrapper(mContext).getFilesDir();
- File dir = new File(rootDir, BUGREPORTS_DIR);
+ final File rootDir = mContext.getFilesDir();
+ final File dir = new File(rootDir, BUGREPORTS_DIR);
if (!dir.exists()) {
Log.i(TAG, "Creating directory " + dir);
assertTrue("Could not create directory " + dir, dir.mkdir());
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 6ca3af8..0428ecf 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -613,23 +613,22 @@
| FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
}
}
+ for (int j = 0; j < widgetCount; j++) {
+ Widget widget = provider.widgets.get(j);
+ if (targetWidget != null && targetWidget != widget) continue;
+ PendingIntent intent = null;
+ if (onClickIntent != null) {
+ intent = PendingIntent.getActivity(mContext, widget.appWidgetId,
+ onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+ RemoteViews views = createMaskedWidgetRemoteViews(iconBitmap, showBadge, intent);
+ if (widget.replaceWithMaskedViewsLocked(views)) {
+ scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
+ }
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
-
- for (int j = 0; j < widgetCount; j++) {
- Widget widget = provider.widgets.get(j);
- if (targetWidget != null && targetWidget != widget) continue;
- PendingIntent intent = null;
- if (onClickIntent != null) {
- intent = PendingIntent.getActivity(mContext, widget.appWidgetId,
- onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- }
- RemoteViews views = createMaskedWidgetRemoteViews(iconBitmap, showBadge, intent);
- if (widget.replaceWithMaskedViewsLocked(views)) {
- scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
- }
- }
}
private void unmaskWidgetsViewsLocked(Provider provider) {
@@ -1062,8 +1061,6 @@
widget.provider = provider;
widget.options = (options != null) ? cloneIfLocalBinder(options) : new Bundle();
- onWidgetProviderAddedOrChangedLocked(widget);
-
// We need to provide a default value for the widget category if it is not specified
if (!widget.options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) {
widget.options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
@@ -1072,6 +1069,8 @@
provider.widgets.add(widget);
+ onWidgetProviderAddedOrChangedLocked(widget);
+
final int widgetCount = provider.widgets.size();
if (widgetCount == 1) {
// Tell the provider that it's ready.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c01b4f5..4c75f50 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9425,7 +9425,8 @@
if (prev != null && prev.isRecentsActivity()) {
task.setTaskToReturnTo(ActivityRecord.RECENTS_ACTIVITY_TYPE);
}
- mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options, "moveTaskToFront");
+ mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options, "moveTaskToFront",
+ false /* forceNonResizable */);
} finally {
Binder.restoreCallingIdentity(origId);
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 53c6024..598d9ff 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1759,8 +1759,8 @@
}
}
- void findTaskToMoveToFrontLocked(
- TaskRecord task, int flags, ActivityOptions options, String reason) {
+ void findTaskToMoveToFrontLocked(TaskRecord task, int flags, ActivityOptions options,
+ String reason, boolean forceNonResizeable) {
if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
mUserLeaving = true;
}
@@ -1811,7 +1811,8 @@
if (DEBUG_STACK) Slog.d(TAG_STACK,
"findTaskToMoveToFront: moved to front of stack=" + task.stack);
- handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, task.stack.mStackId);
+ handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, task.stack.mStackId,
+ forceNonResizeable);
}
boolean canUseActivityOptionsLaunchBounds(ActivityOptions options, int launchStackId) {
@@ -3360,19 +3361,25 @@
}
}
+ void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredStackId, int actualStackId) {
+ handleNonResizableTaskIfNeeded(task, preferredStackId, actualStackId,
+ false /* forceNonResizable */);
+ }
+
void handleNonResizableTaskIfNeeded(
- TaskRecord task, int preferredStackId, int actualStackId) {
+ TaskRecord task, int preferredStackId, int actualStackId, boolean forceNonResizable) {
if ((!isStackDockedInEffect(actualStackId) && preferredStackId != DOCKED_STACK_ID)
|| task.isHomeTask()) {
return;
}
- if (!task.canGoInDockedStack()) {
+ if (!task.canGoInDockedStack() || forceNonResizable) {
// Display a warning toast that we tried to put a non-dockable task in the docked stack.
mService.mHandler.sendEmptyMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG);
- // Dismiss docked stack.
- mService.moveTasksToFullscreenStack(DOCKED_STACK_ID, false);
+ // Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
+ // we need to move it to top of fullscreen stack, otherwise it will be covered.
+ mService.moveTasksToFullscreenStack(DOCKED_STACK_ID, actualStackId == DOCKED_STACK_ID);
} else if (task.mResizeMode == RESIZE_MODE_FORCE_RESIZEABLE) {
String packageName = task.getTopActivity() != null
? task.getTopActivity().appInfo.packageName : null;
@@ -3443,8 +3450,12 @@
}
if (andResume) {
- findTaskToMoveToFrontLocked(task, 0, null, reason);
+ findTaskToMoveToFrontLocked(task, 0, null, reason,
+ lockTaskModeState != LOCK_TASK_MODE_NONE);
resumeFocusedStackTopActivityLocked();
+ } else if (lockTaskModeState != LOCK_TASK_MODE_NONE) {
+ handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, task.stack.mStackId,
+ true /* forceNonResizable */);
}
}
diff --git a/services/core/java/com/android/server/job/JobPackageTracker.java b/services/core/java/com/android/server/job/JobPackageTracker.java
new file mode 100644
index 0000000..e5a2095
--- /dev/null
+++ b/services/core/java/com/android/server/job/JobPackageTracker.java
@@ -0,0 +1,361 @@
+/*
+ * 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.job;
+
+import android.app.job.JobInfo;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.text.format.DateFormat;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+import com.android.server.job.controllers.JobStatus;
+
+import java.io.PrintWriter;
+
+public final class JobPackageTracker {
+ // We batch every 30 minutes.
+ static final long BATCHING_TIME = 30*60*1000;
+ // Number of historical data sets we keep.
+ static final int NUM_HISTORY = 5;
+
+ DataSet mCurDataSet = new DataSet();
+ DataSet[] mLastDataSets = new DataSet[NUM_HISTORY];
+
+ final static class PackageEntry {
+ long pastActiveTime;
+ long activeStartTime;
+ int activeCount;
+ boolean hadActive;
+ long pastActiveTopTime;
+ long activeTopStartTime;
+ int activeTopCount;
+ boolean hadActiveTop;
+ long pastPendingTime;
+ long pendingStartTime;
+ int pendingCount;
+ boolean hadPending;
+
+ public long getActiveTime(long now) {
+ long time = pastActiveTime;
+ if (activeCount > 0) {
+ time += now - activeStartTime;
+ }
+ return time;
+ }
+
+ public long getActiveTopTime(long now) {
+ long time = pastActiveTopTime;
+ if (activeTopCount > 0) {
+ time += now - activeTopStartTime;
+ }
+ return time;
+ }
+
+ public long getPendingTime(long now) {
+ long time = pastPendingTime;
+ if (pendingCount > 0) {
+ time += now - pendingStartTime;
+ }
+ return time;
+ }
+ }
+
+ final static class DataSet {
+ final SparseArray<ArrayMap<String, PackageEntry>> mEntries = new SparseArray<>();
+ final long mStartUptimeTime;
+ final long mStartElapsedTime;
+ final long mStartClockTime;
+ long mSummedTime;
+
+ public DataSet(DataSet otherTimes) {
+ mStartUptimeTime = otherTimes.mStartUptimeTime;
+ mStartElapsedTime = otherTimes.mStartElapsedTime;
+ mStartClockTime = otherTimes.mStartClockTime;
+ }
+
+ public DataSet() {
+ mStartUptimeTime = SystemClock.uptimeMillis();
+ mStartElapsedTime = SystemClock.elapsedRealtime();
+ mStartClockTime = System.currentTimeMillis();
+ }
+
+ private PackageEntry getOrCreateEntry(int uid, String pkg) {
+ ArrayMap<String, PackageEntry> uidMap = mEntries.get(uid);
+ if (uidMap == null) {
+ uidMap = new ArrayMap<>();
+ mEntries.put(uid, uidMap);
+ }
+ PackageEntry entry = uidMap.get(pkg);
+ if (entry == null) {
+ entry = new PackageEntry();
+ uidMap.put(pkg, entry);
+ }
+ return entry;
+ }
+
+ public PackageEntry getEntry(int uid, String pkg) {
+ ArrayMap<String, PackageEntry> uidMap = mEntries.get(uid);
+ if (uidMap == null) {
+ return null;
+ }
+ return uidMap.get(pkg);
+ }
+
+ long getTotalTime(long now) {
+ if (mSummedTime > 0) {
+ return mSummedTime;
+ }
+ return now - mStartUptimeTime;
+ }
+
+ void incPending(int uid, String pkg, long now) {
+ PackageEntry pe = getOrCreateEntry(uid, pkg);
+ if (pe.pendingCount == 0) {
+ pe.pendingStartTime = now;
+ }
+ pe.pendingCount++;
+ }
+
+ void decPending(int uid, String pkg, long now) {
+ PackageEntry pe = getOrCreateEntry(uid, pkg);
+ if (pe.pendingCount == 1) {
+ pe.pastPendingTime += now - pe.pendingStartTime;
+ }
+ pe.pendingCount--;
+ }
+
+ void incActive(int uid, String pkg, long now) {
+ PackageEntry pe = getOrCreateEntry(uid, pkg);
+ if (pe.activeCount == 0) {
+ pe.activeStartTime = now;
+ }
+ pe.activeCount++;
+ }
+
+ void decActive(int uid, String pkg, long now) {
+ PackageEntry pe = getOrCreateEntry(uid, pkg);
+ if (pe.activeCount == 1) {
+ pe.pastActiveTime += now - pe.activeStartTime;
+ }
+ pe.activeCount--;
+ }
+
+ void incActiveTop(int uid, String pkg, long now) {
+ PackageEntry pe = getOrCreateEntry(uid, pkg);
+ if (pe.activeTopCount == 0) {
+ pe.activeTopStartTime = now;
+ }
+ pe.activeTopCount++;
+ }
+
+ void decActiveTop(int uid, String pkg, long now) {
+ PackageEntry pe = getOrCreateEntry(uid, pkg);
+ if (pe.activeTopCount == 1) {
+ pe.pastActiveTopTime += now - pe.activeTopStartTime;
+ }
+ pe.activeTopCount--;
+ }
+
+ void finish(DataSet next, long now) {
+ for (int i = mEntries.size() - 1; i >= 0; i--) {
+ ArrayMap<String, PackageEntry> uidMap = mEntries.valueAt(i);
+ for (int j = uidMap.size() - 1; j >= 0; j--) {
+ PackageEntry pe = uidMap.valueAt(j);
+ if (pe.activeCount > 0 || pe.activeTopCount > 0 || pe.pendingCount > 0) {
+ // Propagate existing activity in to next data set.
+ PackageEntry nextPe = next.getOrCreateEntry(mEntries.keyAt(i), uidMap.keyAt(j));
+ nextPe.activeStartTime = now;
+ nextPe.activeCount = pe.activeCount;
+ nextPe.activeTopStartTime = now;
+ nextPe.activeTopCount = pe.activeTopCount;
+ nextPe.pendingStartTime = now;
+ nextPe.pendingCount = pe.pendingCount;
+ // Finish it off.
+ if (pe.activeCount > 0) {
+ pe.pastActiveTime += now - pe.activeStartTime;
+ pe.activeCount = 0;
+ }
+ if (pe.activeTopCount > 0) {
+ pe.pastActiveTopTime += now - pe.activeTopStartTime;
+ pe.activeTopCount = 0;
+ }
+ if (pe.pendingCount > 0) {
+ pe.pastPendingTime += now - pe.pendingStartTime;
+ pe.pendingCount = 0;
+ }
+ }
+ }
+ }
+ }
+
+ void addTo(DataSet out, long now) {
+ out.mSummedTime += getTotalTime(now);
+ for (int i = mEntries.size() - 1; i >= 0; i--) {
+ ArrayMap<String, PackageEntry> uidMap = mEntries.valueAt(i);
+ for (int j = uidMap.size() - 1; j >= 0; j--) {
+ PackageEntry pe = uidMap.valueAt(j);
+ PackageEntry outPe = out.getOrCreateEntry(mEntries.keyAt(i), uidMap.keyAt(j));
+ outPe.pastActiveTime += pe.pastActiveTime;
+ outPe.pastActiveTopTime += pe.pastActiveTopTime;
+ outPe.pastPendingTime += pe.pastPendingTime;
+ if (pe.activeCount > 0) {
+ outPe.pastActiveTime += now - pe.activeStartTime;
+ outPe.hadActive = true;
+ }
+ if (pe.activeTopCount > 0) {
+ outPe.pastActiveTopTime += now - pe.activeTopStartTime;
+ outPe.hadActiveTop = true;
+ }
+ if (pe.pendingCount > 0) {
+ outPe.pastPendingTime += now - pe.pendingStartTime;
+ outPe.hadPending = true;
+ }
+ }
+ }
+ }
+
+ void printDuration(PrintWriter pw, long period, long duration, String suffix) {
+ float fraction = duration / (float) period;
+ int percent = (int) ((fraction * 100) + .5f);
+ if (percent > 0) {
+ pw.print(" ");
+ pw.print(percent);
+ pw.print("% ");
+ pw.print(suffix);
+ }
+ }
+
+ void dump(PrintWriter pw, String header, String prefix, long now, long nowEllapsed) {
+ final long period = getTotalTime(now);
+ pw.print(prefix); pw.print(header); pw.print(" at ");
+ pw.print(DateFormat.format("yyyy-MM-dd-HH-mm-ss", mStartClockTime).toString());
+ pw.print(" (");
+ TimeUtils.formatDuration(mStartElapsedTime, nowEllapsed, pw);
+ pw.print(") over ");
+ TimeUtils.formatDuration(period, pw);
+ pw.println(":");
+ final int NE = mEntries.size();
+ for (int i = 0; i < NE; i++) {
+ ArrayMap<String, PackageEntry> uidMap = mEntries.valueAt(i);
+ final int NP = uidMap.size();
+ for (int j = 0; j < NP; j++) {
+ PackageEntry pe = uidMap.valueAt(j);
+ pw.print(prefix); pw.print(" ");
+ UserHandle.formatUid(pw, mEntries.keyAt(i));
+ pw.print(" / "); pw.print(uidMap.keyAt(j));
+ pw.print(":");
+ printDuration(pw, period, pe.getPendingTime(now), "pending");
+ printDuration(pw, period, pe.getActiveTime(now), "active");
+ printDuration(pw, period, pe.getActiveTopTime(now), "active-top");
+ if (pe.pendingCount > 0 || pe.hadPending) {
+ pw.print(" (pending)");
+ }
+ if (pe.activeCount > 0 || pe.hadActive) {
+ pw.print(" (active)");
+ }
+ if (pe.activeTopCount > 0 || pe.hadActiveTop) {
+ pw.print(" (active-top)");
+ }
+ pw.println();
+ }
+ }
+ }
+ }
+
+ void rebatchIfNeeded(long now) {
+ long totalTime = mCurDataSet.getTotalTime(now);
+ if (totalTime > BATCHING_TIME) {
+ DataSet last = mCurDataSet;
+ last.mSummedTime = totalTime;
+ mCurDataSet = new DataSet();
+ last.finish(mCurDataSet, now);
+ System.arraycopy(mLastDataSets, 0, mLastDataSets, 1, mLastDataSets.length-1);
+ mLastDataSets[0] = last;
+ }
+ }
+
+ public void notePending(JobStatus job) {
+ final long now = SystemClock.uptimeMillis();
+ rebatchIfNeeded(now);
+ mCurDataSet.incPending(job.getSourceUid(), job.getSourcePackageName(), now);
+ }
+
+ public void noteNonpending(JobStatus job) {
+ final long now = SystemClock.uptimeMillis();
+ mCurDataSet.decPending(job.getSourceUid(), job.getSourcePackageName(), now);
+ rebatchIfNeeded(now);
+ }
+
+ public void noteActive(JobStatus job) {
+ final long now = SystemClock.uptimeMillis();
+ rebatchIfNeeded(now);
+ if (job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
+ mCurDataSet.incActiveTop(job.getSourceUid(), job.getSourcePackageName(), now);
+ } else {
+ mCurDataSet.incActive(job.getSourceUid(), job.getSourcePackageName(), now);
+ }
+ }
+
+ public void noteInactive(JobStatus job) {
+ final long now = SystemClock.uptimeMillis();
+ if (job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
+ mCurDataSet.decActiveTop(job.getSourceUid(), job.getSourcePackageName(), now);
+ } else {
+ mCurDataSet.decActive(job.getSourceUid(), job.getSourcePackageName(), now);
+ }
+ rebatchIfNeeded(now);
+ }
+
+ public float getLoadFactor(JobStatus job) {
+ final int uid = job.getSourceUid();
+ final String pkg = job.getSourcePackageName();
+ PackageEntry cur = mCurDataSet.getEntry(uid, pkg);
+ PackageEntry last = mLastDataSets[0] != null ? mLastDataSets[0].getEntry(uid, pkg) : null;
+ if (cur == null && last == null) {
+ return 0;
+ }
+ final long now = SystemClock.uptimeMillis();
+ long time = cur.getActiveTime(now) + cur.getPendingTime(now);
+ long period = mCurDataSet.getTotalTime(now);
+ if (last != null) {
+ time += last.getActiveTime(now) + last.getPendingTime(now);
+ period += mLastDataSets[0].getTotalTime(now);
+ }
+ return time / (float)period;
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ final long now = SystemClock.uptimeMillis();
+ final long nowEllapsed = SystemClock.elapsedRealtime();
+ final DataSet total;
+ if (mLastDataSets[0] != null) {
+ total = new DataSet(mLastDataSets[0]);
+ mLastDataSets[0].addTo(total, now);
+ } else {
+ total = new DataSet(mCurDataSet);
+ }
+ mCurDataSet.addTo(total, now);
+ for (int i = 1; i < mLastDataSets.length; i++) {
+ if (mLastDataSets[i] != null) {
+ mLastDataSets[i].dump(pw, "Historical stats", prefix, now, nowEllapsed);
+ pw.println();
+ }
+ }
+ total.dump(pw, "Current stats", prefix, now, nowEllapsed);
+ }
+}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index b235002..c4a2c731 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -93,16 +93,24 @@
public static final boolean DEBUG = false;
/** The maximum number of concurrent jobs we run at one time. */
- private static final int MAX_JOB_CONTEXTS_COUNT = 8;
+ private static final int MAX_JOB_CONTEXTS_COUNT = 12;
+ /** The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app. */
+ private static final int FG_JOB_CONTEXTS_COUNT = 4;
/** Enforce a per-app limit on scheduled jobs? */
private static final boolean ENFORCE_MAX_JOBS = true;
/** The maximum number of jobs that we allow an unprivileged app to schedule */
private static final int MAX_JOBS_PER_APP = 100;
+ /** This is the job execution factor that is considered to be heavy use of the system. */
+ private static final float HEAVY_USE_FACTOR = .9f;
+ /** This is the job execution factor that is considered to be moderate use of the system. */
+ private static final float MODERATE_USE_FACTOR = .5f;
/** Global local for all job scheduler state. */
final Object mLock = new Object();
/** Master list of jobs. */
final JobStore mJobs;
+ /** Tracking amount of time each package runs for. */
+ final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
static final int MSG_JOB_EXPIRED = 0;
static final int MSG_CHECK_JOB = 1;
@@ -173,7 +181,7 @@
* Current limit on the number of concurrent JobServiceContext entries we want to
* keep actively running a job.
*/
- int mMaxActiveJobs = MAX_JOB_CONTEXTS_COUNT - 2;
+ int mMaxActiveJobs = MAX_JOB_CONTEXTS_COUNT - FG_JOB_CONTEXTS_COUNT;
/**
* Which uids are currently in the foreground.
@@ -386,7 +394,9 @@
stopTrackingJob(cancelled, incomingJob, true /* writeBack */);
synchronized (mLock) {
// Remove from pending queue.
- mPendingJobs.remove(cancelled);
+ if (mPendingJobs.remove(cancelled)) {
+ mJobPackageTracker.noteNonpending(cancelled);
+ }
// Cancel if running.
stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED);
reportActive();
@@ -518,7 +528,7 @@
// Create the "runners".
for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
mActiveServices.add(
- new JobServiceContext(this, mBatteryStats,
+ new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
getContext().getMainLooper()));
}
// Attach jobs to their controllers.
@@ -604,6 +614,20 @@
return false;
}
+ void noteJobsPending(List<JobStatus> jobs) {
+ for (int i = jobs.size() - 1; i >= 0; i--) {
+ JobStatus job = jobs.get(i);
+ mJobPackageTracker.notePending(job);
+ }
+ }
+
+ void noteJobsNonpending(List<JobStatus> jobs) {
+ for (int i = jobs.size() - 1; i >= 0; i--) {
+ JobStatus job = jobs.get(i);
+ mJobPackageTracker.noteNonpending(job);
+ }
+ }
+
/**
* Reschedules the given job based on the job's backoff policy. It doesn't make sense to
* specify an override deadline on a failed job (the failed job will run even though it's not
@@ -759,6 +783,7 @@
// state is such that all ready jobs should be run immediately.
if (runNow != null && !mPendingJobs.contains(runNow)
&& mJobs.containsJob(runNow)) {
+ mJobPackageTracker.notePending(runNow);
mPendingJobs.add(runNow);
}
queueReadyJobsForExecutionLockedH();
@@ -797,6 +822,7 @@
if (DEBUG) {
Slog.d(TAG, "queuing all ready jobs for execution:");
}
+ noteJobsNonpending(mPendingJobs);
mPendingJobs.clear();
mJobs.forEachJob(mReadyQueueFunctor);
mReadyQueueFunctor.postProcess();
@@ -832,6 +858,7 @@
public void postProcess() {
if (newReadyJobs != null) {
+ noteJobsPending(newReadyJobs);
mPendingJobs.addAll(newReadyJobs);
}
newReadyJobs = null;
@@ -910,6 +937,7 @@
if (DEBUG) {
Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs.");
}
+ noteJobsPending(runnableJobs);
mPendingJobs.addAll(runnableJobs);
} else {
if (DEBUG) {
@@ -935,6 +963,7 @@
private void maybeQueueReadyJobsForExecutionLockedH() {
if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
+ noteJobsNonpending(mPendingJobs);
mPendingJobs.clear();
mJobs.forEachJob(mMaybeQueueFunctor);
mMaybeQueueFunctor.postProcess();
@@ -998,16 +1027,28 @@
}
}
+ private int adjustJobPriority(int curPriority, JobStatus job) {
+ if (curPriority < JobInfo.PRIORITY_TOP_APP) {
+ float factor = mJobPackageTracker.getLoadFactor(job);
+ if (factor >= HEAVY_USE_FACTOR) {
+ curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
+ } else if (factor >= MODERATE_USE_FACTOR) {
+ curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
+ }
+ }
+ return curPriority;
+ }
+
private int evaluateJobPriorityLocked(JobStatus job) {
int priority = job.getPriority();
if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
- return priority;
+ return adjustJobPriority(priority, job);
}
int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
if (override != 0) {
- return override;
+ return adjustJobPriority(override, job);
}
- return priority;
+ return adjustJobPriority(priority, job);
}
/**
@@ -1029,16 +1070,16 @@
}
switch (memLevel) {
case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
- mMaxActiveJobs = ((MAX_JOB_CONTEXTS_COUNT - 2) * 2) / 3;
+ mMaxActiveJobs = ((MAX_JOB_CONTEXTS_COUNT - FG_JOB_CONTEXTS_COUNT) * 2) / 3;
break;
case ProcessStats.ADJ_MEM_FACTOR_LOW:
- mMaxActiveJobs = (MAX_JOB_CONTEXTS_COUNT - 2) / 3;
+ mMaxActiveJobs = (MAX_JOB_CONTEXTS_COUNT - FG_JOB_CONTEXTS_COUNT) / 3;
break;
case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
mMaxActiveJobs = 1;
break;
default:
- mMaxActiveJobs = MAX_JOB_CONTEXTS_COUNT - 2;
+ mMaxActiveJobs = MAX_JOB_CONTEXTS_COUNT - FG_JOB_CONTEXTS_COUNT;
break;
}
@@ -1134,7 +1175,9 @@
if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
Slog.d(TAG, "Error executing " + pendingJob);
}
- mPendingJobs.remove(pendingJob);
+ if (mPendingJobs.remove(pendingJob)) {
+ mJobPackageTracker.noteNonpending(pendingJob);
+ }
}
}
if (!preservePreferredUid) {
@@ -1444,6 +1487,8 @@
pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
}
pw.println();
+ mJobPackageTracker.dump(pw, "");
+ pw.println();
pw.println("Pending queue:");
for (int i=0; i<mPendingJobs.size(); i++) {
JobStatus job = mPendingJobs.get(i);
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 4239248..4fd1350 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -105,6 +105,7 @@
private final Context mContext;
private final Object mLock;
private final IBatteryStats mBatteryStats;
+ private final JobPackageTracker mJobPackageTracker;
private PowerManager.WakeLock mWakeLock;
// Execution state.
@@ -136,16 +137,18 @@
/** Track when job will timeout. */
private long mTimeoutElapsed;
- JobServiceContext(JobSchedulerService service, IBatteryStats batteryStats, Looper looper) {
- this(service.getContext(), service.getLock(), batteryStats, service, looper);
+ JobServiceContext(JobSchedulerService service, IBatteryStats batteryStats,
+ JobPackageTracker tracker, Looper looper) {
+ this(service.getContext(), service.getLock(), batteryStats, tracker, service, looper);
}
@VisibleForTesting
JobServiceContext(Context context, Object lock, IBatteryStats batteryStats,
- JobCompletedListener completedListener, Looper looper) {
+ JobPackageTracker tracker, JobCompletedListener completedListener, Looper looper) {
mContext = context;
mLock = lock;
mBatteryStats = batteryStats;
+ mJobPackageTracker = tracker;
mCallbackHandler = new JobServiceHandler(looper);
mCompletedListener = completedListener;
mAvailable = true;
@@ -208,6 +211,7 @@
} catch (RemoteException e) {
// Whatever.
}
+ mJobPackageTracker.noteActive(job);
mAvailable = false;
return true;
}
@@ -580,6 +584,7 @@
return;
}
completedJob = mRunningJob;
+ mJobPackageTracker.noteInactive(completedJob);
try {
mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(),
mRunningJob.getSourceUid());
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 57f38d1..40ae652 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -18,6 +18,7 @@
import android.Manifest;
import android.animation.ValueAnimator;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
@@ -153,6 +154,8 @@
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.net.Socket;
import java.text.DateFormat;
import java.util.ArrayList;
@@ -332,6 +335,14 @@
private static final String PROPERTY_BUILD_DATE_UTC = "ro.build.date.utc";
+ // Enums for animation scale update types.
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({WINDOW_ANIMATION_SCALE, TRANSITION_ANIMATION_SCALE, ANIMATION_DURATION_SCALE})
+ private @interface UpdateAnimationScaleMode {};
+ private static final int WINDOW_ANIMATION_SCALE = 0;
+ private static final int TRANSITION_ANIMATION_SCALE = 1;
+ private static final int ANIMATION_DURATION_SCALE = 2;
+
final private KeyguardDisableHandler mKeyguardDisableHandler;
final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -613,18 +624,42 @@
private final class SettingsObserver extends ContentObserver {
private final Uri mDisplayInversionEnabledUri =
Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
+ private final Uri mWindowAnimationScaleUri =
+ Settings.Global.getUriFor(Settings.Global.WINDOW_ANIMATION_SCALE);
+ private final Uri mTransitionAnimationScaleUri =
+ Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE);
+ private final Uri mAnimationDurationScaleUri =
+ Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE);
public SettingsObserver() {
super(new Handler());
ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(mDisplayInversionEnabledUri, false, this,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(mWindowAnimationScaleUri, false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(mTransitionAnimationScaleUri, false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(mAnimationDurationScaleUri, false, this,
+ UserHandle.USER_ALL);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
if (mDisplayInversionEnabledUri.equals(uri)) {
updateCircularDisplayMaskIfNeeded();
+ } else {
+ @UpdateAnimationScaleMode
+ final int mode;
+ if (uri.equals(mWindowAnimationScaleUri)) {
+ mode = WINDOW_ANIMATION_SCALE;
+ } else if (uri.equals(mTransitionAnimationScaleUri)) {
+ mode = TRANSITION_ANIMATION_SCALE;
+ } else { // uri.equals(mAnimationDurationScaleUri)
+ mode = ANIMATION_DURATION_SCALE;
+ }
+ Message m = mH.obtainMessage(H.UPDATE_ANIMATION_SCALE, mode, 0);
+ mH.sendMessage(m);
}
}
}
@@ -7748,6 +7783,8 @@
public static final int NOTIFY_APP_TRANSITION_FINISHED = 49;
public static final int NOTIFY_STARTING_WINDOW_DRAWN = 50;
+ public static final int UPDATE_ANIMATION_SCALE = 51;
+
/**
* Used to denote that an integer field in a message will not be used.
*/
@@ -8030,6 +8067,36 @@
break;
}
+ case UPDATE_ANIMATION_SCALE: {
+ @UpdateAnimationScaleMode
+ final int mode = msg.arg1;
+ switch (mode) {
+ case WINDOW_ANIMATION_SCALE: {
+ mWindowAnimationScaleSetting = Settings.Global.getFloat(
+ mContext.getContentResolver(),
+ Settings.Global.WINDOW_ANIMATION_SCALE,
+ mWindowAnimationScaleSetting);
+ break;
+ }
+ case TRANSITION_ANIMATION_SCALE: {
+ mTransitionAnimationScaleSetting = Settings.Global.getFloat(
+ mContext.getContentResolver(),
+ Settings.Global.TRANSITION_ANIMATION_SCALE,
+ mTransitionAnimationScaleSetting);
+ break;
+ }
+ case ANIMATION_DURATION_SCALE: {
+ mAnimatorDurationScaleSetting = Settings.Global.getFloat(
+ mContext.getContentResolver(),
+ Settings.Global.ANIMATOR_DURATION_SCALE,
+ mAnimatorDurationScaleSetting);
+ dispatchNewAnimatorScaleLocked(null);
+ break;
+ }
+ }
+ break;
+ }
+
case FORCE_GC: {
synchronized (mWindowMap) {
// Since we're holding both mWindowMap and mAnimator we don't need to
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
index 005a483..c1f0038 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
@@ -37,6 +37,8 @@
final int parentLeft, parentTop;
final Matrix matrix;
final String className;
+ final float textSize;
+ final int textColor;
final CharSequence text;
final int scrollY;
final int[] lineCharOffsets;
@@ -50,6 +52,8 @@
this.parentTop = parentTop;
this.matrix = new Matrix(matrix);
this.className = node.getClassName();
+ this.textSize = node.getTextSize();
+ this.textColor = node.getTextColor();
this.text = node.getText() != null ? node.getText() : node.getContentDescription();
this.scrollY = node.getScrollY();
this.lineCharOffsets = node.getTextLineCharOffsets();
@@ -113,7 +117,9 @@
TextEntry te = mTextRects.get(i);
Log.d(TAG, "View " + te.className + " " + te.bounds.toShortString()
+ " in " + te.parentLeft + "," + te.parentTop
- + " matrix=" + te.matrix.toShortString() + ": "
+ + " matrix=" + te.matrix.toShortString()
+ + " size=" + te.textSize + " color=#" + Integer.toHexString(te.textColor)
+ + ": "
+ te.text);
if (te.lineCharOffsets != null && te.lineBaselines != null) {
final int num = te.lineCharOffsets.length < te.lineBaselines.length
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index f8c1ea3..1a8197c 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -687,7 +687,7 @@
Bundle scanParams = new Bundle();
scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
- sAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, key, scanParams);
+ mAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, key, scanParams);
}
/**
@@ -700,7 +700,7 @@
int key = removeListener(listener);
if (key == INVALID_KEY) return;
validateChannel();
- sAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key);
+ mAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, key);
}
/**
* reports currently available scan results on appropriate listeners
@@ -708,7 +708,7 @@
*/
public boolean getScanResults() {
validateChannel();
- Message reply = sAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0);
+ Message reply = mAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0);
return reply.what == CMD_OP_SUCCEEDED;
}
@@ -741,7 +741,7 @@
Bundle scanParams = new Bundle();
scanParams.putParcelable(SCAN_PARAMS_SCAN_SETTINGS_KEY, settings);
scanParams.putParcelable(SCAN_PARAMS_WORK_SOURCE_KEY, workSource);
- sAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams);
+ mAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, key, scanParams);
}
/**
@@ -754,7 +754,7 @@
int key = removeListener(listener);
if (key == INVALID_KEY) return;
validateChannel();
- sAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key);
+ mAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, key);
}
private void startPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings, int key) {
@@ -764,7 +764,7 @@
scanSettings.isPnoScan = true;
pnoParams.putParcelable(PNO_PARAMS_SCAN_SETTINGS_KEY, scanSettings);
pnoParams.putParcelable(PNO_PARAMS_PNO_SETTINGS_KEY, pnoSettings);
- sAsyncChannel.sendMessage(CMD_START_PNO_SCAN, 0, key, pnoParams);
+ mAsyncChannel.sendMessage(CMD_START_PNO_SCAN, 0, key, pnoParams);
}
/**
* Start wifi connected PNO scan
@@ -820,7 +820,7 @@
int key = removeListener(listener);
if (key == INVALID_KEY) return;
validateChannel();
- sAsyncChannel.sendMessage(CMD_STOP_PNO_SCAN, 0, key);
+ mAsyncChannel.sendMessage(CMD_STOP_PNO_SCAN, 0, key);
}
/** specifies information about an access point of interest */
@@ -956,7 +956,7 @@
int key = addListener(listener);
if (key == INVALID_KEY) return;
validateChannel();
- sAsyncChannel.sendMessage(CMD_START_TRACKING_CHANGE, 0, key);
+ mAsyncChannel.sendMessage(CMD_START_TRACKING_CHANGE, 0, key);
}
/**
@@ -968,14 +968,14 @@
int key = removeListener(listener);
if (key == INVALID_KEY) return;
validateChannel();
- sAsyncChannel.sendMessage(CMD_STOP_TRACKING_CHANGE, 0, key);
+ mAsyncChannel.sendMessage(CMD_STOP_TRACKING_CHANGE, 0, key);
}
/** @hide */
@SystemApi
public void configureWifiChange(WifiChangeSettings settings) {
validateChannel();
- sAsyncChannel.sendMessage(CMD_CONFIGURE_WIFI_CHANGE, 0, 0, settings);
+ mAsyncChannel.sendMessage(CMD_CONFIGURE_WIFI_CHANGE, 0, 0, settings);
}
/** interface to receive hotlist events on; use this on {@link #setHotlist} */
@@ -1060,7 +1060,7 @@
HotlistSettings settings = new HotlistSettings();
settings.bssidInfos = bssidInfos;
settings.apLostThreshold = apLostThreshold;
- sAsyncChannel.sendMessage(CMD_SET_HOTLIST, 0, key, settings);
+ mAsyncChannel.sendMessage(CMD_SET_HOTLIST, 0, key, settings);
}
/**
@@ -1072,7 +1072,7 @@
int key = removeListener(listener);
if (key == INVALID_KEY) return;
validateChannel();
- sAsyncChannel.sendMessage(CMD_RESET_HOTLIST, 0, key);
+ mAsyncChannel.sendMessage(CMD_RESET_HOTLIST, 0, key);
}
@@ -1137,17 +1137,14 @@
private IWifiScanner mService;
private static final int INVALID_KEY = 0;
- private static int sListenerKey = 1;
+ private int mListenerKey = 1;
- private static final SparseArray sListenerMap = new SparseArray();
- private static final Object sListenerMapLock = new Object();
+ private final SparseArray mListenerMap = new SparseArray();
+ private final Object mListenerMapLock = new Object();
- private static AsyncChannel sAsyncChannel;
- private static CountDownLatch sConnected;
-
- private static final Object sThreadRefLock = new Object();
- private static int sThreadRefCount;
- private static Handler sInternalHandler;
+ private AsyncChannel mAsyncChannel;
+ private final CountDownLatch mConnected;
+ private final Handler mInternalHandler;
/**
* Create a new WifiScanner instance.
@@ -1156,10 +1153,11 @@
* the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.
* @param context the application context
* @param service the Binder interface
+ * @param looper the Looper used to deliver callbacks
* @hide
*/
- public WifiScanner(Context context, IWifiScanner service) {
- this(context, service, null, true);
+ public WifiScanner(Context context, IWifiScanner service, Looper looper) {
+ this(context, service, looper, true);
}
/**
@@ -1167,8 +1165,7 @@
*
* @param context The application context.
* @param service The IWifiScanner Binder interface
- * @param looper Looper for running WifiScanner operations. If null, a handler thread will be
- * created for running WifiScanner operations.
+ * @param looper the Looper used to deliver callbacks
* @param waitForConnection If true, this will not return until a connection to Wifi Scanner
* service is established.
* @hide
@@ -1178,50 +1175,34 @@
boolean waitForConnection) {
mContext = context;
mService = service;
- init(looper, waitForConnection);
- }
- private void init(Looper looper, boolean waitForConnection) {
- synchronized (sThreadRefLock) {
- if (++sThreadRefCount == 1) {
- Messenger messenger = null;
- try {
- messenger = mService.getMessenger();
- } catch (RemoteException e) {
- /* do nothing */
- } catch (SecurityException e) {
- /* do nothing */
- }
+ Messenger messenger = null;
+ try {
+ messenger = mService.getMessenger();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
- if (messenger == null) {
- sAsyncChannel = null;
- return;
- }
+ if (messenger == null) {
+ throw new IllegalStateException("getMessenger() returned null! This is invalid.");
+ }
- sAsyncChannel = new AsyncChannel();
- sConnected = new CountDownLatch(1);
+ mAsyncChannel = new AsyncChannel();
+ mConnected = new CountDownLatch(1);
- if (looper == null) {
- HandlerThread thread = new HandlerThread("WifiScanner");
- thread.start();
- sInternalHandler = new ServiceHandler(thread.getLooper());
- } else {
- sInternalHandler = new ServiceHandler(looper);
- }
- sAsyncChannel.connect(mContext, sInternalHandler, messenger);
- if (waitForConnection) {
- try {
- sConnected.await();
- } catch (InterruptedException e) {
- Log.e(TAG, "interrupted wait at init");
- }
- }
+ mInternalHandler = new ServiceHandler(looper);
+ mAsyncChannel.connect(mContext, mInternalHandler, messenger);
+ if (waitForConnection) {
+ try {
+ mConnected.await();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "interrupted wait at init");
}
}
}
private void validateChannel() {
- if (sAsyncChannel == null) throw new IllegalStateException(
+ if (mAsyncChannel == null) throw new IllegalStateException(
"No permission to access and change wifi or a bad initialization");
}
@@ -1229,7 +1210,7 @@
// send an error message to internal handler; Otherwise add the listener to the listener map and
// return the key of the listener.
private int addListener(ActionListener listener) {
- synchronized (sListenerMap) {
+ synchronized (mListenerMapLock) {
boolean keyExists = (getListenerKey(listener) != INVALID_KEY);
// Note we need to put the listener into listener map even if it's a duplicate as the
// internal handler will need the key to find the listener. In case of duplicates,
@@ -1239,7 +1220,7 @@
if (DBG) Log.d(TAG, "listener key already exists");
OperationResult operationResult = new OperationResult(REASON_DUPLICATE_REQEUST,
"Outstanding request with same key not stopped yet");
- Message message = Message.obtain(sInternalHandler, CMD_OP_FAILED, 0, key,
+ Message message = Message.obtain(mInternalHandler, CMD_OP_FAILED, 0, key,
operationResult);
message.sendToTarget();
return INVALID_KEY;
@@ -1249,55 +1230,55 @@
}
}
- private static int putListener(Object listener) {
+ private int putListener(Object listener) {
if (listener == null) return INVALID_KEY;
int key;
- synchronized (sListenerMapLock) {
+ synchronized (mListenerMapLock) {
do {
- key = sListenerKey++;
+ key = mListenerKey++;
} while (key == INVALID_KEY);
- sListenerMap.put(key, listener);
+ mListenerMap.put(key, listener);
}
return key;
}
- private static Object getListener(int key) {
+ private Object getListener(int key) {
if (key == INVALID_KEY) return null;
- synchronized (sListenerMapLock) {
- Object listener = sListenerMap.get(key);
+ synchronized (mListenerMapLock) {
+ Object listener = mListenerMap.get(key);
return listener;
}
}
- private static int getListenerKey(Object listener) {
+ private int getListenerKey(Object listener) {
if (listener == null) return INVALID_KEY;
- synchronized (sListenerMapLock) {
- int index = sListenerMap.indexOfValue(listener);
+ synchronized (mListenerMapLock) {
+ int index = mListenerMap.indexOfValue(listener);
if (index == -1) {
return INVALID_KEY;
} else {
- return sListenerMap.keyAt(index);
+ return mListenerMap.keyAt(index);
}
}
}
- private static Object removeListener(int key) {
+ private Object removeListener(int key) {
if (key == INVALID_KEY) return null;
- synchronized (sListenerMapLock) {
- Object listener = sListenerMap.get(key);
- sListenerMap.remove(key);
+ synchronized (mListenerMapLock) {
+ Object listener = mListenerMap.get(key);
+ mListenerMap.remove(key);
return listener;
}
}
- private static int removeListener(Object listener) {
+ private int removeListener(Object listener) {
int key = getListenerKey(listener);
if (key == INVALID_KEY) {
Log.e(TAG, "listener cannot be found");
return key;
}
- synchronized (sListenerMapLock) {
- sListenerMap.remove(key);
+ synchronized (mListenerMapLock) {
+ mListenerMap.remove(key);
return key;
}
}
@@ -1338,7 +1319,7 @@
};
}
- private static class ServiceHandler extends Handler {
+ private class ServiceHandler extends Handler {
ServiceHandler(Looper looper) {
super(looper);
}
@@ -1347,14 +1328,14 @@
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- sAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+ mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
} else {
Log.e(TAG, "Failed to set up channel connection");
// This will cause all further async API calls on the WifiManager
// to fail and throw an exception
- sAsyncChannel = null;
+ mAsyncChannel = null;
}
- sConnected.countDown();
+ mConnected.countDown();
return;
case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
return;
@@ -1362,7 +1343,7 @@
Log.e(TAG, "Channel connection lost");
// This will cause all further async API calls on the WifiManager
// to fail and throw an exception
- sAsyncChannel = null;
+ mAsyncChannel = null;
getLooper().quit();
return;
}