summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/am/src/com/android/commands/am/Am.java26
-rw-r--r--cmds/svc/src/com/android/commands/svc/NfcCommand.java85
-rw-r--r--cmds/svc/src/com/android/commands/svc/Svc.java3
-rw-r--r--core/java/android/app/Activity.java18
-rw-r--r--core/java/android/app/ActivityManager.java16
-rw-r--r--core/java/android/app/ActivityManagerNative.java31
-rw-r--r--core/java/android/app/ActivityThread.java57
-rw-r--r--core/java/android/app/ApplicationThreadNative.java35
-rw-r--r--core/java/android/app/IActivityManager.java5
-rw-r--r--core/java/android/app/IApplicationThread.java3
-rw-r--r--core/java/android/app/Instrumentation.java2
-rw-r--r--core/java/android/content/Intent.java38
-rw-r--r--core/java/android/content/pm/ActivityInfo.java9
-rw-r--r--core/java/android/content/pm/PackageParser.java11
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java5
-rw-r--r--core/java/android/os/UserHandle.java35
-rw-r--r--core/java/android/provider/Settings.java24
-rw-r--r--core/java/android/view/Window.java26
-rw-r--r--core/java/android/webkit/WebView.java3
-rw-r--r--core/java/android/widget/AdapterView.java1
-rw-r--r--core/java/android/widget/DropDownListView.java5
-rw-r--r--core/java/android/widget/MenuItemHoverListener.java13
-rw-r--r--core/java/android/widget/MenuPopupWindow.java70
-rw-r--r--core/java/android/widget/PopupMenu.java21
-rw-r--r--core/java/android/widget/TimePickerClockDelegate.java8
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodUtils.java18
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java318
-rw-r--r--core/java/com/android/internal/view/menu/CascadingMenuPopup.java163
-rw-r--r--core/java/com/android/internal/view/menu/MenuAdapter.java4
-rw-r--r--core/java/com/android/internal/view/menu/MenuPopupHelper.java55
-rw-r--r--core/java/com/android/internal/view/menu/StandardMenuPopup.java89
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java14
-rw-r--r--core/jni/android/graphics/SurfaceTexture.cpp7
-rw-r--r--core/res/res/layout-land/time_picker_material.xml25
-rw-r--r--core/res/res/layout/time_picker_header_material.xml6
-rw-r--r--core/tests/BTtraffic/Android.mk16
-rw-r--r--core/tests/BTtraffic/AndroidManifest.xml22
-rw-r--r--core/tests/BTtraffic/README45
-rw-r--r--core/tests/BTtraffic/res/values/strings.xml3
-rw-r--r--core/tests/BTtraffic/src/com/android/google/experimental/bttraffic/BTtraffic.java328
-rw-r--r--core/tests/SvcMonitor/Android.mk16
-rw-r--r--core/tests/SvcMonitor/AndroidManifest.xml21
-rw-r--r--core/tests/SvcMonitor/README27
-rw-r--r--core/tests/SvcMonitor/res/values/strings.xml3
-rw-r--r--core/tests/SvcMonitor/src/com/android/google/experimental/svcmoniter/SvcMonitor.java209
-rw-r--r--core/tests/coretests/assets/fonts/HintedAdvanceWidthTest-Regular.ttfbin0 -> 1136 bytes
-rw-r--r--core/tests/coretests/assets/fonts/HintedAdvanceWidthTest-Regular.ttx328
-rw-r--r--core/tests/coretests/src/android/graphics/PaintTest.java97
-rw-r--r--core/tests/coretests/src/android/text/method/WordIteratorTest.java458
-rw-r--r--core/tests/coretests/src/android/widget/TextViewActivityTest.java16
-rw-r--r--core/tests/coretests/src/android/widget/TextViewWordLimitsTest.java288
-rw-r--r--core/tests/coretests/src/android/widget/espresso/TextViewActions.java105
-rw-r--r--core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java116
-rw-r--r--docs/html/guide/topics/renderscript/reference/rs_for_each.jd2
-rw-r--r--docs/html/guide/topics/renderscript/reference/rs_graphics.jd46
-rw-r--r--docs/html/guide/topics/renderscript/reference/rs_object_types.jd20
-rw-r--r--docs/html/guide/topics/renderscript/reference/rs_time.jd4
-rw-r--r--docs/html/guide/topics/renderscript/reference/rs_value_types.jd24
-rw-r--r--media/jni/soundpool/SoundPool.cpp3
-rw-r--r--packages/DocumentsUI/res/color/item_doc_grid_overlay.xml3
-rw-r--r--packages/DocumentsUI/res/values/attrs.xml20
-rw-r--r--packages/DocumentsUI/res/values/colors.xml10
-rw-r--r--packages/DocumentsUI/res/values/styles.xml17
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java10
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java4
-rw-r--r--packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java19
-rw-r--r--packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java62
-rw-r--r--packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java94
-rw-r--r--packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java2
-rw-r--r--packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java101
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java15
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java30
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java7
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java2
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java31
-rw-r--r--services/core/java/com/android/server/DeviceIdleController.java2
-rw-r--r--services/core/java/com/android/server/GestureLauncherService.java13
-rw-r--r--services/core/java/com/android/server/InputMethodManagerService.java22
-rw-r--r--services/core/java/com/android/server/LockSettingsService.java3
-rw-r--r--services/core/java/com/android/server/LockSettingsStrongAuth.java2
-rw-r--r--services/core/java/com/android/server/MmsServiceBroker.java2
-rw-r--r--services/core/java/com/android/server/TextServicesManagerService.java2
-rw-r--r--services/core/java/com/android/server/VibratorService.java4
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java37
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java25
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java35
-rw-r--r--services/core/java/com/android/server/am/CompatModePackages.java2
-rw-r--r--services/core/java/com/android/server/am/ProviderMap.java2
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java2
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkDiagnostics.java56
-rw-r--r--services/core/java/com/android/server/fingerprint/FingerprintService.java25
-rw-r--r--services/core/java/com/android/server/firewall/SenderPackageFilter.java4
-rw-r--r--services/core/java/com/android/server/location/GpsLocationProvider.java350
-rw-r--r--services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java16
-rw-r--r--services/core/java/com/android/server/policy/BarController.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java28
-rw-r--r--services/core/java/com/android/server/wm/Task.java4
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioner.java152
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java8
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java72
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java42
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java75
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfacePlacer.java7
-rw-r--r--services/java/com/android/server/SystemServer.java2
-rw-r--r--services/usage/java/com/android/server/usage/UnixCalendar.java50
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsDatabase.java17
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java74
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java9
-rw-r--r--tools/aapt2/Logger.h7
-rw-r--r--tools/aapt2/StringPiece.h5
-rw-r--r--tools/aapt2/Util.cpp93
-rw-r--r--tools/aapt2/Util.h1
-rw-r--r--tools/aapt2/Util_test.cpp7
122 files changed, 3966 insertions, 1278 deletions
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 9709299fd16a..9185d7ac127a 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -2266,12 +2266,12 @@ public class Am extends BaseCommand {
System.err.println("Error: invalid input bounds");
return;
}
- taskResize(taskId, bounds, 0);
+ taskResize(taskId, bounds, 0, false);
}
- private void taskResize(int taskId, Rect bounds, int delay_ms) {
+ private void taskResize(int taskId, Rect bounds, int delay_ms, boolean pretendUserResize) {
try {
- mAm.resizeTask(taskId, bounds);
+ mAm.resizeTask(taskId, bounds, pretendUserResize);
Thread.sleep(delay_ms);
} catch (RemoteException e) {
System.err.println("Error changing task bounds: " + e);
@@ -2354,7 +2354,7 @@ public class Am extends BaseCommand {
taskRect.top += maxMove;
taskRect.bottom += maxMove;
}
- taskResize(taskId, taskRect, delay_ms);
+ taskResize(taskId, taskRect, delay_ms, false);
}
} else {
while (maxToTravel < 0
@@ -2371,7 +2371,7 @@ public class Am extends BaseCommand {
taskRect.top -= maxMove;
taskRect.bottom -= maxMove;
}
- taskResize(taskId, taskRect, delay_ms);
+ taskResize(taskId, taskRect, delay_ms, false);
}
}
// Return the remaining distance we didn't travel because we reached the target location.
@@ -2405,7 +2405,7 @@ public class Am extends BaseCommand {
currentTaskBounds.left -= getStepSize(
currentTaskBounds.left, stackBounds.left, stepSize, GREATER_THAN_TARGET);
- taskResize(taskId, currentTaskBounds, delay_ms);
+ taskResize(taskId, currentTaskBounds, delay_ms, true);
} while (stackBounds.top < currentTaskBounds.top
|| stackBounds.left < currentTaskBounds.left);
@@ -2418,7 +2418,7 @@ public class Am extends BaseCommand {
currentTaskBounds.left += getStepSize(
currentTaskBounds.left, initialTaskBounds.left, stepSize, !GREATER_THAN_TARGET);
- taskResize(taskId, currentTaskBounds, delay_ms);
+ taskResize(taskId, currentTaskBounds, delay_ms, true);
} while (initialTaskBounds.top > currentTaskBounds.top
|| initialTaskBounds.left > currentTaskBounds.left);
@@ -2431,7 +2431,7 @@ public class Am extends BaseCommand {
currentTaskBounds.right += getStepSize(
currentTaskBounds.right, stackBounds.right, stepSize, !GREATER_THAN_TARGET);
- taskResize(taskId, currentTaskBounds, delay_ms);
+ taskResize(taskId, currentTaskBounds, delay_ms, true);
} while (stackBounds.top < currentTaskBounds.top
|| stackBounds.right > currentTaskBounds.right);
@@ -2444,7 +2444,7 @@ public class Am extends BaseCommand {
currentTaskBounds.right -= getStepSize(currentTaskBounds.right, initialTaskBounds.right,
stepSize, GREATER_THAN_TARGET);
- taskResize(taskId, currentTaskBounds, delay_ms);
+ taskResize(taskId, currentTaskBounds, delay_ms, true);
} while (initialTaskBounds.top > currentTaskBounds.top
|| initialTaskBounds.right < currentTaskBounds.right);
@@ -2457,7 +2457,7 @@ public class Am extends BaseCommand {
currentTaskBounds.left -= getStepSize(
currentTaskBounds.left, stackBounds.left, stepSize, GREATER_THAN_TARGET);
- taskResize(taskId, currentTaskBounds, delay_ms);
+ taskResize(taskId, currentTaskBounds, delay_ms, true);
} while (stackBounds.bottom > currentTaskBounds.bottom
|| stackBounds.left < currentTaskBounds.left);
@@ -2470,7 +2470,7 @@ public class Am extends BaseCommand {
currentTaskBounds.left += getStepSize(
currentTaskBounds.left, initialTaskBounds.left, stepSize, !GREATER_THAN_TARGET);
- taskResize(taskId, currentTaskBounds, delay_ms);
+ taskResize(taskId, currentTaskBounds, delay_ms, true);
} while (initialTaskBounds.bottom < currentTaskBounds.bottom
|| initialTaskBounds.left > currentTaskBounds.left);
@@ -2483,7 +2483,7 @@ public class Am extends BaseCommand {
currentTaskBounds.right += getStepSize(
currentTaskBounds.right, stackBounds.right, stepSize, !GREATER_THAN_TARGET);
- taskResize(taskId, currentTaskBounds, delay_ms);
+ taskResize(taskId, currentTaskBounds, delay_ms, true);
} while (stackBounds.bottom > currentTaskBounds.bottom
|| stackBounds.right > currentTaskBounds.right);
@@ -2496,7 +2496,7 @@ public class Am extends BaseCommand {
currentTaskBounds.right -= getStepSize(currentTaskBounds.right, initialTaskBounds.right,
stepSize, GREATER_THAN_TARGET);
- taskResize(taskId, currentTaskBounds, delay_ms);
+ taskResize(taskId, currentTaskBounds, delay_ms, true);
} while (initialTaskBounds.bottom < currentTaskBounds.bottom
|| initialTaskBounds.right < currentTaskBounds.right);
}
diff --git a/cmds/svc/src/com/android/commands/svc/NfcCommand.java b/cmds/svc/src/com/android/commands/svc/NfcCommand.java
new file mode 100644
index 000000000000..e0f09ee2c666
--- /dev/null
+++ b/cmds/svc/src/com/android/commands/svc/NfcCommand.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015 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.commands.svc;
+
+import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.nfc.INfcAdapter;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+public class NfcCommand extends Svc.Command {
+
+ public NfcCommand() {
+ super("nfc");
+ }
+
+ @Override
+ public String shortHelp() {
+ return "Control NFC functions";
+ }
+
+ @Override
+ public String longHelp() {
+ return shortHelp() + "\n"
+ + "\n"
+ + "usage: svc nfc [enable|disable]\n"
+ + " Turn NFC on or off.\n\n";
+ }
+
+ @Override
+ public void run(String[] args) {
+ boolean validCommand = false;
+ if (args.length >= 2) {
+ boolean flag = false;
+ if ("enable".equals(args[1])) {
+ flag = true;
+ validCommand = true;
+ } else if ("disable".equals(args[1])) {
+ flag = false;
+ validCommand = true;
+ }
+ if (validCommand) {
+ IPackageManager pm = IPackageManager.Stub.asInterface(
+ ServiceManager.getService("package"));
+ try {
+ if (pm.hasSystemFeature(PackageManager.FEATURE_NFC)) {
+ INfcAdapter nfc = INfcAdapter.Stub
+ .asInterface(ServiceManager.getService(Context.NFC_SERVICE));
+ try {
+ if (flag) {
+ nfc.enable();
+ } else
+ nfc.disable(true);
+ } catch (RemoteException e) {
+ System.err.println("NFC operation failed: " + e);
+ }
+ } else {
+ System.err.println("NFC feature not supported.");
+ }
+ } catch (RemoteException e) {
+ System.err.println("RemoteException while calling PackageManager, is the "
+ + "system running?");
+ }
+ return;
+ }
+ }
+ System.err.println(longHelp());
+ }
+
+}
diff --git a/cmds/svc/src/com/android/commands/svc/Svc.java b/cmds/svc/src/com/android/commands/svc/Svc.java
index 0fbba11e927d..2cccd1a4dc92 100644
--- a/cmds/svc/src/com/android/commands/svc/Svc.java
+++ b/cmds/svc/src/com/android/commands/svc/Svc.java
@@ -95,6 +95,7 @@ public class Svc {
new PowerCommand(),
new DataCommand(),
new WifiCommand(),
- new UsbCommand()
+ new UsbCommand(),
+ new NfcCommand(),
};
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f7dcf02da0a5..4997dc751082 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4879,7 +4879,8 @@ public class Activity extends ContextThemeWrapper
if (Looper.myLooper() != mMainThread.getLooper()) {
throw new IllegalStateException("Must be called from main thread");
}
- mMainThread.requestRelaunchActivity(mToken, null, null, 0, false, null, null, false);
+ mMainThread.requestRelaunchActivity(mToken, null, null, 0, false, null, null, false,
+ false /* preserveWindow */);
}
/**
@@ -6223,12 +6224,13 @@ public class Activity extends ContextThemeWrapper
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
- Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
+ Configuration config, String referrer, IVoiceInteractor voiceInteractor,
+ Window window) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
- mWindow = new PhoneWindow(this);
+ mWindow = new PhoneWindow(this, window);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
@@ -6317,11 +6319,15 @@ public class Activity extends ContextThemeWrapper
final void performRestart() {
mFragments.noteStateNotSaved();
+ if (mToken != null && mParent == null) {
+ // We might have view roots that were preserved during a relaunch, we need to start them
+ // again. We don't need to check mStopped, the roots will check if they were actually
+ // stopped.
+ WindowManagerGlobal.getInstance().setStoppedState(mToken, false /* stopped */);
+ }
+
if (mStopped) {
mStopped = false;
- if (mToken != null && mParent == null) {
- WindowManagerGlobal.getInstance().setStoppedState(mToken, false);
- }
synchronized (mManagedCursors) {
final int N = mManagedCursors.size();
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index eeae20fa1ccf..9ef51c8554d3 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -452,6 +452,22 @@ public class ActivityManager {
*/
public static final int FIRST_DYNAMIC_STACK_ID = LAST_STATIC_STACK_ID + 1;
+ /**
+ * Input parameter to {@link android.app.IActivityManager#moveTaskToDockedStack} which
+ * specifies the position of the created docked stack at the top half of the screen if
+ * in portrait mode or at the left half of the screen if in landscape mode.
+ * @hide
+ */
+ public static final int DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT = 0;
+
+ /**
+ * Input parameter to {@link android.app.IActivityManager#moveTaskToDockedStack} which
+ * specifies the position of the created docked stack at the bottom half of the screen if
+ * in portrait mode or at the right half of the screen if in landscape mode.
+ * @hide
+ */
+ public static final int DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT = 1;
+
/** @hide */
public int getFrontActivityScreenCompatMode() {
try {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index d1c73bc945ff..da6fc592e69a 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -743,6 +743,16 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case MOVE_TASK_TO_DOCKED_STACK_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int taskId = data.readInt();
+ int createMode = data.readInt();
+ boolean toTop = data.readInt() != 0;
+ moveTaskToDockedStack(taskId, createMode, toTop);
+ reply.writeNoException();
+ return true;
+ }
+
case RESIZE_STACK_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int stackId = data.readInt();
@@ -2442,8 +2452,9 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
case RESIZE_TASK_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
int taskId = data.readInt();
+ final boolean resizedByUser = data.readInt() == 1;
Rect r = Rect.CREATOR.createFromParcel(data);
- resizeTask(taskId, r);
+ resizeTask(taskId, r, resizedByUser);
reply.writeNoException();
return true;
}
@@ -3510,6 +3521,21 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
}
@Override
+ public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop)
+ throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(taskId);
+ data.writeInt(createMode);
+ data.writeInt(toTop ? 1 : 0);
+ mRemote.transact(MOVE_TASK_TO_DOCKED_STACK_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+ @Override
public void resizeStack(int stackId, Rect r) throws RemoteException
{
Parcel data = Parcel.obtain();
@@ -5874,12 +5900,13 @@ class ActivityManagerProxy implements IActivityManager
}
@Override
- public void resizeTask(int taskId, Rect r) throws RemoteException
+ public void resizeTask(int taskId, Rect r, boolean resizedByUser) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(taskId);
+ data.writeInt(resizedByUser ? 1 : 0);
r.writeToParcel(data, 0);
mRemote.transact(RESIZE_TASK_TRANSACTION, data, reply, 0);
reply.readException();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 67dee7f43c32..4b8efab3e59e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -315,8 +315,9 @@ public final class ActivityThread {
int pendingConfigChanges;
boolean onlyLocalRequest;
- View mPendingRemoveWindow;
+ Window mPendingRemoveWindow;
WindowManager mPendingRemoveWindowManager;
+ boolean mPreserveWindow;
ActivityClientRecord() {
parent = null;
@@ -670,9 +671,9 @@ public final class ActivityThread {
public final void scheduleRelaunchActivity(IBinder token,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
int configChanges, boolean notResumed, Configuration config,
- Configuration overrideConfig) {
+ Configuration overrideConfig, boolean preserveWindow) {
requestRelaunchActivity(token, pendingResults, pendingNewIntents,
- configChanges, notResumed, config, overrideConfig, true);
+ configChanges, notResumed, config, overrideConfig, true, preserveWindow);
}
public final void scheduleNewIntent(List<ReferrerIntent> intents, IBinder token) {
@@ -2376,10 +2377,16 @@ public final class ActivityThread {
Configuration config = new Configuration(mCompatConfiguration);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
+ Window window = null;
+ if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
+ window = r.mPendingRemoveWindow;
+ r.mPendingRemoveWindow = null;
+ r.mPendingRemoveWindowManager = null;
+ }
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
- r.referrer, r.voiceInteractor);
+ r.referrer, r.voiceInteractor, window);
if (customIntent != null) {
activity.mIntent = customIntent;
@@ -3191,10 +3198,14 @@ public final class ActivityThread {
return r;
}
- static final void cleanUpPendingRemoveWindows(ActivityClientRecord r) {
+ static final void cleanUpPendingRemoveWindows(ActivityClientRecord r, boolean force) {
+ if (r.mPreserveWindow && !force) {
+ return;
+ }
if (r.mPendingRemoveWindow != null) {
- r.mPendingRemoveWindowManager.removeViewImmediate(r.mPendingRemoveWindow);
- IBinder wtoken = r.mPendingRemoveWindow.getWindowToken();
+ r.mPendingRemoveWindowManager.removeViewImmediate(
+ r.mPendingRemoveWindow.getDecorView());
+ IBinder wtoken = r.mPendingRemoveWindow.getDecorView().getWindowToken();
if (wtoken != null) {
WindowManagerGlobal.getInstance().closeAll(wtoken,
r.activity.getClass().getName(), "Activity");
@@ -3245,7 +3256,11 @@ public final class ActivityThread {
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
- if (a.mVisibleFromClient) {
+ if (r.mPreserveWindow) {
+ a.mWindowAdded = true;
+ r.mPreserveWindow = false;
+ }
+ if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
@@ -3260,7 +3275,7 @@ public final class ActivityThread {
}
// Get rid of anything left hanging around.
- cleanUpPendingRemoveWindows(r);
+ cleanUpPendingRemoveWindows(r, false /* force */);
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
@@ -3745,7 +3760,8 @@ public final class ActivityThread {
// request all activities to relaunch for the changes to take place
for (Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
- requestRelaunchActivity(entry.getKey(), null, null, 0, false, null, null, false);
+ requestRelaunchActivity(entry.getKey(), null, null, 0, false, null, null, false,
+ false /* preserveWindow */);
}
}
}
@@ -3931,7 +3947,7 @@ public final class ActivityThread {
ActivityClientRecord r = performDestroyActivity(token, finishing,
configChanges, getNonConfigInstance);
if (r != null) {
- cleanUpPendingRemoveWindows(r);
+ cleanUpPendingRemoveWindows(r, finishing);
WindowManager wm = r.activity.getWindowManager();
View v = r.activity.mDecor;
if (v != null) {
@@ -3940,11 +3956,18 @@ public final class ActivityThread {
}
IBinder wtoken = v.getWindowToken();
if (r.activity.mWindowAdded) {
- if (r.onlyLocalRequest) {
+ boolean reuseForResize = r.window.hasNonClientDecorView() && r.mPreserveWindow;
+ if (r.onlyLocalRequest || reuseForResize) {
// Hold off on removing this until the new activity's
// window is being added.
- r.mPendingRemoveWindow = v;
+ r.mPendingRemoveWindow = r.window;
r.mPendingRemoveWindowManager = wm;
+ if (reuseForResize) {
+ // We can only keep the part of the view hierarchy that we control,
+ // everything else must be removed, because it might not be able to
+ // behave properly when activity is relaunching.
+ r.window.clearContentView();
+ }
} else {
wm.removeViewImmediate(v);
}
@@ -3986,10 +4009,14 @@ public final class ActivityThread {
mSomeActivitiesChanged = true;
}
+ /**
+ * @param preserveWindow Whether the activity should try to reuse the window it created,
+ * including the decor view after the relaunch.
+ */
public final void requestRelaunchActivity(IBinder token,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
int configChanges, boolean notResumed, Configuration config,
- Configuration overrideConfig, boolean fromServer) {
+ Configuration overrideConfig, boolean fromServer, boolean preserveWindow) {
ActivityClientRecord target = null;
synchronized (mResourcesManager) {
@@ -4020,6 +4047,7 @@ public final class ActivityThread {
target.token = token;
target.pendingResults = pendingResults;
target.pendingIntents = pendingNewIntents;
+ target.mPreserveWindow = preserveWindow;
if (!fromServer) {
ActivityClientRecord existing = mActivities.get(token);
if (existing != null) {
@@ -4120,6 +4148,7 @@ public final class ActivityThread {
r.activity.mConfigChangeFlags |= configChanges;
r.onlyLocalRequest = tmp.onlyLocalRequest;
+ r.mPreserveWindow = tmp.mPreserveWindow;
Intent currentIntent = r.activity.mIntent;
r.activity.mChangingConfigurations = true;
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index f164a0a76d8f..bead625a0bc1 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -63,14 +63,14 @@ public abstract class ApplicationThreadNative extends Binder
if (in != null) {
return in;
}
-
+
return new ApplicationThreadProxy(obj);
}
-
+
public ApplicationThreadNative() {
attachInterface(this, descriptor);
}
-
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -96,7 +96,7 @@ public abstract class ApplicationThreadNative extends Binder
scheduleStopActivity(b, show, configChanges);
return true;
}
-
+
case SCHEDULE_WINDOW_VISIBILITY_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
@@ -125,7 +125,7 @@ public abstract class ApplicationThreadNative extends Binder
scheduleResumeActivity(b, procState, isForward, resumeArgs);
return true;
}
-
+
case SCHEDULE_SEND_RESULT_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
@@ -179,7 +179,9 @@ public abstract class ApplicationThreadNative extends Binder
if (data.readInt() != 0) {
overrideConfig = Configuration.CREATOR.createFromParcel(data);
}
- scheduleRelaunchActivity(b, ri, pi, configChanges, notResumed, config, overrideConfig);
+ boolean preserveWindows = data.readInt() == 1;
+ scheduleRelaunchActivity(b, ri, pi, configChanges, notResumed, config, overrideConfig,
+ preserveWindows);
return true;
}
@@ -201,7 +203,7 @@ public abstract class ApplicationThreadNative extends Binder
scheduleDestroyActivity(b, finishing, configChanges);
return true;
}
-
+
case SCHEDULE_RECEIVER_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
@@ -371,7 +373,7 @@ public abstract class ApplicationThreadNative extends Binder
}
return true;
}
-
+
case DUMP_PROVIDER_TRANSACTION: {
data.enforceInterface(IApplicationThread.descriptor);
ParcelFileDescriptor fd = data.readFileDescriptor();
@@ -731,15 +733,15 @@ public abstract class ApplicationThreadNative extends Binder
class ApplicationThreadProxy implements IApplicationThread {
private final IBinder mRemote;
-
+
public ApplicationThreadProxy(IBinder remote) {
mRemote = remote;
}
-
+
public final IBinder asBinder() {
return mRemote;
}
-
+
public final void schedulePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges, boolean dontReport) throws RemoteException {
Parcel data = Parcel.obtain();
@@ -856,7 +858,7 @@ class ApplicationThreadProxy implements IApplicationThread {
public final void scheduleRelaunchActivity(IBinder token,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
int configChanges, boolean notResumed, Configuration config,
- Configuration overrideConfig) throws RemoteException {
+ Configuration overrideConfig, boolean preserveWindow) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(token);
@@ -871,6 +873,7 @@ class ApplicationThreadProxy implements IApplicationThread {
} else {
data.writeInt(0);
}
+ data.writeInt(preserveWindow ? 1 : 0);
mRemote.transact(SCHEDULE_RELAUNCH_ACTIVITY_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
@@ -898,7 +901,7 @@ class ApplicationThreadProxy implements IApplicationThread {
IBinder.FLAG_ONEWAY);
data.recycle();
}
-
+
public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String resultData,
Bundle map, boolean sync, int sendingUser, int processState) throws RemoteException {
@@ -940,7 +943,7 @@ class ApplicationThreadProxy implements IApplicationThread {
IBinder.FLAG_ONEWAY);
data.recycle();
}
-
+
public final void scheduleCreateService(IBinder token, ServiceInfo info,
CompatibilityInfo compatInfo, int processState) throws RemoteException {
Parcel data = Parcel.obtain();
@@ -1055,7 +1058,7 @@ class ApplicationThreadProxy implements IApplicationThread {
IBinder.FLAG_ONEWAY);
data.recycle();
}
-
+
public final void scheduleExit() throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
@@ -1128,7 +1131,7 @@ class ApplicationThreadProxy implements IApplicationThread {
mRemote.transact(DUMP_SERVICE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
data.recycle();
}
-
+
public void dumpProvider(FileDescriptor fd, IBinder token, String[] args)
throws RemoteException {
Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 66fa79639256..7bd832bf5f95 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -140,6 +140,8 @@ public interface IActivityManager extends IInterface {
public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) throws RemoteException;
public void moveTaskBackwards(int task) throws RemoteException;
public void moveTaskToStack(int taskId, int stackId, boolean toTop) throws RemoteException;
+ public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop)
+ throws RemoteException;
public void resizeStack(int stackId, Rect bounds) throws RemoteException;
public void positionTaskInStack(int taskId, int stackId, int position) throws RemoteException;
public List<StackInfo> getAllStackInfos() throws RemoteException;
@@ -489,7 +491,7 @@ public interface IActivityManager extends IInterface {
public void setTaskDescription(IBinder token, ActivityManager.TaskDescription values)
throws RemoteException;
public void setTaskResizeable(int taskId, boolean resizeable) throws RemoteException;
- public void resizeTask(int taskId, Rect bounds) throws RemoteException;
+ public void resizeTask(int taskId, Rect bounds, boolean resizedByUser) throws RemoteException;
public Rect getTaskBounds(int taskId) throws RemoteException;
public Bitmap getTaskDescriptionIcon(String filename) throws RemoteException;
@@ -888,4 +890,5 @@ public interface IActivityManager extends IInterface {
int GET_ACTIVITY_STACK_ID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 343;
int MOVE_ACTIVITY_TO_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 344;
int REPORT_SIZE_CONFIGURATIONS = IBinder.FIRST_CALL_TRANSACTION + 345;
+ int MOVE_TASK_TO_DOCKED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 346;
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index dc8f53d94b4e..2d78e19750ae 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -65,7 +65,8 @@ public interface IApplicationThread extends IInterface {
boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException;
void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults,
List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed,
- Configuration config, Configuration overrideConfig) throws RemoteException;
+ Configuration config, Configuration overrideConfig, boolean preserveWindow)
+ throws RemoteException;
void scheduleNewIntent(List<ReferrerIntent> intent, IBinder token) throws RemoteException;
void scheduleDestroyActivity(IBinder token, boolean finished,
int configChanges) throws RemoteException;
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index dee8d2115863..718433719d0d 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1044,7 +1044,7 @@ public class Instrumentation {
activity.attach(context, aThread, this, token, 0, application, intent,
info, title, parent, id,
(Activity.NonConfigurationInstances)lastNonConfigurationInstance,
- new Configuration(), null, null);
+ new Configuration(), null, null, null);
return activity;
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e6590493616b..b08db20c2eea 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1613,6 +1613,23 @@ public class Intent implements Parcelable, Cloneable {
= "android.intent.action.GET_PERMISSIONS_COUNT";
/**
+ * Broadcast action that requests list of all apps that have runtime permissions. It will
+ * respond to the request by sending a broadcast with action defined by
+ * {@link #EXTRA_GET_PERMISSIONS_PACKAGES_RESPONSE_INTENT}. The response will contain
+ * {@link #EXTRA_GET_PERMISSIONS_APP_LIST_RESULT}, as well as
+ * {@link #EXTRA_GET_PERMISSIONS_APP_LABEL_LIST_RESULT}, with contents described below or
+ * a null upon failure.
+ *
+ * <p>{@link #EXTRA_GET_PERMISSIONS_APP_LIST_RESULT} will contain a list of package names of
+ * apps that have runtime permissions. {@link #EXTRA_GET_PERMISSIONS_APP_LABEL_LIST_RESULT}
+ * will contain the list of app labels corresponding ot the apps in the first list.
+ *
+ * @hide
+ */
+ public static final String ACTION_GET_PERMISSIONS_PACKAGES
+ = "android.intent.action.GET_PERMISSIONS_PACKAGES";
+
+ /**
* Extra included in response to {@link #ACTION_GET_PERMISSIONS_COUNT}.
* @hide
*/
@@ -1627,6 +1644,20 @@ public class Intent implements Parcelable, Cloneable {
= "android.intent.extra.GET_PERMISSIONS_GROUP_LIST_RESULT";
/**
+ * String list of apps that have one or more runtime permissions.
+ * @hide
+ */
+ public static final String EXTRA_GET_PERMISSIONS_APP_LIST_RESULT
+ = "android.intent.extra.GET_PERMISSIONS_APP_LIST_RESULT";
+
+ /**
+ * String list of app labels for apps that have one or more runtime permissions.
+ * @hide
+ */
+ public static final String EXTRA_GET_PERMISSIONS_APP_LABEL_LIST_RESULT
+ = "android.intent.extra.GET_PERMISSIONS_APP_LABEL_LIST_RESULT";
+
+ /**
* Required extra to be sent with {@link #ACTION_GET_PERMISSIONS_COUNT} broadcasts.
* @hide
*/
@@ -1634,6 +1665,13 @@ public class Intent implements Parcelable, Cloneable {
= "android.intent.extra.GET_PERMISSIONS_RESONSE_INTENT";
/**
+ * Required extra to be sent with {@link #ACTION_GET_PERMISSIONS_PACKAGES} broadcasts.
+ * @hide
+ */
+ public static final String EXTRA_GET_PERMISSIONS_PACKAGES_RESPONSE_INTENT
+ = "android.intent.extra.GET_PERMISSIONS_PACKAGES_RESONSE_INTENT";
+
+ /**
* Activity action: Launch UI to manage which apps have a given permission.
* <p>
* Input: {@link #EXTRA_PERMISSION_NAME} specifies the permission access
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index a59f429bfabc..a121b4d312fd 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -33,12 +33,16 @@ import java.lang.annotation.RetentionPolicy;
*/
public class ActivityInfo extends ComponentInfo
implements Parcelable {
+
+ // NOTE: When adding new data members be sure to update the copy-constructor, Parcel
+ // constructor, and writeToParcel.
+
/**
* A style resource identifier (in the package's resources) of this
* activity's theme. From the "theme" attribute or, if not set, 0.
*/
public int theme;
-
+
/**
* Constant corresponding to <code>standard</code> in
* the {@link android.R.attr#launchMode} attribute.
@@ -707,6 +711,7 @@ public class ActivityInfo extends ComponentInfo
super(orig);
theme = orig.theme;
launchMode = orig.launchMode;
+ documentLaunchMode = orig.documentLaunchMode;
permission = orig.permission;
taskAffinity = orig.taskAffinity;
targetActivity = orig.targetActivity;
@@ -788,6 +793,7 @@ public class ActivityInfo extends ComponentInfo
super.writeToParcel(dest, parcelableFlags);
dest.writeInt(theme);
dest.writeInt(launchMode);
+ dest.writeInt(documentLaunchMode);
dest.writeString(permission);
dest.writeString(taskAffinity);
dest.writeString(targetActivity);
@@ -827,6 +833,7 @@ public class ActivityInfo extends ComponentInfo
super(source);
theme = source.readInt();
launchMode = source.readInt();
+ documentLaunchMode = source.readInt();
permission = source.readString();
taskAffinity = source.readString();
targetActivity = source.readString();
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 968f9b27d629..d7ecbfeb80eb 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -4561,6 +4561,17 @@ public class PackageParser {
return applicationInfo.isUpdatedSystemApp();
}
+ /**
+ * @hide
+ */
+ public boolean canHaveOatDir() {
+ // The following app types CANNOT have oat directory
+ // - non-updated system apps
+ // - forward-locked apps or apps installed in ASEC containers
+ return (!isSystemApp() || isUpdatedSystemApp())
+ && !isForwardLocked() && !applicationInfo.isExternalAsec();
+ }
+
public String toString() {
return "Package{"
+ Integer.toHexString(System.identityHashCode(this))
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 7fef5e17c5cb..04caa8fcda38 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -772,10 +772,10 @@ public class FingerprintManager {
if (mRemovalCallback != null) {
int reqFingerId = mRemovalFingerprint.getFingerId();
int reqGroupId = mRemovalFingerprint.getGroupId();
- if (fingerId != reqFingerId) {
+ if (reqFingerId != 0 && fingerId != reqFingerId) {
Log.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId);
}
- if (fingerId != reqFingerId) {
+ if (groupId != reqGroupId) {
Log.w(TAG, "Group id didn't match: " + groupId + " != " + reqGroupId);
}
mRemovalCallback.onRemovalSucceeded(mRemovalFingerprint);
@@ -962,4 +962,3 @@ public class FingerprintManager {
};
}
-
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 48ede4f01585..213e0831c0f2 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -57,15 +57,15 @@ public final class UserHandle implements Parcelable {
/**
* @hide A user id constant to indicate the "owner" user of the device
- * @deprecated Consider using either USER_SYSTEM constant or
- * UserInfo.isPrimary().
+ * @deprecated Consider using either {@link UserHandle#USER_SYSTEM} constant or
+ * check the target user's flag {@link android.content.pm.UserInfo#isAdmin}.
*/
public static final int USER_OWNER = 0;
/**
* @hide A user handle to indicate the primary/owner user of the device
- * @deprecated Consider using either SYSTEM constant or
- * UserInfo.isPrimary().
+ * @deprecated Consider using either {@link UserHandle#SYSTEM} constant or
+ * check the target user's flag {@link android.content.pm.UserInfo#isAdmin}.
*/
public static final UserHandle OWNER = new UserHandle(USER_OWNER);
@@ -90,7 +90,7 @@ public final class UserHandle implements Parcelable {
* user.
* @hide
*/
- public static final boolean isSameUser(int uid1, int uid2) {
+ public static boolean isSameUser(int uid1, int uid2) {
return getUserId(uid1) == getUserId(uid2);
}
@@ -102,12 +102,12 @@ public final class UserHandle implements Parcelable {
* @return whether the appId is the same for both uids
* @hide
*/
- public static final boolean isSameApp(int uid1, int uid2) {
+ public static boolean isSameApp(int uid1, int uid2) {
return getAppId(uid1) == getAppId(uid2);
}
/** @hide */
- public static final boolean isIsolated(int uid) {
+ public static boolean isIsolated(int uid) {
if (uid > 0) {
final int appId = getAppId(uid);
return appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID;
@@ -130,7 +130,7 @@ public final class UserHandle implements Parcelable {
* Returns the user id for a given uid.
* @hide
*/
- public static final int getUserId(int uid) {
+ public static int getUserId(int uid) {
if (MU_ENABLED) {
return uid / PER_USER_RANGE;
} else {
@@ -139,12 +139,12 @@ public final class UserHandle implements Parcelable {
}
/** @hide */
- public static final int getCallingUserId() {
+ public static int getCallingUserId() {
return getUserId(Binder.getCallingUid());
}
/** @hide */
- public static final UserHandle getCallingUserHandle() {
+ public static UserHandle getCallingUserHandle() {
int userId = getUserId(Binder.getCallingUid());
UserHandle userHandle = userHandles.get(userId);
// Intentionally not synchronized to save time
@@ -159,7 +159,7 @@ public final class UserHandle implements Parcelable {
* Returns the uid that is composed from the userId and the appId.
* @hide
*/
- public static final int getUid(int userId, int appId) {
+ public static int getUid(int userId, int appId) {
if (MU_ENABLED) {
return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
} else {
@@ -171,7 +171,7 @@ public final class UserHandle implements Parcelable {
* Returns the app id (or base uid) for a given uid, stripping out the user id from it.
* @hide
*/
- public static final int getAppId(int uid) {
+ public static int getAppId(int uid) {
return uid % PER_USER_RANGE;
}
@@ -179,7 +179,7 @@ public final class UserHandle implements Parcelable {
* Returns the gid shared between all apps with this userId.
* @hide
*/
- public static final int getUserGid(int userId) {
+ public static int getUserGid(int userId) {
return getUid(userId, Process.SHARED_USER_GID);
}
@@ -187,7 +187,7 @@ public final class UserHandle implements Parcelable {
* Returns the shared app gid for a given uid or appId.
* @hide
*/
- public static final int getSharedAppGid(int id) {
+ public static int getSharedAppGid(int id) {
return Process.FIRST_SHARED_APPLICATION_GID + (id % PER_USER_RANGE)
- Process.FIRST_APPLICATION_UID;
}
@@ -196,7 +196,7 @@ public final class UserHandle implements Parcelable {
* Returns the app id for a given shared app gid. Returns -1 if the ID is invalid.
* @hide
*/
- public static final int getAppIdFromSharedAppGid(int gid) {
+ public static int getAppIdFromSharedAppGid(int gid) {
final int appId = getAppId(gid) + Process.FIRST_APPLICATION_UID
- Process.FIRST_SHARED_APPLICATION_GID;
if (appId < 0 || appId >= Process.FIRST_SHARED_APPLICATION_GID) {
@@ -272,7 +272,7 @@ public final class UserHandle implements Parcelable {
* @hide
*/
@SystemApi
- public static final int myUserId() {
+ public static int myUserId() {
return getUserId(Process.myUid());
}
@@ -280,9 +280,10 @@ public final class UserHandle implements Parcelable {
* Returns true if this UserHandle refers to the owner user; false otherwise.
* @return true if this UserHandle refers to the owner user; false otherwise.
* @hide
+ * TODO: find an alternative to this Api.
*/
@SystemApi
- public final boolean isOwner() {
+ public boolean isOwner() {
return this.equals(OWNER);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a038b0d30887..225f0cf97c2e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3283,7 +3283,6 @@ public final class Settings {
DOCK_SOUNDS_ENABLED, // moved to global
LOCKSCREEN_SOUNDS_ENABLED,
SHOW_WEB_SUGGESTIONS,
- NOTIFICATION_LIGHT_PULSE,
SIP_CALL_OPTIONS,
SIP_RECEIVE_CALLS,
POINTER_SPEED,
@@ -4921,7 +4920,26 @@ public final class Settings {
"accessibility_display_daltonizer";
/**
- * The timout for considering a press to be a long press in milliseconds.
+ * Setting that specifies whether automatic click when the mouse pointer stops moving is
+ * enabled.
+ *
+ * @hide
+ */
+ public static final String ACCESSIBILITY_AUTOCLICK_ENABLED =
+ "accessibility_autoclick_enabled";
+
+ /**
+ * Integer setting specifying amount of time in ms the mouse pointer has to stay still
+ * before performing click when {@link #ACCESSIBILITY_AUTOCLICK_ENABLED} is set.
+ *
+ * @see #ACCESSIBILITY_AUTOCLICK_ENABLED
+ * @hide
+ */
+ public static final String ACCESSIBILITY_AUTOCLICK_DELAY =
+ "accessibility_autoclick_delay";
+
+ /**
+ * The timeout for considering a press to be a long press in milliseconds.
* @hide
*/
public static final String LONG_PRESS_TIMEOUT = "long_press_timeout";
@@ -5785,6 +5803,8 @@ public final class Settings {
SLEEP_TIMEOUT,
DOUBLE_TAP_TO_WAKE,
CAMERA_GESTURE_DISABLED,
+ ACCESSIBILITY_AUTOCLICK_ENABLED,
+ ACCESSIBILITY_AUTOCLICK_DELAY
};
/**
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index b146a51292e1..0e7089ff7a8c 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1175,6 +1175,13 @@ public abstract class Window {
public abstract void addContentView(View view, ViewGroup.LayoutParams params);
/**
+ * Remove the view that was used as the screen content.
+ *
+ * @hide
+ */
+ public abstract void clearContentView();
+
+ /**
* Return the view in this Window that currently has focus, or null if
* there are none. Note that this does not look in any containing
* Window.
@@ -1239,6 +1246,15 @@ public abstract class Window {
public void setElevation(float elevation) {}
/**
+ * Gets the window elevation.
+ *
+ * @hide
+ */
+ public float getElevation() {
+ return 0.0f;
+ }
+
+ /**
* Sets whether window content should be clipped to the outline of the
* window background.
*
@@ -1991,5 +2007,13 @@ public abstract class Window {
*/
public abstract void setNavigationBarColor(@ColorInt int color);
-
+ /**
+ * Get information whether the activity has non client decoration view. These views are used in
+ * the multi window environment, to provide dragging handle and maximize/close buttons.
+ *
+ * @hide
+ */
+ public boolean hasNonClientDecorView() {
+ return false;
+ }
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 057b7010999a..d0c50c9309c2 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -962,6 +962,9 @@ public class WebView extends AbsoluteLayout
* If the base URL uses any other scheme, then the data will be loaded into
* the WebView as a plain string (i.e. not part of a data URL) and any URL-encoded
* entities in the string will not be decoded.
+ * <p>
+ * Note that the baseUrl is sent in the 'Referer' HTTP header when
+ * requesting subresources (images, etc.) of the page loaded using this method.
*
* @param baseUrl the URL to use as the page's base URL. If null defaults to
* 'about:blank'.
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 2cfefba10c57..6ed7ab8fde68 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -815,6 +815,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup {
@Override
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
dispatchThawSelfOnly(container);
+ handleDataChanged();
}
class AdapterDataSetObserver extends DataSetObserver {
diff --git a/core/java/android/widget/DropDownListView.java b/core/java/android/widget/DropDownListView.java
index bcbafc9af158..c869ccbdef58 100644
--- a/core/java/android/widget/DropDownListView.java
+++ b/core/java/android/widget/DropDownListView.java
@@ -132,11 +132,6 @@ public class DropDownListView extends ListView {
return selectedView != null && selectedView.isEnabled() || super.shouldShowSelector();
}
- protected void clearSelection() {
- setSelectedPositionInt(-1);
- setNextSelectedPositionInt(-1);
- }
-
@Override
public boolean onHoverEvent(MotionEvent ev) {
final int action = ev.getActionMasked();
diff --git a/core/java/android/widget/MenuItemHoverListener.java b/core/java/android/widget/MenuItemHoverListener.java
new file mode 100644
index 000000000000..87c5c852973e
--- /dev/null
+++ b/core/java/android/widget/MenuItemHoverListener.java
@@ -0,0 +1,13 @@
+package android.widget;
+
+import com.android.internal.view.menu.MenuBuilder;
+
+/**
+ * An interface notified when a menu item is hovered. Useful for cases when hover should trigger
+ * some behavior at a higher level, like managing the opening and closing of submenus.
+ *
+ * @hide
+ */
+public interface MenuItemHoverListener {
+ public void onItemHovered(MenuBuilder menu, int position);
+}
diff --git a/core/java/android/widget/MenuPopupWindow.java b/core/java/android/widget/MenuPopupWindow.java
index 900aa326d502..ba77b1b53079 100644
--- a/core/java/android/widget/MenuPopupWindow.java
+++ b/core/java/android/widget/MenuPopupWindow.java
@@ -22,12 +22,12 @@ import android.content.res.Resources;
import android.transition.Transition;
import android.util.AttributeSet;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
import com.android.internal.view.menu.ListMenuItemView;
import com.android.internal.view.menu.MenuAdapter;
+import com.android.internal.view.menu.MenuBuilder;
/**
* A MenuPopupWindow represents the popup window for menu.
@@ -37,20 +37,32 @@ import com.android.internal.view.menu.MenuAdapter;
*
* @hide
*/
-public class MenuPopupWindow extends ListPopupWindow {
+public class MenuPopupWindow extends ListPopupWindow implements MenuItemHoverListener {
+ private MenuItemHoverListener mHoverListener;
+
public MenuPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
DropDownListView createDropDownListView(Context context, boolean hijackFocus) {
- return new MenuDropDownListView(context, hijackFocus);
+ MenuDropDownListView view = new MenuDropDownListView(context, hijackFocus);
+ view.setHoverListener(this);
+ return view;
}
public void setEnterTransition(Transition enterTransition) {
mPopup.setEnterTransition(enterTransition);
}
+ public void setExitTransition(Transition exitTransition) {
+ mPopup.setExitTransition(exitTransition);
+ }
+
+ public void setHoverListener(MenuItemHoverListener hoverListener) {
+ mHoverListener = hoverListener;
+ }
+
/**
* Set whether this window is touch modal or if outside touches will be sent to
* other windows behind it.
@@ -59,10 +71,23 @@ public class MenuPopupWindow extends ListPopupWindow {
mPopup.setTouchModal(touchModal);
}
- private static class MenuDropDownListView extends DropDownListView {
+ @Override
+ public void onItemHovered(MenuBuilder menu, int position) {
+ // Forward up the chain
+ if (mHoverListener != null) {
+ mHoverListener.onItemHovered(menu, position);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static class MenuDropDownListView extends DropDownListView {
final int mAdvanceKey;
final int mRetreatKey;
+ private MenuItemHoverListener mHoverListener;
+
public MenuDropDownListView(Context context, boolean hijackFocus) {
super(context, hijackFocus);
@@ -77,6 +102,15 @@ public class MenuPopupWindow extends ListPopupWindow {
}
}
+ public void setHoverListener(MenuItemHoverListener hoverListener) {
+ mHoverListener = hoverListener;
+ }
+
+ public void clearSelection() {
+ setSelectedPositionInt(INVALID_POSITION);
+ setNextSelectedPositionInt(INVALID_POSITION);
+ }
+
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
ListMenuItemView selectedItem = (ListMenuItemView) getSelectedView();
@@ -99,6 +133,32 @@ public class MenuPopupWindow extends ListPopupWindow {
return super.onKeyDown(keyCode, event);
}
+ @Override
+ public boolean onHoverEvent(MotionEvent ev) {
+ boolean dispatchHover = false;
+ final int position = pointToPosition((int) ev.getX(), (int) ev.getY());
+
+ final int action = ev.getActionMasked();
+ if (action == MotionEvent.ACTION_HOVER_ENTER
+ || action == MotionEvent.ACTION_HOVER_MOVE) {
+ if (position != INVALID_POSITION && position != mSelectedPosition) {
+ final View hoveredItem = getChildAt(position - getFirstVisiblePosition());
+ if (hoveredItem.isEnabled()) {
+ dispatchHover = true;
+ }
+ }
+ }
+
+ boolean superVal = super.onHoverEvent(ev);
+
+ if (dispatchHover && mHoverListener != null) {
+ mHoverListener.onItemHovered(
+ ((MenuAdapter) getAdapter()).getAdapterMenu(), position);
+ }
+
+ return superVal;
+ }
}
+
} \ No newline at end of file
diff --git a/core/java/android/widget/PopupMenu.java b/core/java/android/widget/PopupMenu.java
index bd1fbb8f29c5..34a843925700 100644
--- a/core/java/android/widget/PopupMenu.java
+++ b/core/java/android/widget/PopupMenu.java
@@ -270,25 +270,8 @@ public class PopupMenu implements MenuBuilder.Callback, MenuPresenter.Callback {
* @hide
*/
public boolean onOpenSubMenu(MenuBuilder subMenu) {
- if (subMenu == null) return false;
-
- if (!subMenu.hasVisibleItems()) {
- return true;
- }
-
- if (!mShowCascadingMenus) {
- // Current menu will be dismissed by the normal helper, submenu will be shown in its
- // place. (If cascading menus are enabled, the cascading implementation will show the
- // submenu itself).
- new MenuPopupHelper(mContext, subMenu, mAnchor).show();
- }
- return true;
- }
-
- /**
- * @hide
- */
- public void onCloseSubMenu(SubMenuBuilder menu) {
+ // The menu presenter will handle opening the submenu itself. Nothing to do here.
+ return false;
}
/**
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 2365b4850161..61ef6dc9d231 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -22,7 +22,6 @@ import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.SpannableStringBuilder;
@@ -32,7 +31,6 @@ import android.text.style.TtsSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.util.StateSet;
-import android.util.TypedValue;
import android.view.HapticFeedbackConstants;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
@@ -66,10 +64,8 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl
// Also NOT a real index, just used for keyboard mode.
private static final int ENABLE_PICKER_INDEX = 3;
- private static final int[] ATTRS_TEXT_COLOR = new int[] {
- com.android.internal.R.attr.textColor};
- private static final int[] ATTRS_DISABLED_ALPHA = new int[] {
- com.android.internal.R.attr.disabledAlpha};
+ private static final int[] ATTRS_TEXT_COLOR = new int[] {R.attr.textColor};
+ private static final int[] ATTRS_DISABLED_ALPHA = new int[] {R.attr.disabledAlpha};
// LayoutLib relies on these constants. Change TimePickerClockDelegate_Delegate if
// modifying these.
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index a4ef00afdf02..01ac22e3c4fd 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -793,6 +793,24 @@ public class InputMethodUtils {
return imeMap;
}
+ @NonNull
+ public static String buildInputMethodsAndSubtypesString(
+ @NonNull final ArrayMap<String, ArraySet<String>> map) {
+ // we want to use the canonical InputMethodSettings implementation,
+ // so we convert data structures first.
+ List<Pair<String, ArrayList<String>>> imeMap = new ArrayList<>(4);
+ for (ArrayMap.Entry<String, ArraySet<String>> entry : map.entrySet()) {
+ final String imeName = entry.getKey();
+ final ArraySet<String> subtypeSet = entry.getValue();
+ final ArrayList<String> subtypes = new ArrayList<>(2);
+ if (subtypeSet != null) {
+ subtypes.addAll(subtypeSet);
+ }
+ imeMap.add(new Pair<>(imeName, subtypes));
+ }
+ return InputMethodSettings.buildInputMethodsSettingString(imeMap);
+ }
+
/**
* Utility class for putting and getting settings for InputMethod
* TODO: Move all putters and getters of settings to this class.
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 7f01841d8afe..ec414474de70 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -170,6 +170,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
+ // When we reuse decor views, we need to recreate the content root. This happens when the decor
+ // view is requested, so we need to force the recreating without introducing an infinite loop.
+ private boolean mForceDecorInstall = false;
+
// This is the non client decor view for the window, containing the caption and window control
// buttons. The visibility of this decor depends on the workspace and the window type.
// If the window type does not require such a view, this member might be null.
@@ -248,6 +252,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private Drawable mBackgroundDrawable;
+ private boolean mLoadEleveation = true;
private float mElevation;
/** Whether window content should be clipped to the background outline. */
@@ -323,6 +328,16 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
mLayoutInflater = LayoutInflater.from(context);
}
+ public PhoneWindow(Context context, Window preservedWindow) {
+ this(context);
+ if (preservedWindow != null) {
+ mDecor = (DecorView) preservedWindow.getDecorView();
+ mElevation = preservedWindow.getElevation();
+ mLoadEleveation = false;
+ mForceDecorInstall = true;
+ }
+ }
+
@Override
public final void setContainer(Window container) {
super.setContainer(container);
@@ -463,6 +478,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
}
+ public void clearContentView() {
+ if (mNonClientDecorView.getChildCount() > 1) {
+ mNonClientDecorView.removeViewAt(1);
+ }
+ }
+
private void transitionTo(Scene scene) {
if (mContentScene == null) {
scene.enter();
@@ -1396,6 +1417,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
@Override
+ public float getElevation() {
+ return mElevation;
+ }
+
+ @Override
public final void setClipToOutline(boolean clipToOutline) {
mClipToOutline = clipToOutline;
if (mDecor != null) {
@@ -1992,7 +2018,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
@Override
public final View getDecorView() {
- if (mDecor == null) {
+ if (mDecor == null || mForceDecorInstall) {
installDecor();
}
return mDecor;
@@ -2266,7 +2292,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
}
- private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
+ private static final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
/* package */int mDefaultOpacity = PixelFormat.OPAQUE;
@@ -2336,6 +2362,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private int mRootScrollY = 0;
+ private PhoneWindow mWindow;
+
public DecorView(Context context, int featureId) {
super(context);
mFeatureId = featureId;
@@ -2357,7 +2385,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
@Override
public void onDraw(Canvas c) {
super.onDraw(c);
- mBackgroundFallback.draw(mContentRoot, c, mContentParent);
+ mBackgroundFallback.draw(mWindow.mContentRoot, c, mWindow.mContentParent);
}
@Override
@@ -2369,7 +2397,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (isDown && (event.getRepeatCount() == 0)) {
// First handle chording of panel key: if a panel key is held
// but not released, try to execute a shortcut in it.
- if ((mPanelChordingKey > 0) && (mPanelChordingKey != keyCode)) {
+ if ((mWindow.mPanelChordingKey > 0) && (mWindow.mPanelChordingKey != keyCode)) {
boolean handled = dispatchKeyShortcutEvent(event);
if (handled) {
return true;
@@ -2378,15 +2406,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
// If a panel is open, perform a shortcut on it without the
// chorded panel key
- if ((mPreparedPanel != null) && mPreparedPanel.isOpen) {
- if (performPanelShortcut(mPreparedPanel, keyCode, event, 0)) {
+ if ((mWindow.mPreparedPanel != null) && mWindow.mPreparedPanel.isOpen) {
+ if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) {
return true;
}
}
}
- if (!isDestroyed()) {
- final Callback cb = getCallback();
+ if (!mWindow.isDestroyed()) {
+ final Callback cb = mWindow.getCallback();
final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
: super.dispatchKeyEvent(event);
if (handled) {
@@ -2394,28 +2422,28 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
}
- return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event)
- : PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event);
+ return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
+ : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
}
@Override
public boolean dispatchKeyShortcutEvent(KeyEvent ev) {
// If the panel is already prepared, then perform the shortcut using it.
boolean handled;
- if (mPreparedPanel != null) {
- handled = performPanelShortcut(mPreparedPanel, ev.getKeyCode(), ev,
+ if (mWindow.mPreparedPanel != null) {
+ handled = mWindow.performPanelShortcut(mWindow.mPreparedPanel, ev.getKeyCode(), ev,
Menu.FLAG_PERFORM_NO_CLOSE);
if (handled) {
- if (mPreparedPanel != null) {
- mPreparedPanel.isHandled = true;
+ if (mWindow.mPreparedPanel != null) {
+ mWindow.mPreparedPanel.isHandled = true;
}
return true;
}
}
// Shortcut not handled by the panel. Dispatch to the view hierarchy.
- final Callback cb = getCallback();
- handled = cb != null && !isDestroyed() && mFeatureId < 0
+ final Callback cb = mWindow.getCallback();
+ handled = cb != null && !mWindow.isDestroyed() && mFeatureId < 0
? cb.dispatchKeyShortcutEvent(ev) : super.dispatchKeyShortcutEvent(ev);
if (handled) {
return true;
@@ -2425,10 +2453,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
// combination such as Control+C. Temporarily prepare the panel then mark it
// unprepared again when finished to ensure that the panel will again be prepared
// the next time it is shown for real.
- PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
- if (st != null && mPreparedPanel == null) {
- preparePanel(st, ev);
- handled = performPanelShortcut(st, ev.getKeyCode(), ev,
+ PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
+ if (st != null && mWindow.mPreparedPanel == null) {
+ mWindow.preparePanel(st, ev);
+ handled = mWindow.performPanelShortcut(st, ev.getKeyCode(), ev,
Menu.FLAG_PERFORM_NO_CLOSE);
st.isPrepared = false;
if (handled) {
@@ -2440,23 +2468,23 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
- final Callback cb = getCallback();
- return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
- : super.dispatchTouchEvent(ev);
+ final Callback cb = mWindow.getCallback();
+ return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
+ ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
}
@Override
public boolean dispatchTrackballEvent(MotionEvent ev) {
- final Callback cb = getCallback();
- return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTrackballEvent(ev)
- : super.dispatchTrackballEvent(ev);
+ final Callback cb = mWindow.getCallback();
+ return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
+ ? cb.dispatchTrackballEvent(ev) : super.dispatchTrackballEvent(ev);
}
@Override
public boolean dispatchGenericMotionEvent(MotionEvent ev) {
- final Callback cb = getCallback();
- return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchGenericMotionEvent(ev)
- : super.dispatchGenericMotionEvent(ev);
+ final Callback cb = mWindow.getCallback();
+ return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
+ ? cb.dispatchGenericMotionEvent(ev) : super.dispatchGenericMotionEvent(ev);
}
public boolean superDispatchKeyEvent(KeyEvent event) {
@@ -2508,7 +2536,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
int action = event.getAction();
- if (mHasNonClientDecor && mNonClientDecorView.mVisible) {
+ if (mHasNonClientDecor && mWindow.mNonClientDecorView.mVisible) {
// Don't dispatch ACTION_DOWN to the non client decor if the window is
// resizable and the event was (starting) outside the window.
// Window resizing events should be handled by WindowManager.
@@ -2531,7 +2559,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
int x = (int)event.getX();
int y = (int)event.getY();
if (isOutOfBounds(x, y)) {
- closePanel(mFeatureId);
+ mWindow.closePanel(mFeatureId);
return true;
}
}
@@ -2557,7 +2585,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (action == MotionEvent.ACTION_MOVE) {
if (y > (mDownY+30)) {
Log.i(TAG, "Closing!");
- closePanel(mFeatureId);
+ mWindow.closePanel(mFeatureId);
mWatchingForMenu = false;
return true;
}
@@ -2573,7 +2601,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (action == MotionEvent.ACTION_DOWN) {
int y = (int)event.getY();
- if (y >= (getHeight()-5) && !hasChildren()) {
+ if (y >= (getHeight()-5) && !mWindow.hasChildren()) {
Log.i(TAG, "Watchiing!");
mWatchingForMenu = true;
}
@@ -2588,7 +2616,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (action == MotionEvent.ACTION_MOVE) {
if (y < (getHeight()-30)) {
Log.i(TAG, "Opening!");
- openPanel(FEATURE_OPTIONS_PANEL, new KeyEvent(
+ mWindow.openPanel(FEATURE_OPTIONS_PANEL, new KeyEvent(
KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
mWatchingForMenu = false;
return true;
@@ -2622,8 +2650,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
@Override
public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
- final Callback cb = getCallback();
- if (cb != null && !isDestroyed()) {
+ final Callback cb = mWindow.getCallback();
+ if (cb != null && !mWindow.isDestroyed()) {
if (cb.dispatchPopulateAccessibilityEvent(event)) {
return true;
}
@@ -2660,7 +2688,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (SWEEP_OPEN_MENU) {
if (mMenuBackground == null && mFeatureId < 0
- && getAttributes().height
+ && mWindow.getAttributes().height
== WindowManager.LayoutParams.MATCH_PARENT) {
mMenuBackground = getContext().getDrawable(
R.drawable.menu_background);
@@ -2685,7 +2713,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
boolean fixedWidth = false;
if (widthMode == AT_MOST) {
- final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor;
+ final TypedValue tvw = isPortrait ? mWindow.mFixedWidthMinor
+ : mWindow.mFixedWidthMajor;
if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
final int w;
if (tvw.type == TypedValue.TYPE_DIMENSION) {
@@ -2706,7 +2735,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
if (heightMode == AT_MOST) {
- final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor;
+ final TypedValue tvh = isPortrait ? mWindow.mFixedHeightMajor
+ : mWindow.mFixedHeightMinor;
if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
final int h;
if (tvh.type == TypedValue.TYPE_DIMENSION) {
@@ -2724,21 +2754,21 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
}
- getOutsets(mOutsets);
- if (mOutsets.top > 0 || mOutsets.bottom > 0) {
+ getOutsets(mWindow.mOutsets);
+ if (mWindow.mOutsets.top > 0 || mWindow.mOutsets.bottom > 0) {
int mode = MeasureSpec.getMode(heightMeasureSpec);
if (mode != MeasureSpec.UNSPECIFIED) {
int height = MeasureSpec.getSize(heightMeasureSpec);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(
- height + mOutsets.top + mOutsets.bottom, mode);
+ height + mWindow.mOutsets.top + mWindow.mOutsets.bottom, mode);
}
}
- if (mOutsets.left > 0 || mOutsets.right > 0) {
+ if (mWindow.mOutsets.left > 0 || mWindow.mOutsets.right > 0) {
int mode = MeasureSpec.getMode(widthMeasureSpec);
if (mode != MeasureSpec.UNSPECIFIED) {
int width = MeasureSpec.getSize(widthMeasureSpec);
widthMeasureSpec = MeasureSpec.makeMeasureSpec(
- width + mOutsets.left + mOutsets.right, mode);
+ width + mWindow.mOutsets.left + mWindow.mOutsets.right, mode);
}
}
@@ -2750,7 +2780,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
if (!fixedWidth && widthMode == AT_MOST) {
- final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor;
+ final TypedValue tv = isPortrait ? mWindow.mMinWidthMinor : mWindow.mMinWidthMajor;
if (tv.type != TypedValue.TYPE_NULL) {
final int min;
if (tv.type == TypedValue.TYPE_DIMENSION) {
@@ -2778,12 +2808,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- getOutsets(mOutsets);
- if (mOutsets.left > 0) {
- offsetLeftAndRight(-mOutsets.left);
+ getOutsets(mWindow.mOutsets);
+ if (mWindow.mOutsets.left > 0) {
+ offsetLeftAndRight(-mWindow.mOutsets.left);
}
- if (mOutsets.top > 0) {
- offsetTopAndBottom(-mOutsets.top);
+ if (mWindow.mOutsets.top > 0) {
+ offsetTopAndBottom(-mWindow.mOutsets.top);
}
}
@@ -2799,23 +2829,23 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
@Override
public boolean showContextMenuForChild(View originalView) {
// Reuse the context menu builder
- if (mContextMenu == null) {
- mContextMenu = new ContextMenuBuilder(getContext());
- mContextMenu.setCallback(mContextMenuCallback);
+ if (mWindow.mContextMenu == null) {
+ mWindow.mContextMenu = new ContextMenuBuilder(getContext());
+ mWindow.mContextMenu.setCallback(mWindow.mContextMenuCallback);
} else {
- mContextMenu.clearAll();
+ mWindow.mContextMenu.clearAll();
}
- final MenuDialogHelper helper = mContextMenu.show(originalView,
+ final MenuDialogHelper helper = mWindow.mContextMenu.show(originalView,
originalView.getWindowToken());
if (helper != null) {
- helper.setPresenterCallback(mContextMenuCallback);
- } else if (mContextMenuHelper != null) {
+ helper.setPresenterCallback(mWindow.mContextMenuCallback);
+ } else if (mWindow.mContextMenuHelper != null) {
// No menu to show, but if we have a menu currently showing it just became blank.
// Close it.
- mContextMenuHelper.dismiss();
+ mWindow.mContextMenuHelper.dismiss();
}
- mContextMenuHelper = helper;
+ mWindow.mContextMenuHelper = helper;
return helper != null;
}
@@ -2845,14 +2875,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
View originatingView, ActionMode.Callback callback, int type) {
ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback);
ActionMode mode = null;
- if (getCallback() != null && !isDestroyed()) {
+ if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
try {
- mode = getCallback().onWindowStartingActionMode(wrappedCallback, type);
+ mode = mWindow.getCallback().onWindowStartingActionMode(wrappedCallback, type);
} catch (AbstractMethodError ame) {
// Older apps might not implement the typed version of this method.
if (type == ActionMode.TYPE_PRIMARY) {
try {
- mode = getCallback().onWindowStartingActionMode(wrappedCallback);
+ mode = mWindow.getCallback().onWindowStartingActionMode(
+ wrappedCallback);
} catch (AbstractMethodError ame2) {
// Older apps might not implement this callback method at all.
}
@@ -2877,9 +2908,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
mode = null;
}
}
- if (mode != null && getCallback() != null && !isDestroyed()) {
+ if (mode != null && mWindow.getCallback() != null && !mWindow.isDestroyed()) {
try {
- getCallback().onActionModeStarted(mode);
+ mWindow.getCallback().onActionModeStarted(mode);
} catch (AbstractMethodError ame) {
// Older apps might not implement this callback method.
}
@@ -2968,10 +2999,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
private WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
- WindowManager.LayoutParams attrs = getAttributes();
+ WindowManager.LayoutParams attrs = mWindow.getAttributes();
int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
- if (!mIsFloating && ActivityManager.isHighEndGfx()) {
+ if (!mWindow.mIsFloating && ActivityManager.isHighEndGfx()) {
boolean disallowAnimate = !isLaidOut();
disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)
& FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
@@ -3003,14 +3034,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
boolean navBarToRightEdge = mLastBottomInset == 0 && mLastRightInset > 0;
int navBarSize = navBarToRightEdge ? mLastRightInset : mLastBottomInset;
- updateColorViewInt(mNavigationColorViewState, sysUiVisibility, mNavigationBarColor,
- navBarSize, navBarToRightEdge, 0 /* rightInset */,
- animate && !disallowAnimate);
+ updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
+ mWindow.mNavigationBarColor, navBarSize, navBarToRightEdge,
+ 0 /* rightInset */, animate && !disallowAnimate);
boolean statusBarNeedsRightInset = navBarToRightEdge
&& mNavigationColorViewState.present;
int statusBarRightInset = statusBarNeedsRightInset ? mLastRightInset : 0;
- updateColorViewInt(mStatusColorViewState, sysUiVisibility, mStatusBarColor,
+ updateColorViewInt(mStatusColorViewState, sysUiVisibility, mWindow.mStatusBarColor,
mLastTopInset, false /* matchVertical */, statusBarRightInset,
animate && !disallowAnimate);
}
@@ -3027,13 +3058,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
int consumedRight = consumingNavBar ? mLastRightInset : 0;
int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
- if (mContentRoot != null
- && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
- MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams();
+ if (mWindow.mContentRoot != null
+ && mWindow.mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
+ MarginLayoutParams lp = (MarginLayoutParams) mWindow.mContentRoot.getLayoutParams();
if (lp.rightMargin != consumedRight || lp.bottomMargin != consumedBottom) {
lp.rightMargin = consumedRight;
lp.bottomMargin = consumedBottom;
- mContentRoot.setLayoutParams(lp);
+ mWindow.mContentRoot.setLayoutParams(lp);
if (insets == null) {
// The insets have changed, but we're not currently in the process
@@ -3071,11 +3102,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
int size, boolean verticalBar, int rightMargin, boolean animate) {
state.present = size > 0 && (sysUiVis & state.systemUiHideFlag) == 0
- && (getAttributes().flags & state.hideWindowFlag) == 0
- && (getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
+ && (mWindow.getAttributes().flags & state.hideWindowFlag) == 0
+ && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
boolean show = state.present
&& (color & Color.BLACK) != 0
- && (getAttributes().flags & state.translucentFlag) == 0;
+ && (mWindow.getAttributes().flags & state.translucentFlag) == 0;
boolean visibilityChanged = false;
View view = state.view;
@@ -3167,14 +3198,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
mPrimaryActionModeView.getLayoutParams();
boolean mlpChanged = false;
if (mPrimaryActionModeView.isShown()) {
- if (mTempRect == null) {
- mTempRect = new Rect();
+ if (mWindow.mTempRect == null) {
+ mWindow.mTempRect = new Rect();
}
- final Rect rect = mTempRect;
+ final Rect rect = mWindow.mTempRect;
// If the parent doesn't consume the insets, manually
// apply the default system window insets.
- mContentParent.computeSystemWindowInsets(insets, rect);
+ mWindow.mContentParent.computeSystemWindowInsets(insets, rect);
final int newMargin = rect.top == 0 ? insets.getSystemWindowInsetTop() : 0;
if (mlp.topMargin != newMargin) {
mlpChanged = true;
@@ -3205,7 +3236,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
// mode is overlaid on the app content (e.g. it's
// sitting in a FrameLayout, see
// screen_simple_overlay_action_mode.xml).
- final boolean nonOverlay = (getLocalFeatures()
+ final boolean nonOverlay = (mWindow.getLocalFeatures()
& (1 << FEATURE_ACTION_MODE_OVERLAY)) == 0;
insets = insets.consumeSystemWindowInsets(
false, nonOverlay && showStatusGuard /* top */, false, false);
@@ -3229,14 +3260,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private void updateNavigationGuard(WindowInsets insets) {
// IMEs lay out below the nav bar, but the content view must not (for back compat)
- if (getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
+ if (mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
// prevent the content view from including the nav bar height
- if (mContentParent != null) {
- if (mContentParent.getLayoutParams() instanceof MarginLayoutParams) {
+ if (mWindow.mContentParent != null) {
+ if (mWindow.mContentParent.getLayoutParams() instanceof MarginLayoutParams) {
MarginLayoutParams mlp =
- (MarginLayoutParams) mContentParent.getLayoutParams();
+ (MarginLayoutParams) mWindow.mContentParent.getLayoutParams();
mlp.bottomMargin = insets.getSystemWindowInsetBottom();
- mContentParent.setLayoutParams(mlp);
+ mWindow.mContentParent.setLayoutParams(mlp);
}
}
// position the navigation guard view, creating it if necessary
@@ -3318,7 +3349,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
mDefaultOpacity = opacity;
if (mFeatureId < 0) {
- setDefaultWindowFormat(opacity);
+ mWindow.setDefaultWindowFormat(opacity);
}
}
@@ -3328,12 +3359,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
// If the user is chording a menu shortcut, release the chord since
// this window lost focus
- if (hasFeature(FEATURE_OPTIONS_PANEL) && !hasWindowFocus && mPanelChordingKey != 0) {
- closePanel(FEATURE_OPTIONS_PANEL);
+ if (mWindow.hasFeature(FEATURE_OPTIONS_PANEL) && !hasWindowFocus
+ && mWindow.mPanelChordingKey != 0) {
+ mWindow.closePanel(FEATURE_OPTIONS_PANEL);
}
- final Callback cb = getCallback();
- if (cb != null && !isDestroyed() && mFeatureId < 0) {
+ final Callback cb = mWindow.getCallback();
+ if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
cb.onWindowFocusChanged(hasWindowFocus);
}
@@ -3349,8 +3381,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- final Callback cb = getCallback();
- if (cb != null && !isDestroyed() && mFeatureId < 0) {
+ final Callback cb = mWindow.getCallback();
+ if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
cb.onAttachedToWindow();
}
@@ -3362,7 +3394,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
* menu was open. When the activity is recreated, the menu
* should be shown again.
*/
- openPanelsAfterRestore();
+ mWindow.openPanelsAfterRestore();
}
}
@@ -3370,13 +3402,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- final Callback cb = getCallback();
+ final Callback cb = mWindow.getCallback();
if (cb != null && mFeatureId < 0) {
cb.onDetachedFromWindow();
}
- if (mDecorContentParent != null) {
- mDecorContentParent.dismissPopups();
+ if (mWindow.mDecorContentParent != null) {
+ mWindow.mDecorContentParent.dismissPopups();
}
if (mPrimaryActionModePopup != null) {
@@ -3391,7 +3423,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
mFloatingToolbar = null;
}
- PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
+ PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
if (st != null && st.menu != null && mFeatureId < 0) {
st.menu.close();
}
@@ -3400,29 +3432,29 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
@Override
public void onCloseSystemDialogs(String reason) {
if (mFeatureId >= 0) {
- closeAllPanels();
+ mWindow.closeAllPanels();
}
}
public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() {
- return mFeatureId < 0 ? mTakeSurfaceCallback : null;
+ return mFeatureId < 0 ? mWindow.mTakeSurfaceCallback : null;
}
public InputQueue.Callback willYouTakeTheInputQueue() {
- return mFeatureId < 0 ? mTakeInputQueueCallback : null;
+ return mFeatureId < 0 ? mWindow.mTakeInputQueueCallback : null;
}
public void setSurfaceType(int type) {
- PhoneWindow.this.setType(type);
+ mWindow.setType(type);
}
public void setSurfaceFormat(int format) {
- PhoneWindow.this.setFormat(format);
+ mWindow.setFormat(format);
}
public void setSurfaceKeepScreenOn(boolean keepOn) {
- if (keepOn) PhoneWindow.this.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- else PhoneWindow.this.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ if (keepOn) mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ else mWindow.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
@Override
@@ -3454,7 +3486,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
endOnGoingFadeAnimation();
cleanupPrimaryActionMode();
if (mPrimaryActionModeView == null) {
- if (isFloating()) {
+ if (mWindow.isFloating()) {
// Use the action bar theme.
final TypedValue outValue = new TypedValue();
final Theme baseTheme = mContext.getTheme();
@@ -3601,7 +3633,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private void setHandledFloatingActionMode(ActionMode mode) {
mFloatingActionMode = mode;
- mFloatingToolbar = new FloatingToolbar(mContext, PhoneWindow.this);
+ mFloatingToolbar = new FloatingToolbar(mContext, mWindow);
((FloatingActionMode) mFloatingActionMode).setFloatingToolbar(mFloatingToolbar);
mFloatingActionMode.invalidate(); // Will show the floating toolbar if necessary.
mFloatingActionModeOriginatingView.getViewTreeObserver()
@@ -3640,6 +3672,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
return windowHasNonClientDecor() && getElevation() > 0;
}
+ void setWindow(PhoneWindow phoneWindow) {
+ mWindow = phoneWindow;
+ }
+
/**
* Clears out internal references when the action mode is destroyed.
*/
@@ -3728,9 +3764,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
cleanupFloatingActionModeViews();
mFloatingActionMode = null;
}
- if (getCallback() != null && !isDestroyed()) {
+ if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
try {
- getCallback().onActionModeFinished(mode);
+ mWindow.getCallback().onActionModeFinished(mode);
} catch (AbstractMethodError ame) {
// Older apps might not implement this callback method.
}
@@ -3750,7 +3786,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
protected DecorView generateDecor(int featureId) {
- return new DecorView(getContext(), featureId);
+ // System process doesn't have application context and in that case we need to directly use
+ // the context we have. Otherwise we want the application context, so we don't cling to the
+ // activity.
+ Context context = getContext().getApplicationContext();
+ if (context == null) {
+ context = getContext();
+ }
+ return new DecorView(context, featureId);
}
protected void setFeatureFromAttrs(int featureId, TypedArray attrs,
@@ -3960,7 +4003,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
+ Integer.toHexString(mFrameResource));
}
}
- mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);
+ if (mLoadEleveation) {
+ mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);
+ }
mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);
mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT);
}
@@ -4032,8 +4077,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
mNonClientDecorView = createNonClientDecorView();
View in = mLayoutInflater.inflate(layoutResource, null);
if (mNonClientDecorView != null) {
- decor.addView(mNonClientDecorView,
- new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+ if (mNonClientDecorView.getParent() == null) {
+ decor.addView(mNonClientDecorView,
+ new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+ }
mNonClientDecorView.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
} else {
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
@@ -4096,6 +4143,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
// Free floating overlapping windows require a non client decor with a caption and shadow..
private NonClientDecorView createNonClientDecorView() {
NonClientDecorView nonClientDecorView = null;
+ for (int i = mDecor.getChildCount() - 1; i >= 0 && nonClientDecorView == null; i--) {
+ View view = mDecor.getChildAt(i);
+ if (view instanceof NonClientDecorView) {
+ // The decor was most likely saved from a relaunch - so reuse it.
+ nonClientDecorView = (NonClientDecorView) view;
+ mDecor.removeViewAt(i);
+ }
+ }
final WindowManager.LayoutParams attrs = getAttributes();
boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
attrs.type == TYPE_APPLICATION;
@@ -4106,21 +4161,28 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
mWorkspaceId < FIRST_DYNAMIC_STACK_ID) {
// Dependent on the brightness of the used title we either use the
// dark or the light button frame.
- TypedValue value = new TypedValue();
- getContext().getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
- if (Color.luminance(value.data) < 0.5) {
- nonClientDecorView = (NonClientDecorView) mLayoutInflater.inflate(
- R.layout.non_client_decor_dark, null);
- } else {
- nonClientDecorView = (NonClientDecorView) mLayoutInflater.inflate(
- R.layout.non_client_decor_light, null);
+ if (nonClientDecorView == null) {
+ TypedValue value = new TypedValue();
+ getContext().getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
+ // We can't use the application context inside the general inflater, because some
+ // views might depend on the fact that they get Activity or even specific activity.
+ // We control the NonClientDecor, so we know that application context should be
+ // safe enough.
+ LayoutInflater inflater =
+ mLayoutInflater.cloneInContext(getContext().getApplicationContext());
+ if (Color.luminance(value.data) < 0.5) {
+ nonClientDecorView = (NonClientDecorView) inflater.inflate(
+ R.layout.non_client_decor_dark, null);
+ } else {
+ nonClientDecorView = (NonClientDecorView) inflater.inflate(
+ R.layout.non_client_decor_light, null);
+ }
}
nonClientDecorView.setPhoneWindow(this, hasNonClientDecor(mWorkspaceId),
nonClientDecorHasShadow(mWorkspaceId));
}
// Tell the decor if it has a visible non client decor.
- mDecor.enableNonClientDecor(nonClientDecorView != null &&
- hasNonClientDecor(mWorkspaceId));
+ mDecor.enableNonClientDecor(nonClientDecorView != null && hasNonClientDecor(mWorkspaceId));
return nonClientDecorView;
}
@@ -4131,14 +4193,17 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
private void installDecor() {
+ mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
+ mDecor.setWindow(this);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
+ mDecor.setWindow(this);
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
@@ -5307,4 +5372,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
// TODO(skuhne): Add side by side mode here to add a decor.
return workspaceId == FREEFORM_WORKSPACE_STACK_ID;
}
+
+ @Override
+ public boolean hasNonClientDecorView() {
+ return mNonClientDecorView != null;
+ }
}
diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
index 4c829a240e70..415f32528914 100644
--- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
@@ -10,16 +10,22 @@ import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
+import android.os.Handler;
import android.os.Parcelable;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnKeyListener;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.AdapterView;
import android.widget.DropDownListView;
+import android.widget.MenuItemHoverListener;
import android.widget.ListView;
import android.widget.MenuPopupWindow;
+import android.widget.MenuPopupWindow.MenuDropDownListView;
import android.widget.PopupWindow;
import android.widget.PopupWindow.OnDismissListener;
@@ -39,21 +45,144 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl
private static final int HORIZ_POSITION_LEFT = 0;
private static final int HORIZ_POSITION_RIGHT = 1;
+ private static final int SUBMENU_TIMEOUT_MS = 200;
+
private final Context mContext;
private final int mMenuMaxWidth;
private final int mPopupStyleAttr;
private final int mPopupStyleRes;
private final boolean mOverflowOnly;
private final int mLayoutDirection;
+ private final Handler mSubMenuHoverHandler;
+
+ private final OnGlobalLayoutListener mGlobalLayoutListener = new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ if (isShowing()) {
+ final View anchor = mShownAnchorView;
+ if (anchor == null || !anchor.isShown()) {
+ dismiss();
+ } else if (isShowing()) {
+ // Recompute window sizes and positions.
+ for (MenuPopupWindow popup : mPopupWindows) {
+ popup.show();
+ }
+ }
+ }
+ }
+ };
+
+ private final OnAttachStateChangeListener mAttachStateChangeListener =
+ new OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ if (mTreeObserver != null) {
+ if (!mTreeObserver.isAlive()) {
+ mTreeObserver = v.getViewTreeObserver();
+ }
+ mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
+ }
+ v.removeOnAttachStateChangeListener(this);
+ }
+ };
+
+ private final MenuItemHoverListener mMenuItemHoverListener = new MenuItemHoverListener() {
+ @Override
+ public void onItemHovered(MenuBuilder menu, int position) {
+ int menuIndex = -1;
+ for (int i = 0; i < mListViews.size(); i++) {
+ final MenuDropDownListView view = (MenuDropDownListView) mListViews.get(i);
+ final MenuAdapter adapter = (MenuAdapter) view.getAdapter();
+
+ if (adapter.getAdapterMenu() == menu) {
+ menuIndex = i;
+ break;
+ }
+ }
+
+ if (menuIndex == -1) {
+ return;
+ }
+
+ final MenuDropDownListView view = (MenuDropDownListView) mListViews.get(menuIndex);
+ final ListMenuItemView selectedItemView = (ListMenuItemView) view.getSelectedView();
+
+ if (selectedItemView != null && selectedItemView.isEnabled()
+ && selectedItemView.getItemData().hasSubMenu()) {
+ // If the currently selected item corresponds to a submenu, schedule to open the
+ // submenu on a timeout.
+
+ mSubMenuHoverHandler.removeCallbacksAndMessages(null);
+ mSubMenuHoverHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ // Make sure the submenu item is still the one selected.
+ if (view.getSelectedView() == selectedItemView
+ && selectedItemView.isEnabled()
+ && selectedItemView.getItemData().hasSubMenu()) {
+ // Close any other submenus that might be open at the current or
+ // a deeper level.
+ int nextIndex = mListViews.indexOf(view) + 1;
+ if (nextIndex < mListViews.size()) {
+ MenuAdapter nextSubMenuAdapter =
+ (MenuAdapter) mListViews.get(nextIndex).getAdapter();
+ // Disable exit animation, to prevent overlapping fading out
+ // submenus.
+ mPopupWindows.get(nextIndex).setExitTransition(null);
+ nextSubMenuAdapter.getAdapterMenu().close();
+ }
+
+ // Then open the selected submenu.
+ view.performItemClick(
+ selectedItemView,
+ view.getSelectedItemPosition(),
+ view.getSelectedItemId());
+ }
+ }
+ }, SUBMENU_TIMEOUT_MS);
+ } else if (menuIndex + 1 < mListViews.size()) {
+ // If the currently selected item does NOT corresponds to a submenu, check if there
+ // is a submenu already open that is one level deeper. If so, schedule to close it
+ // on a timeout.
+
+ final MenuDropDownListView nextView =
+ (MenuDropDownListView) mListViews.get(menuIndex + 1);
+ final MenuAdapter nextAdapter = (MenuAdapter) nextView.getAdapter();
+
+ view.clearSelection();
+
+ mSubMenuHoverHandler.removeCallbacksAndMessages(null);
+ mSubMenuHoverHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ // Make sure the menu wasn't already closed by something else and that
+ // it wasn't re-hovered by the user since this was scheduled.
+ int nextMenuIndex = mListViews.indexOf(nextView);
+ if (nextMenuIndex != -1 && nextView.getSelectedView() == null) {
+ // Disable exit animation, to prevent overlapping fading out submenus.
+ mPopupWindows.get(nextMenuIndex).setExitTransition(null);
+ nextAdapter.getAdapterMenu().close();
+ }
+ }
+ }, SUBMENU_TIMEOUT_MS);
+ }
+ }
+ };
private int mDropDownGravity = Gravity.NO_GRAVITY;
- private View mAnchor;
+ private View mAnchorView;
+ private View mShownAnchorView;
private List<DropDownListView> mListViews;
private List<MenuPopupWindow> mPopupWindows;
private List<int[]> mOffsets;
private int mPreferredPosition;
private boolean mForceShowIcon;
private Callback mPresenterCallback;
+ private ViewTreeObserver mTreeObserver;
private PopupWindow.OnDismissListener mOnDismissListener;
/**
@@ -64,7 +193,7 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl
public CascadingMenuPopup(Context context, View anchor, int popupStyleAttr,
int popupStyleRes, boolean overflowOnly) {
mContext = Preconditions.checkNotNull(context);
- mAnchor = Preconditions.checkNotNull(anchor);
+ mAnchorView = Preconditions.checkNotNull(anchor);
mPopupStyleAttr = popupStyleAttr;
mPopupStyleRes = popupStyleRes;
mOverflowOnly = overflowOnly;
@@ -82,6 +211,7 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl
mPopupWindows = new ArrayList<MenuPopupWindow>();
mListViews = new ArrayList<DropDownListView>();
mOffsets = new ArrayList<int[]>();
+ mSubMenuHoverHandler = new Handler();
}
@Override
@@ -92,12 +222,12 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl
private MenuPopupWindow createPopupWindow() {
MenuPopupWindow popupWindow = new MenuPopupWindow(
mContext, null, mPopupStyleAttr, mPopupStyleRes);
+ popupWindow.setHoverListener(mMenuItemHoverListener);
popupWindow.setOnItemClickListener(this);
popupWindow.setOnDismissListener(this);
- popupWindow.setAnchorView(mAnchor);
+ popupWindow.setAnchorView(mAnchorView);
popupWindow.setDropDownGravity(mDropDownGravity);
popupWindow.setModal(true);
- popupWindow.setTouchModal(false);
return popupWindow;
}
@@ -116,6 +246,16 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl
popupWindow.show();
mListViews.add((DropDownListView) popupWindow.getListView());
}
+
+ mShownAnchorView = mAnchorView;
+ if (mShownAnchorView != null) {
+ final boolean addGlobalListener = mTreeObserver == null;
+ mTreeObserver = mShownAnchorView.getViewTreeObserver(); // Refresh to latest
+ if (addGlobalListener) {
+ mTreeObserver.addOnGlobalLayoutListener(mGlobalLayoutListener);
+ }
+ mShownAnchorView.addOnAttachStateChangeListener(mAttachStateChangeListener);
+ }
}
@Override
@@ -160,7 +300,7 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl
lastListView.getLocationOnScreen(screenLocation);
final Rect displayFrame = new Rect();
- mAnchor.getWindowVisibleDisplayFrame(displayFrame);
+ mShownAnchorView.getWindowVisibleDisplayFrame(displayFrame);
if (mPreferredPosition == HORIZ_POSITION_RIGHT) {
final int right = screenLocation[0] + lastListView.getWidth() + nextMenuWidth;
@@ -196,6 +336,7 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl
int y = 0;
if (addSubMenu) {
+ popupWindow.setTouchModal(false);
popupWindow.setEnterTransition(null);
ListView lastListView = mListViews.get(mListViews.size() - 1);
@@ -319,7 +460,7 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl
if (menuIndex == -1 && menu == adapter.mAdapterMenu) {
menuIndex = i;
- wasSelected = view.getSelectedItem() != null;
+ wasSelected = view.getSelectedView() != null;
}
// Once the menu has been found, remove it and all submenus beneath it from the
@@ -352,6 +493,13 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl
}
if (mPopupWindows.size() == 0) {
+ if (mTreeObserver != null) {
+ if (mTreeObserver.isAlive()) {
+ mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
+ }
+ mTreeObserver = null;
+ }
+ mShownAnchorView.removeOnAttachStateChangeListener(mAttachStateChangeListener);
// If every [sub]menu was dismissed, that means the whole thing was dismissed, so notify
// the owner.
mOnDismissListener.onDismiss();
@@ -379,7 +527,7 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl
@Override
public void setAnchorView(View anchor) {
- mAnchor = anchor;
+ mAnchorView = anchor;
}
@Override
@@ -391,4 +539,5 @@ final class CascadingMenuPopup extends MenuPopup implements AdapterView.OnItemCl
public ListView getListView() {
return mListViews.size() > 0 ? mListViews.get(mListViews.size() - 1) : null;
}
+
} \ No newline at end of file
diff --git a/core/java/com/android/internal/view/menu/MenuAdapter.java b/core/java/com/android/internal/view/menu/MenuAdapter.java
index 1e03b1f8b971..673cfd12d878 100644
--- a/core/java/com/android/internal/view/menu/MenuAdapter.java
+++ b/core/java/com/android/internal/view/menu/MenuAdapter.java
@@ -40,6 +40,10 @@ public class MenuAdapter extends BaseAdapter {
findExpandedIndex();
}
+ public boolean getForceShowIcon() {
+ return mForceShowIcon;
+ }
+
public void setForceShowIcon(boolean forceShow) {
mForceShowIcon = forceShow;
}
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index e0d7feefb1b0..ea7998339f3b 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -16,10 +16,11 @@
package com.android.internal.view.menu;
+import com.android.internal.view.menu.MenuPresenter.Callback;
+
import android.content.Context;
import android.view.Gravity;
import android.view.View;
-import android.view.ViewTreeObserver;
import android.widget.PopupWindow;
/**
@@ -27,8 +28,7 @@ import android.widget.PopupWindow;
*
* @hide
*/
-public class MenuPopupHelper implements ViewTreeObserver.OnGlobalLayoutListener,
- PopupWindow.OnDismissListener, View.OnAttachStateChangeListener {
+public class MenuPopupHelper implements PopupWindow.OnDismissListener {
private final Context mContext;
private final MenuBuilder mMenu;
private final boolean mOverflowOnly;
@@ -37,9 +37,9 @@ public class MenuPopupHelper implements ViewTreeObserver.OnGlobalLayoutListener,
private View mAnchorView;
private MenuPopup mPopup;
- private ViewTreeObserver mTreeObserver;
private int mDropDownGravity = Gravity.NO_GRAVITY;
+ private Callback mPresenterCallback;
public MenuPopupHelper(Context context, MenuBuilder menu) {
this(context, menu, null, false, com.android.internal.R.attr.popupMenuStyle, 0);
@@ -114,18 +114,15 @@ public class MenuPopupHelper implements ViewTreeObserver.OnGlobalLayoutListener,
return true;
}
- final View anchor = mAnchorView;
- if (anchor != null) {
- final boolean addGlobalListener = mTreeObserver == null;
- mTreeObserver = anchor.getViewTreeObserver(); // Refresh to latest
- if (addGlobalListener) mTreeObserver.addOnGlobalLayoutListener(this);
- anchor.addOnAttachStateChangeListener(this);
- mPopup.setAnchorView(anchor);
- mPopup.setGravity(mDropDownGravity);
- } else {
+ if (mAnchorView == null) {
return false;
}
+ mPopup = createMenuPopup();
+ mPopup.setAnchorView(mAnchorView);
+ mPopup.setGravity(mDropDownGravity);
+ mPopup.setCallback(mPresenterCallback);
+
// In order for subclasses of MenuPopupHelper to satisfy the OnDismissedListener interface,
// we must set the listener to this outer Helper rather than to the inner MenuPopup.
// Not to worry -- the inner MenuPopup will call our own #onDismiss method after it's done
@@ -146,45 +143,15 @@ public class MenuPopupHelper implements ViewTreeObserver.OnGlobalLayoutListener,
@Override
public void onDismiss() {
mPopup = null;
- if (mTreeObserver != null) {
- if (!mTreeObserver.isAlive()) mTreeObserver = mAnchorView.getViewTreeObserver();
- mTreeObserver.removeGlobalOnLayoutListener(this);
- mTreeObserver = null;
- }
- mAnchorView.removeOnAttachStateChangeListener(this);
}
public boolean isShowing() {
return mPopup != null && mPopup.isShowing();
}
- @Override
- public void onGlobalLayout() {
- if (isShowing()) {
- final View anchor = mAnchorView;
- if (anchor == null || !anchor.isShown()) {
- dismiss();
- } else if (isShowing()) {
- // Recompute window size and position
- mPopup.show();
- }
- }
- }
-
- @Override
- public void onViewAttachedToWindow(View v) {
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- if (mTreeObserver != null) {
- if (!mTreeObserver.isAlive()) mTreeObserver = v.getViewTreeObserver();
- mTreeObserver.removeGlobalOnLayoutListener(this);
- }
- v.removeOnAttachStateChangeListener(this);
- }
public void setCallback(MenuPresenter.Callback cb) {
+ mPresenterCallback = cb;
mPopup.setCallback(cb);
}
}
diff --git a/core/java/com/android/internal/view/menu/StandardMenuPopup.java b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
index 9a30ffafb75d..8877f3d96a39 100644
--- a/core/java/com/android/internal/view/menu/StandardMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
@@ -9,7 +9,10 @@ import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.view.View.OnAttachStateChangeListener;
import android.view.View.OnKeyListener;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.view.ViewTreeObserver;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.MenuPopupWindow;
@@ -34,15 +37,53 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On
private final int mPopupMaxWidth;
private final int mPopupStyleAttr;
private final int mPopupStyleRes;
+ // The popup window is final in order to couple its lifecycle to the lifecycle of the
+ // StandardMenuPopup.
+ private final MenuPopupWindow mPopup;
+
+ private final OnGlobalLayoutListener mGlobalLayoutListener = new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ if (isShowing()) {
+ final View anchor = mShownAnchorView;
+ if (anchor == null || !anchor.isShown()) {
+ dismiss();
+ } else if (isShowing()) {
+ // Recompute window size and position
+ mPopup.show();
+ }
+ }
+ }
+ };
+
+ private final OnAttachStateChangeListener mAttachStateChangeListener =
+ new OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ if (mTreeObserver != null) {
+ if (!mTreeObserver.isAlive()) mTreeObserver = v.getViewTreeObserver();
+ mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
+ }
+ v.removeOnAttachStateChangeListener(this);
+ }
+ };
private PopupWindow.OnDismissListener mOnDismissListener;
private View mAnchorView;
- private MenuPopupWindow mPopup;
+ private View mShownAnchorView;
private Callback mPresenterCallback;
+ private ViewTreeObserver mTreeObserver;
private ViewGroup mMeasureParent;
+ /** Whether the popup has been dismissed. Once dismissed, it cannot be opened again. */
+ private boolean mWasDismissed;
+
/** Whether the cached content width value is valid. */
private boolean mHasContentWidth;
@@ -88,18 +129,26 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On
return true;
}
+ if (mWasDismissed || mAnchorView == null) {
+ return false;
+ }
+
+ mShownAnchorView = mAnchorView;
+
mPopup.setOnDismissListener(this);
mPopup.setOnItemClickListener(this);
mPopup.setAdapter(mAdapter);
mPopup.setModal(true);
- final View anchor = mAnchorView;
- if (anchor != null) {
- mPopup.setAnchorView(anchor);
- mPopup.setDropDownGravity(mDropDownGravity);
- } else {
- return false;
+ final View anchor = mShownAnchorView;
+ final boolean addGlobalListener = mTreeObserver == null;
+ mTreeObserver = anchor.getViewTreeObserver(); // Refresh to latest
+ if (addGlobalListener) {
+ mTreeObserver.addOnGlobalLayoutListener(mGlobalLayoutListener);
}
+ anchor.addOnAttachStateChangeListener(mAttachStateChangeListener);
+ mPopup.setAnchorView(anchor);
+ mPopup.setDropDownGravity(mDropDownGravity);
if (!mHasContentWidth) {
mContentWidth = measureIndividualMenuWidth(
@@ -141,14 +190,20 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On
@Override
public boolean isShowing() {
- return mPopup != null && mPopup.isShowing();
+ return !mWasDismissed && mPopup.isShowing();
}
@Override
public void onDismiss() {
- mPopup = null;
+ mWasDismissed = true;
mMenu.close();
+ if (mTreeObserver != null) {
+ if (!mTreeObserver.isAlive()) mTreeObserver = mShownAnchorView.getViewTreeObserver();
+ mTreeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
+ mTreeObserver = null;
+ }
+ mShownAnchorView.removeOnAttachStateChangeListener(mAttachStateChangeListener);
mOnDismissListener.onDismiss();
}
@@ -170,19 +225,10 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On
public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
if (subMenu.hasVisibleItems()) {
MenuPopupHelper subPopup = new MenuPopupHelper(
- mContext, subMenu, mAnchorView, mOverflowOnly, mPopupStyleAttr, mPopupStyleRes);
+ mContext, subMenu, mShownAnchorView, mOverflowOnly, mPopupStyleAttr,
+ mPopupStyleRes);
subPopup.setCallback(mPresenterCallback);
-
- boolean preserveIconSpacing = false;
- final int count = subMenu.size();
- for (int i = 0; i < count; i++) {
- MenuItem childItem = subMenu.getItem(i);
- if (childItem.isVisible() && childItem.getIcon() != null) {
- preserveIconSpacing = true;
- break;
- }
- }
- subPopup.setForceShowIcon(preserveIconSpacing);
+ subPopup.setForceShowIcon(mAdapter.getForceShowIcon());
if (subPopup.tryShow()) {
if (mPresenterCallback != null) {
@@ -210,7 +256,6 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On
return false;
}
-
@Override
public Parcelable onSaveInstanceState() {
return null;
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 6a98ec70f92e..f7e9add58f21 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1075,12 +1075,22 @@ public class LockPatternUtils {
* enter a pattern.
*/
public long getLockoutAttemptDeadline(int userId) {
- final long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L, userId);
+ long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L, userId);
final long timeoutMs = getLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, 0L, userId);
final long now = SystemClock.elapsedRealtime();
- if (deadline < now || deadline > (now + timeoutMs)) {
+ if (deadline < now) {
+ // timeout expired
+ setLong(LOCKOUT_ATTEMPT_DEADLINE, 0, userId);
+ setLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, 0, userId);
return 0L;
}
+
+ if (deadline > (now + timeoutMs)) {
+ // device was rebooted, set new deadline
+ deadline = now + timeoutMs;
+ setLong(LOCKOUT_ATTEMPT_DEADLINE, deadline, userId);
+ }
+
return deadline;
}
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index b9e48a0c0cc8..80de52611735 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -241,17 +241,16 @@ static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached,
BufferQueue::createBufferQueue(&producer, &consumer);
if (singleBufferMode) {
- consumer->disableAsyncBuffer();
- consumer->setDefaultMaxBufferCount(1);
+ consumer->setMaxBufferCount(1);
}
sp<GLConsumer> surfaceTexture;
if (isDetached) {
surfaceTexture = new GLConsumer(consumer, GL_TEXTURE_EXTERNAL_OES,
- true, true);
+ true, !singleBufferMode);
} else {
surfaceTexture = new GLConsumer(consumer, texName,
- GL_TEXTURE_EXTERNAL_OES, true, true);
+ GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode);
}
if (surfaceTexture == 0) {
diff --git a/core/res/res/layout-land/time_picker_material.xml b/core/res/res/layout-land/time_picker_material.xml
index 2473e875bf65..bb347cb249b8 100644
--- a/core/res/res/layout-land/time_picker_material.xml
+++ b/core/res/res/layout-land/time_picker_material.xml
@@ -46,7 +46,8 @@
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_centerInParent="true"
- android:paddingTop="@dimen/timepicker_radial_picker_top_margin">
+ android:paddingTop="@dimen/timepicker_radial_picker_top_margin"
+ android:layout_marginBottom="-12dp">
<!-- The hour should always be to the left of the separator,
regardless of the current locale's layout direction. -->
@@ -57,14 +58,16 @@
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="right" />
+ android:gravity="right"
+ android:includeFontPadding="false" />
<TextView
android:id="@+id/separator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
- android:importantForAccessibility="no" />
+ android:importantForAccessibility="no"
+ android:includeFontPadding="false" />
<!-- The minutes should always be to the right of the separator,
regardless of the current locale's layout direction. -->
@@ -75,7 +78,8 @@
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="left" />
+ android:gravity="left"
+ android:includeFontPadding="false" />
</LinearLayout>
<!-- The layout alignment of this view will switch between toRightOf
@@ -93,22 +97,27 @@
android:id="@+id/am_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:minHeight="48dp"
+ android:minWidth="48dp"
+ android:gravity="bottom"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
- android:paddingTop="@dimen/timepicker_am_top_padding"
+ android:paddingTop="4dp"
+ android:paddingBottom="6dp"
android:lines="1"
- android:ellipsize="none"
- android:includeFontPadding="false" />
+ android:ellipsize="none" />
<CheckedTextView
android:id="@+id/pm_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:minHeight="48dp"
+ android:minWidth="48dp"
+ android:gravity="top"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
- android:paddingTop="@dimen/timepicker_pm_top_padding"
android:lines="1"
android:ellipsize="none"
android:includeFontPadding="false" />
diff --git a/core/res/res/layout/time_picker_header_material.xml b/core/res/res/layout/time_picker_header_material.xml
index 3c3a8a8ff3ed..acdc5090f41e 100644
--- a/core/res/res/layout/time_picker_header_material.xml
+++ b/core/res/res/layout/time_picker_header_material.xml
@@ -78,6 +78,9 @@
android:id="@+id/am_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:minWidth="48dp"
+ android:minHeight="48dp"
+ android:gravity="bottom"
android:paddingTop="@dimen/timepicker_am_top_padding"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:lines="1"
@@ -86,6 +89,9 @@
android:id="@+id/pm_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:minWidth="48dp"
+ android:minHeight="48dp"
+ android:gravity="top"
android:paddingTop="@dimen/timepicker_pm_top_padding"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:lines="1"
diff --git a/core/tests/BTtraffic/Android.mk b/core/tests/BTtraffic/Android.mk
new file mode 100644
index 000000000000..7d8352717a34
--- /dev/null
+++ b/core/tests/BTtraffic/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_RESOURCE_DIR := \
+ $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := bttraffic
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/tests/BTtraffic/AndroidManifest.xml b/core/tests/BTtraffic/AndroidManifest.xml
new file mode 100644
index 000000000000..00d9707de2bf
--- /dev/null
+++ b/core/tests/BTtraffic/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.experimental.bttraffic" >
+
+ <uses-permission android:name="android.permission.BLUETOOTH"/>
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
+
+ <uses-sdk
+ android:minSdkVersion="18"
+ android:targetSdkVersion="18"
+ />
+ <application
+ android:allowBackup="false"
+ android:label="@string/app_name" >
+ <service
+ android:name=".BTtraffic"
+ android:enabled="true"
+ android:exported="true" >
+ </service>
+ </application>
+
+</manifest>
diff --git a/core/tests/BTtraffic/README b/core/tests/BTtraffic/README
new file mode 100644
index 000000000000..430488f656f9
--- /dev/null
+++ b/core/tests/BTtraffic/README
@@ -0,0 +1,45 @@
+This is a tool to generate classic Bluetooth traffic with specified period and package size.
+Together with the SvcMonitor, which will be called automatically in this android service, can be
+used to measure the CPU usage from the Java layer Bluetooth code and the underlying system service
+com.android.bluetooth.
+
+1. Server (Listener) - Client (Sender) model. Both run as an Android service.
+2. No pairing needed. Communicate via unsecured RFcomm. Client establishes the connection by
+providing the MAC addr of the server.
+3. Bluetooth has to be turned on on both side.
+4. Client can configure the traffic by specifying the transfer period and package size.
+5. A separate monitor process will be automatically forked and will be reading from /proc file
+system to calculate the cpu usage. The measurement is updated once per second.
+6. The monitor process (com.google.android.experimental.svcmonitor/.ScvMonitor) can be run as an
+independent service to measure cpu usage on any similarly configured tests (e.g. wifi, BLE). Refer
+to SvcMonitor's README for usage and details.
+
+Usage:
+To instal the test:
+On both the server and client device, install the 2 apk:
+$ adb install $OUT/system/app/bttraffic/bttraffic.apk
+$ adb install $OUT/system/app/svcmonitor/svcmonitor.apk
+
+To start the service on the SERVER side:
+$ adb shell am startservice -a start --ez ack true \
+com.google.android.experimental.bttraffic/.BTtraffic
+
+To start the service on the CLIENT side:
+$ adb shell am startservice -a start \
+-e addr "F8:A9:D0:A8:74:8E" --ei size 1000 --ei period 15 \
+com.google.android.experimental.bttraffic/.BTtraffic
+
+To stop the test:
+On either the server or client:
+$ adb shell am startservice -a stop \
+com.google.android.experimental.bttraffic/.BTtraffic
+
+To look at the data:
+$ adb logcat | grep bttraffic
+
+Options:
+-e addr: MAC addr of the server, in uppercase letter.
+--ei size: package size, unit: byte; default: 1024, MAX: 20MB
+--ei period: system sleep time between sending each package, unit: ms, default: 5000
+ ** if -1 is provided, client will only send the package once.
+--ez ack: whether acknowledge is required (true/false)
diff --git a/core/tests/BTtraffic/res/values/strings.xml b/core/tests/BTtraffic/res/values/strings.xml
new file mode 100644
index 000000000000..e70276e03647
--- /dev/null
+++ b/core/tests/BTtraffic/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+ <string name="app_name">Bluetooth Test</string>
+</resources>
diff --git a/core/tests/BTtraffic/src/com/android/google/experimental/bttraffic/BTtraffic.java b/core/tests/BTtraffic/src/com/android/google/experimental/bttraffic/BTtraffic.java
new file mode 100644
index 000000000000..286c0aa2915f
--- /dev/null
+++ b/core/tests/BTtraffic/src/com/android/google/experimental/bttraffic/BTtraffic.java
@@ -0,0 +1,328 @@
+package com.google.android.experimental.bttraffic;
+
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothServerSocket;
+import android.bluetooth.BluetoothSocket;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.Exception;
+import java.lang.Runtime;
+import java.lang.RuntimeException;
+import java.lang.Process;
+import java.nio.ByteBuffer;
+import java.util.Random;
+import java.util.Set;
+import java.util.UUID;
+
+public class BTtraffic extends Service {
+ public static final String TAG = "bttraffic";
+ static final String SERVICE_NAME = "bttraffic";
+ static final String SYS_SERVICE_NAME = "com.android.bluetooth";
+ static final UUID SERVICE_UUID = UUID.fromString("5e8945b0-1234-5432-a5e2-0800200c9a67");
+ volatile Thread mWorkerThread;
+ volatile boolean isShuttingDown = false;
+ volatile boolean isServer = false;
+
+ public BTtraffic() {}
+
+ static void safeClose(Closeable closeable) {
+ try {
+ closeable.close();
+ } catch (IOException e) {
+ Log.d(TAG, "Unable to close resource.\n");
+ }
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (intent == null) {
+ stopSelf();
+ return 0;
+ }
+ if ("stop".equals(intent.getAction())) {
+ stopService();
+ } else if ("start".equals(intent.getAction())) {
+ startWorker(intent);
+ } else {
+ Log.d(TAG, "unknown action: + " + intent.getAction());
+ }
+ return 0;
+ }
+
+ private void startWorker(Intent intent) {
+ if (mWorkerThread != null) {
+ Log.d(TAG, "worker thread already active");
+ return;
+ }
+ isShuttingDown = false;
+ String remoteAddr = intent.getStringExtra("addr");
+ Log.d(TAG, "startWorker: addr=" + remoteAddr);
+ Runnable worker =
+ remoteAddr == null
+ ? new ListenerRunnable(this, intent)
+ : new SenderRunnable(this, remoteAddr, intent);
+ isServer = remoteAddr == null ? true: false;
+ mWorkerThread = new Thread(worker, "BTtrafficWorker");
+ try {
+ startMonitor();
+ Log.d(TAG, "Monitor service started");
+ mWorkerThread.start();
+ Log.d(TAG, "Worker thread started");
+ } catch (Exception e) {
+ Log.d(TAG, "Failed to start service", e);
+ }
+ }
+
+ private void startMonitor()
+ throws Exception {
+ if (isServer) {
+ Log.d(TAG, "Start monitor on server");
+ String[] startmonitorCmd = {
+ "/system/bin/am",
+ "startservice",
+ "-a", "start",
+ "-e", "java", SERVICE_NAME,
+ "-e", "hal", SYS_SERVICE_NAME,
+ "com.google.android.experimental.svcmonitor/.SvcMonitor"
+ };
+ Process ps = new ProcessBuilder()
+ .command(startmonitorCmd)
+ .redirectErrorStream(true)
+ .start();
+ } else {
+ Log.d(TAG, "No need to start SvcMonitor on client");
+ }
+ }
+
+ private void stopMonitor()
+ throws Exception {
+ if (isServer) {
+ Log.d(TAG, "StopMonitor on server");
+ String[] stopmonitorCmd = {
+ "/system/bin/am",
+ "startservice",
+ "-a", "stop",
+ "com.google.android.experimental.svcmonitor/.SvcMonitor"
+ };
+ Process ps = new ProcessBuilder()
+ .command(stopmonitorCmd)
+ .redirectErrorStream(true)
+ .start();
+ } else {
+ Log.d(TAG, "No need to stop Svcmonitor on client");
+ }
+ }
+
+ public void stopService() {
+ if (mWorkerThread == null) {
+ Log.d(TAG, "no active thread");
+ return;
+ }
+
+ isShuttingDown = true;
+
+ try {
+ stopMonitor();
+ } catch (Exception e) {
+ Log.d(TAG, "Unable to stop SvcMonitor!", e);
+ }
+
+ if (Thread.currentThread() != mWorkerThread) {
+ mWorkerThread.interrupt();
+ Log.d(TAG, "Interrupting thread");
+ try {
+ mWorkerThread.join();
+ } catch (InterruptedException e) {
+ Log.d(TAG, "Unable to join thread!");
+ }
+ }
+
+ mWorkerThread = null;
+ stopSelf();
+ Log.d(TAG, "Service stopped");
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ public static class ListenerRunnable implements Runnable {
+ private final BTtraffic bttraffic;
+ private final boolean sendAck;
+ private Intent intent;
+ private final int maxbuffersize = 20 * 1024 * 1024;
+
+ public ListenerRunnable(BTtraffic bttraffic, Intent intent) {
+ this.bttraffic = bttraffic;
+ this.sendAck = intent.getBooleanExtra("ack", true);
+ this.intent = intent;
+ }
+
+ @Override
+ public void run() {
+ BluetoothServerSocket serverSocket;
+
+ try {
+ Log.d(TAG, "getting server socket");
+ serverSocket = BluetoothAdapter.getDefaultAdapter()
+ .listenUsingInsecureRfcommWithServiceRecord(
+ SERVICE_NAME, SERVICE_UUID);
+ } catch (IOException e) {
+ Log.d(TAG, "error creating server socket, stopping thread");
+ bttraffic.stopService();
+ return;
+ }
+
+ Log.d(TAG, "got server socket, starting accept loop");
+ BluetoothSocket socket = null;
+ try {
+ Log.d(TAG, "accepting");
+ socket = serverSocket.accept();
+
+ if (!Thread.interrupted()) {
+ Log.d(TAG, "accepted, listening");
+ doListening(socket.getInputStream(), socket.getOutputStream());
+ Log.d(TAG, "listen finished");
+ }
+ } catch (IOException e) {
+ Log.d(TAG, "error while accepting or listening", e);
+ } finally {
+ Log.d(TAG, "Linster interruped");
+ Log.d(TAG, "closing socket and stopping service");
+ safeClose(serverSocket);
+ safeClose(socket);
+ if (!bttraffic.isShuttingDown)
+ bttraffic.stopService();
+ }
+
+ }
+
+ private void doListening(InputStream inputStream, OutputStream outputStream)
+ throws IOException {
+ ByteBuffer byteBuffer = ByteBuffer.allocate(maxbuffersize);
+
+ while (!Thread.interrupted()) {
+ readBytesIntoBuffer(inputStream, byteBuffer, 4);
+ byteBuffer.flip();
+ int length = byteBuffer.getInt();
+ if (Thread.interrupted())
+ break;
+ readBytesIntoBuffer(inputStream, byteBuffer, length);
+
+ if (sendAck)
+ outputStream.write(0x55);
+ }
+ }
+
+ void readBytesIntoBuffer(InputStream inputStream, ByteBuffer byteBuffer, int numToRead)
+ throws IOException {
+ byteBuffer.clear();
+ while (true) {
+ int position = byteBuffer.position();
+ int remaining = numToRead - position;
+ if (remaining == 0) {
+ break;
+ }
+ int count = inputStream.read(byteBuffer.array(), position, remaining);
+ if (count < 0) {
+ throw new IOException("read the EOF");
+ }
+ byteBuffer.position(position + count);
+ }
+ }
+ }
+
+ public static class SenderRunnable implements Runnable {
+ private final BTtraffic bttraffic;
+ private final String remoteAddr;
+ private final int pkgsize, period;
+ private final int defaultpkgsize = 1024;
+ private final int defaultperiod = 5000;
+ private static ByteBuffer lengthBuffer = ByteBuffer.allocate(4);
+
+ public SenderRunnable(BTtraffic bttraffic, String remoteAddr, Intent intent) {
+ this.bttraffic = bttraffic;
+ this.remoteAddr = remoteAddr;
+ this.pkgsize = intent.getIntExtra("size", defaultpkgsize);
+ this.period = intent.getIntExtra("period", defaultperiod);
+ }
+
+ @Override
+ public void run() {
+ BluetoothDevice device = null;
+ try {
+ device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(remoteAddr);
+ } catch (IllegalArgumentException e) {
+ Log.d(TAG, "Invalid BT MAC address!\n");
+ }
+ if (device == null) {
+ Log.d(TAG, "can't find matching device, stopping thread and service");
+ bttraffic.stopService();
+ return;
+ }
+
+ BluetoothSocket socket = null;
+ try {
+ Log.d(TAG, "connecting to device with MAC addr: " + remoteAddr);
+ socket = device.createInsecureRfcommSocketToServiceRecord(SERVICE_UUID);
+ socket.connect();
+ Log.d(TAG, "connected, starting to send");
+ doSending(socket.getOutputStream());
+ Log.d(TAG, "send stopped, stopping service");
+ } catch (Exception e) {
+ Log.d(TAG, "error while sending", e);
+ } finally {
+ Log.d(TAG, "finishing, closing thread and service");
+ safeClose(socket);
+ if (!bttraffic.isShuttingDown)
+ bttraffic.stopService();
+ }
+ }
+
+ private void doSending(OutputStream outputStream) throws IOException {
+ Log.w(TAG, "doSending");
+ try {
+ Random random = new Random(System.currentTimeMillis());
+
+ byte[] bytes = new byte[pkgsize];
+ random.nextBytes(bytes);
+ while (!Thread.interrupted()) {
+ writeBytes(outputStream, bytes.length);
+ outputStream.write(bytes, 0, bytes.length);
+ if (period < 0)
+ break;
+ if (period == 0)
+ continue;
+
+ SystemClock.sleep(period);
+ }
+ Log.d(TAG, "Sender interrupted");
+ } catch (IOException e) {
+ Log.d(TAG, "doSending got error", e);
+ }
+ }
+
+ private static void writeBytes(OutputStream outputStream, int value) throws IOException {
+ lengthBuffer.putInt(value);
+ lengthBuffer.flip();
+ outputStream.write(lengthBuffer.array(), lengthBuffer.position(), lengthBuffer.limit());
+ }
+ }
+
+}
diff --git a/core/tests/SvcMonitor/Android.mk b/core/tests/SvcMonitor/Android.mk
new file mode 100644
index 000000000000..2b8045506f02
--- /dev/null
+++ b/core/tests/SvcMonitor/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_RESOURCE_DIR := \
+ $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := svcmonitor
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/tests/SvcMonitor/AndroidManifest.xml b/core/tests/SvcMonitor/AndroidManifest.xml
new file mode 100644
index 000000000000..de5a9bdaed41
--- /dev/null
+++ b/core/tests/SvcMonitor/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.experimental.svcmonitor" >
+
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
+
+ <uses-sdk
+ android:minSdkVersion="18"
+ android:targetSdkVersion="18"
+ />
+ <application
+ android:allowBackup="false"
+ android:label="@string/app_name" >
+ <service
+ android:name=".SvcMonitor"
+ android:enabled="true"
+ android:exported="true" >
+ </service>
+ </application>
+
+</manifest>
diff --git a/core/tests/SvcMonitor/README b/core/tests/SvcMonitor/README
new file mode 100644
index 000000000000..13a4380589b4
--- /dev/null
+++ b/core/tests/SvcMonitor/README
@@ -0,0 +1,27 @@
+This Android service measures CPU usage of a program and an underlying system service it relies on.
+An example of this would be an android app XYZ communicates to some other device via Bluetooth. The
+SvcMonitor service can monitor the CPU usage of XYZ and com.android.bluetooth.
+
+Usage:
+
+To start the service:
+$ adb shell am startservice -a start \
+-e java XYZ -e hal com.android.bluetooth \
+com.google.android.experimental.svcmonitor/.SvcMonitor
+
+To stop the service:
+$ adb shell am startservice -a stop \
+com.google.android.experimental.svcmonitor/.SvcMonitor
+
+To stop the service config:
+$ adb shell am startservice -a change \
+-e java NewName -e hal NewService \
+com.google.android.experimental.svcmonitor/.SvcMonitor
+
+To monitor the data:
+$ adb logcat | grep XYZ
+
+Options:
+-e java NameOfProgram: any running process’s name.
+-e hal NameOfSysService: name of the system service the previous process relies on.
+--ei period: period between each measurement (frequency). Unit: ms, Default:1000, Min: 100
diff --git a/core/tests/SvcMonitor/res/values/strings.xml b/core/tests/SvcMonitor/res/values/strings.xml
new file mode 100644
index 000000000000..e70276e03647
--- /dev/null
+++ b/core/tests/SvcMonitor/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+ <string name="app_name">Bluetooth Test</string>
+</resources>
diff --git a/core/tests/SvcMonitor/src/com/android/google/experimental/svcmoniter/SvcMonitor.java b/core/tests/SvcMonitor/src/com/android/google/experimental/svcmoniter/SvcMonitor.java
new file mode 100644
index 000000000000..a451445530cd
--- /dev/null
+++ b/core/tests/SvcMonitor/src/com/android/google/experimental/svcmoniter/SvcMonitor.java
@@ -0,0 +1,209 @@
+package com.google.android.experimental.svcmonitor;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.lang.Runnable;
+import java.lang.Thread;
+import java.util.Set;
+
+public class SvcMonitor extends Service {
+ public static final String TAG = "svcmonitor";
+ String javaProc, halProc;
+ volatile Thread tMonitor;
+ int period;
+
+ public SvcMonitor() {};
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (intent == null) {
+ stopSelf();
+ return 0;
+ }
+ Log.d(TAG, "Starting SvcMonitor");
+ if ("stop".equals(intent.getAction())) {
+ stopService();
+ } else if ("start".equals(intent.getAction())) {
+ startMonitor(intent);
+ } else if ("change".equals(intent.getAction())) {
+ changeConfig(intent);
+ } else {
+ Log.d(TAG, "unknown action: + " + intent.getAction());
+ }
+ return 0;
+ }
+
+ private void changeConfig(Intent intent) {
+ if (tMonitor == null) {
+ Log.d(TAG, "Service not active. Start service first");
+ return;
+ }
+ stopThread();
+ startMonitor(intent);
+ }
+
+ private void startMonitor(Intent intent) {
+ if (tMonitor != null) {
+ Log.d(TAG, "thread already active");
+ return;
+ }
+ javaProc = intent.getStringExtra("java");
+ halProc = intent.getStringExtra("hal");
+ period = intent.getIntExtra("period", 1000);
+ if (javaProc == null || halProc == null || period < 100) {
+ Log.d(TAG, "Failed starting monitor, invalid arguments.");
+ stopSelf();
+ return;
+ }
+ Runnable monitor = new MonitorRunnable(this);
+ tMonitor = new Thread(monitor);
+ tMonitor.start();
+ }
+
+ private void stopService() {
+ stopThread();
+ stopSelf();
+ Log.d(TAG, "SvcMonitor stopped");
+ }
+
+ private void stopThread() {
+ if (tMonitor == null) {
+ Log.d(TAG, "no active thread");
+ return;
+ }
+ Log.d(TAG, "interrupting monitor thread");
+ tMonitor.interrupt();
+ try {
+ tMonitor.join();
+ } catch (InterruptedException e) {
+ Log.d(TAG, "Unable to finish monitor thread");
+ }
+ tMonitor = null;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ public static class MonitorRunnable implements Runnable {
+ long java_time_old, hal_time_old, cpu_time_old = -1;
+ String javaPID, halPID;
+ SvcMonitor svcmonitor;
+ static String javaProcTAG;
+ int period;
+
+ public MonitorRunnable(SvcMonitor svcmonitor) {
+ this.svcmonitor = svcmonitor;
+ this.period = svcmonitor.period;
+ javaPID = getPIDof(svcmonitor.javaProc);
+ halPID = getPIDof(svcmonitor.halProc);
+ java_time_old = getPsTime(javaPID);
+ hal_time_old = getPsTime(halPID);
+ cpu_time_old = getPsTime("");
+ javaProcTAG = String.valueOf(svcmonitor.javaProc.toCharArray());
+ }
+
+ @Override
+ public void run() {
+ if (halPID.isEmpty() || javaPID.isEmpty()) {
+ Log.d(javaProcTAG, "No such process: " +
+ (halPID.isEmpty() ? svcmonitor.halProc : svcmonitor.javaProc));
+ return;
+ }
+ while (!Thread.interrupted()) {
+ calculateUsage();
+ SystemClock.sleep(period);
+ }
+ Log.d(TAG, "Stopping monitor thread");
+ }
+
+ private void calculateUsage() {
+ long java_time = getPsTime(javaPID);
+ long hal_time = getPsTime(halPID);
+ long cpu_time = getPsTime("");
+
+ if (cpu_time_old >= 0) {
+ float java_diff = (float) (java_time - java_time_old);
+ float hal_diff = (float) (hal_time - hal_time_old);
+ float cpu_diff = (float) (cpu_time - cpu_time_old);
+ Log.w(javaProcTAG, "\n----------------\n");
+ Log.w(javaProcTAG, "JAVA level CPU: "
+ + (java_diff * 100.0 / cpu_diff) + "%\n");
+ Log.w(javaProcTAG, " HAL level CPU: "
+ + (hal_diff * 100.0 / cpu_diff) + "%\n");
+ Log.w(javaProcTAG, " SYS level CPU: "
+ + ((java_diff + hal_diff) * 100.0 / cpu_diff) + "%\n");
+ } else {
+ Log.w(TAG, "Waiting for status\n");
+ }
+
+ java_time_old = java_time;
+ hal_time_old = hal_time;
+ cpu_time_old = cpu_time;
+ }
+
+ private String getPIDof(String psName) {
+ String pid = "";
+
+ try {
+ String[] cmd = {"/system/bin/sh", "-c", "ps | grep " + psName};
+ Process ps = Runtime.getRuntime().exec(cmd);
+ BufferedReader in = new BufferedReader(
+ new InputStreamReader(ps.getInputStream()));
+ String temp = in.readLine();
+ if (temp == null || temp.isEmpty())
+ throw new IOException("No such process: " + psName);
+ pid = temp.split(" +")[1];
+ in.close();
+ } catch (IOException e) {
+ Log.d(javaProcTAG, "Error finding PID of process: " + psName + "\n", e);
+ }
+ return pid;
+ }
+
+ private long getPsTime(String pid) {
+ String psStat = getPsStat("/" + pid);
+ String[] statBreakDown = psStat.split(" +");
+ long psTime;
+
+ if (pid.isEmpty()) {
+ psTime = Long.parseLong(statBreakDown[1])
+ + Long.parseLong(statBreakDown[2])
+ + Long.parseLong(statBreakDown[3])
+ + Long.parseLong(statBreakDown[4]);
+ } else {
+ psTime = Long.parseLong(statBreakDown[13])
+ + Long.parseLong(statBreakDown[14]);
+ }
+
+ return psTime;
+ }
+
+ private String getPsStat(String psname) {
+ String stat = "";
+ try {
+ FileInputStream fs = new FileInputStream("/proc" + psname + "/stat");
+ BufferedReader br = new BufferedReader(new InputStreamReader(fs));
+ stat = br.readLine();
+ fs.close();
+ } catch (IOException e) {
+ Log.d(TAG, "Error retreiving stat. \n");
+ }
+ return stat;
+ }
+ }
+}
diff --git a/core/tests/coretests/assets/fonts/HintedAdvanceWidthTest-Regular.ttf b/core/tests/coretests/assets/fonts/HintedAdvanceWidthTest-Regular.ttf
new file mode 100644
index 000000000000..1924c35a9c0a
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/HintedAdvanceWidthTest-Regular.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts/HintedAdvanceWidthTest-Regular.ttx b/core/tests/coretests/assets/fonts/HintedAdvanceWidthTest-Regular.ttx
new file mode 100644
index 000000000000..2b8478d522d2
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/HintedAdvanceWidthTest-Regular.ttx
@@ -0,0 +1,328 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+ <GlyphOrder>
+ <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
+ <GlyphID id="0" name=".notdef"/>
+ <GlyphID id="1" name="space"/>
+ <GlyphID id="9" name="H"/>
+ <GlyphID id="16" name="O"/>
+ </GlyphOrder>
+
+ <head>
+ <!-- Most of this table will be recalculated by the compiler -->
+ <tableVersion value="1.0"/>
+ <fontRevision value="1.0"/>
+ <checkSumAdjustment value="0x640cdb2f"/>
+ <magicNumber value="0x5f0f3cf5"/>
+ <flags value="00000000 00000011"/>
+ <unitsPerEm value="1000"/>
+ <created value="Wed Sep 9 08:01:17 2015"/>
+ <modified value="Wed Sep 9 08:48:07 2015"/>
+ <xMin value="30"/>
+ <yMin value="-200"/>
+ <xMax value="629"/>
+ <yMax value="800"/>
+ <macStyle value="00000000 00000000"/>
+ <lowestRecPPEM value="7"/>
+ <fontDirectionHint value="2"/>
+ <indexToLocFormat value="0"/>
+ <glyphDataFormat value="0"/>
+ </head>
+
+ <hhea>
+ <tableVersion value="1.0"/>
+ <ascent value="1000"/>
+ <descent value="-200"/>
+ <lineGap value="0"/>
+ <advanceWidthMax value="659"/>
+ <minLeftSideBearing value="0"/>
+ <minRightSideBearing value="30"/>
+ <xMaxExtent value="629"/>
+ <caretSlopeRise value="1"/>
+ <caretSlopeRun value="0"/>
+ <caretOffset value="0"/>
+ <reserved0 value="0"/>
+ <reserved1 value="0"/>
+ <reserved2 value="0"/>
+ <reserved3 value="0"/>
+ <metricDataFormat value="0"/>
+ <numberOfHMetrics value="18"/>
+ </hhea>
+
+ <maxp>
+ <!-- Most of this table will be recalculated by the compiler -->
+ <tableVersion value="0x10000"/>
+ <numGlyphs value="54"/>
+ <maxPoints value="73"/>
+ <maxContours value="10"/>
+ <maxCompositePoints value="0"/>
+ <maxCompositeContours value="0"/>
+ <maxZones value="2"/>
+ <maxTwilightPoints value="12"/>
+ <maxStorage value="28"/>
+ <maxFunctionDefs value="119"/>
+ <maxInstructionDefs value="0"/>
+ <maxStackElements value="61"/>
+ <maxSizeOfInstructions value="2967"/>
+ <maxComponentElements value="0"/>
+ <maxComponentDepth value="0"/>
+ </maxp>
+
+ <OS_2>
+ <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+ will be recalculated by the compiler -->
+ <version value="3"/>
+ <xAvgCharWidth value="594"/>
+ <usWeightClass value="400"/>
+ <usWidthClass value="5"/>
+ <fsType value="00000000 00001000"/>
+ <ySubscriptXSize value="650"/>
+ <ySubscriptYSize value="600"/>
+ <ySubscriptXOffset value="0"/>
+ <ySubscriptYOffset value="75"/>
+ <ySuperscriptXSize value="650"/>
+ <ySuperscriptYSize value="600"/>
+ <ySuperscriptXOffset value="0"/>
+ <ySuperscriptYOffset value="350"/>
+ <yStrikeoutSize value="50"/>
+ <yStrikeoutPosition value="300"/>
+ <sFamilyClass value="0"/>
+ <panose>
+ <bFamilyType value="0"/>
+ <bSerifStyle value="0"/>
+ <bWeight value="5"/>
+ <bProportion value="0"/>
+ <bContrast value="0"/>
+ <bStrokeVariation value="0"/>
+ <bArmStyle value="0"/>
+ <bLetterForm value="0"/>
+ <bMidline value="0"/>
+ <bXHeight value="0"/>
+ </panose>
+ <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+ <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+ <achVendID value="UKWN"/>
+ <fsSelection value="00000000 01000000"/>
+ <usFirstCharIndex value="32"/>
+ <usLastCharIndex value="122"/>
+ <sTypoAscender value="800"/>
+ <sTypoDescender value="-200"/>
+ <sTypoLineGap value="200"/>
+ <usWinAscent value="1000"/>
+ <usWinDescent value="200"/>
+ <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+ <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+ <sxHeight value="500"/>
+ <sCapHeight value="700"/>
+ <usDefaultChar value="0"/>
+ <usBreakChar value="32"/>
+ <usMaxContext value="0"/>
+ </OS_2>
+
+ <hmtx>
+ <mtx name=".notdef" width="500" lsb="93"/>
+ <mtx name="H" width="627" lsb="50"/>
+ <mtx name="O" width="659" lsb="30"/>
+ <mtx name="space" width="300" lsb="0"/>
+ </hmtx>
+
+ <cmap>
+ <tableVersion version="0"/>
+ <cmap_format_4 platformID="0" platEncID="3" language="0">
+ <map code="0x20" name="space"/><!-- SPACE -->
+ <map code="0x48" name="H"/><!-- LATIN CAPITAL LETTER H -->
+ <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+ </cmap_format_4>
+ <cmap_format_4 platformID="3" platEncID="1" language="0">
+ <map code="0x20" name="space"/><!-- SPACE -->
+ <map code="0x48" name="H"/><!-- LATIN CAPITAL LETTER H -->
+ <map code="0x4f" name="O"/><!-- LATIN CAPITAL LETTER O -->
+ </cmap_format_4>
+ </cmap>
+
+ <fpgm>
+ <assembly>
+ </assembly>
+ </fpgm>
+
+ <prep>
+ <assembly>
+ </assembly>
+ </prep>
+
+ <cvt>
+ <cv index="0" value="0"/>
+ </cvt>
+
+ <loca>
+ <!-- The 'loca' table will be calculated by the compiler -->
+ </loca>
+
+ <glyf>
+
+ <!-- The xMin, yMin, xMax and yMax values
+ will be recalculated by the compiler. -->
+
+ <TTGlyph name=".notdef" xMin="93" yMin="-200" xMax="410" yMax="800">
+ <contour>
+ <pt x="410" y="0" on="1"/>
+ <pt x="50" y="0" on="1"/>
+ <pt x="50" y="700" on="1"/>
+ <pt x="410" y="700" on="1"/>
+ </contour>
+ <instructions><assembly>
+ PUSHB[] 1 2 4 3
+ SLOOP[]
+ PUSH[] -400
+ SHPIX[]
+
+ PUSHB[] 0 3 5 3
+ SLOOP[]
+ PUSH[] 400
+ SHPIX[]
+ </assembly></instructions>
+ </TTGlyph>
+
+ <TTGlyph name="H" xMin="50" yMin="0" xMax="577" yMax="700">
+ <contour>
+ <pt x="50" y="700" on="1"/>
+ <pt x="200" y="700" on="1"/>
+ <pt x="200" y="447" on="1"/>
+ <pt x="427" y="447" on="1"/>
+ <pt x="427" y="700" on="1"/>
+ <pt x="577" y="700" on="1"/>
+ <pt x="577" y="0" on="1"/>
+ <pt x="427" y="0" on="1"/>
+ <pt x="427" y="297" on="1"/>
+ <pt x="200" y="297" on="1"/>
+ <pt x="200" y="0" on="1"/>
+ <pt x="50" y="0" on="1"/>
+ </contour>
+ <instructions><assembly>
+ PUSHB[] 0 11 12 3
+ SLOOP[]
+ PUSH[] -200
+ SHPIX[]
+ PUSHB[] 5 6 13 3
+ SLOOP[]
+ PUSH[] 200
+ SHPIX[]
+ </assembly></instructions>
+ </TTGlyph>
+
+ <TTGlyph name="O" xMin="30" yMin="0" xMax="629" yMax="700">
+ <contour>
+ <pt x="248" y="0" on="0"/>
+ <pt x="111" y="94" on="0"/>
+ <pt x="30" y="255" on="0"/>
+ <pt x="30" y="350" on="1"/>
+ <pt x="30" y="445" on="0"/>
+ <pt x="111" y="606" on="0"/>
+ <pt x="248" y="700" on="0"/>
+ <pt x="330" y="700" on="1"/>
+ <pt x="411" y="700" on="0"/>
+ <pt x="548" y="606" on="0"/>
+ <pt x="629" y="445" on="0"/>
+ <pt x="629" y="350" on="1"/>
+ <pt x="629" y="255" on="0"/>
+ <pt x="548" y="94" on="0"/>
+ <pt x="411" y="0" on="0"/>
+ <pt x="330" y="0" on="1"/>
+ </contour>
+ <contour>
+ <pt x="370" y="150" on="0"/>
+ <pt x="439" y="209" on="0"/>
+ <pt x="480" y="302" on="0"/>
+ <pt x="480" y="350" on="1"/>
+ <pt x="480" y="398" on="0"/>
+ <pt x="439" y="491" on="0"/>
+ <pt x="370" y="550" on="0"/>
+ <pt x="330" y="550" on="1"/>
+ <pt x="289" y="550" on="0"/>
+ <pt x="220" y="491" on="0"/>
+ <pt x="179" y="398" on="0"/>
+ <pt x="179" y="350" on="1"/>
+ <pt x="179" y="302" on="0"/>
+ <pt x="220" y="209" on="0"/>
+ <pt x="289" y="150" on="0"/>
+ <pt x="330" y="150" on="1"/>
+ </contour>
+ <instructions><assembly>
+ PUSH[] 32 -200
+ SHPIX[]
+ PUSHB[] 33 200
+ SHPIX[]
+ </assembly></instructions>
+ </TTGlyph>
+
+ <TTGlyph name="space">
+ <contour></contour>
+ <instructions><assembly>
+ PUSH[] 0 -200
+ SHPIX[]
+ PUSHB[] 1 200
+ SHPIX[]
+ </assembly></instructions>
+ </TTGlyph>
+
+ </glyf>
+
+ <name>
+ <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Hinted Advance Width Test
+ </namerecord>
+ <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ Hinted Advance Width Test
+ </namerecord>
+ <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+ HintedAdvanceWidthTest-Regular
+ </namerecord>
+ <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+ Hinted Advance Width Test
+ </namerecord>
+ <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+ Regular
+ </namerecord>
+ <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+ Hinted Advance Width Test
+ </namerecord>
+ <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+ HintedAdvanceWidthTest-Regular
+ </namerecord>
+ </name>
+
+ <post>
+ <formatType value="2.0"/>
+ <italicAngle value="0.0"/>
+ <underlinePosition value="-75"/>
+ <underlineThickness value="50"/>
+ <isFixedPitch value="0"/>
+ <minMemType42 value="0"/>
+ <maxMemType42 value="0"/>
+ <minMemType1 value="0"/>
+ <maxMemType1 value="0"/>
+ <psNames>
+ <!-- This file uses unique glyph names based on the information
+ found in the 'post' table. Since these names might not be unique,
+ we have to invent artificial names in case of clashes. In order to
+ be able to retain the original information, we need a name to
+ ps name mapping for those cases where they differ. That's what
+ you see below.
+ -->
+ </psNames>
+ <extraNames>
+ <!-- following are the name that are not taken from the standard Mac glyph order -->
+ </extraNames>
+ </post>
+
+ <gasp>
+ <gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
+ </gasp>
+
+</ttFont>
diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java
new file mode 100644
index 000000000000..e97bb33487c4
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/PaintTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015 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 android.graphics;
+
+import android.graphics.Paint;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+ * PaintTest tests {@link Paint}.
+ */
+public class PaintTest extends InstrumentationTestCase {
+ private static final String FONT_PATH = "fonts/HintedAdvanceWidthTest-Regular.ttf";
+
+ static void assertEquals(String message, float[] expected, float[] actual) {
+ if (expected.length != actual.length) {
+ fail(message + " expected array length:<" + expected.length + "> but was:<"
+ + actual.length + ">");
+ }
+ for (int i = 0; i < expected.length; ++i) {
+ if (expected[i] != actual[i]) {
+ fail(message + " expected array element[" +i + "]:<" + expected[i] + ">but was:<"
+ + actual[i] + ">");
+ }
+ }
+ }
+
+ static class HintingTestCase {
+ public final String mText;
+ public final float mTextSize;
+ public final float[] mWidthWithoutHinting;
+ public final float[] mWidthWithHinting;
+
+ public HintingTestCase(String text, float textSize, float[] widthWithoutHinting,
+ float[] widthWithHinting) {
+ mText = text;
+ mTextSize = textSize;
+ mWidthWithoutHinting = widthWithoutHinting;
+ mWidthWithHinting = widthWithHinting;
+ }
+ }
+
+ // Following test cases are only valid for HintedAdvanceWidthTest-Regular.ttf in assets/fonts.
+ HintingTestCase[] HINTING_TESTCASES = {
+ new HintingTestCase("H", 11f, new float[] { 7f }, new float[] { 13f }),
+ new HintingTestCase("O", 11f, new float[] { 7f }, new float[] { 13f }),
+
+ new HintingTestCase("H", 13f, new float[] { 8f }, new float[] { 14f }),
+ new HintingTestCase("O", 13f, new float[] { 9f }, new float[] { 15f }),
+
+ new HintingTestCase("HO", 11f, new float[] { 7f, 7f }, new float[] { 13f, 13f }),
+ new HintingTestCase("OH", 11f, new float[] { 7f, 7f }, new float[] { 13f, 13f }),
+
+ new HintingTestCase("HO", 13f, new float[] { 8f, 9f }, new float[] { 14f, 15f }),
+ new HintingTestCase("OH", 13f, new float[] { 9f, 8f }, new float[] { 15f, 14f }),
+ };
+
+ @SmallTest
+ public void testHintingWidth() {
+ final Typeface fontTypeface = Typeface.createFromAsset(
+ getInstrumentation().getContext().getAssets(), FONT_PATH);
+ Paint paint = new Paint();
+ paint.setTypeface(fontTypeface);
+
+ for (int i = 0; i < HINTING_TESTCASES.length; ++i) {
+ HintingTestCase testCase = HINTING_TESTCASES[i];
+
+ paint.setTextSize(testCase.mTextSize);
+
+ float[] widths = new float[testCase.mText.length()];
+
+ paint.setHinting(Paint.HINTING_OFF);
+ paint.getTextWidths(String.valueOf(testCase.mText), widths);
+ assertEquals("Text width of '" + testCase.mText + "' without hinting is not expected.",
+ testCase.mWidthWithoutHinting, widths);
+
+ paint.setHinting(Paint.HINTING_ON);
+ paint.getTextWidths(String.valueOf(testCase.mText), widths);
+ assertEquals("Text width of '" + testCase.mText + "' with hinting is not expected.",
+ testCase.mWidthWithHinting, widths);
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/text/method/WordIteratorTest.java b/core/tests/coretests/src/android/text/method/WordIteratorTest.java
new file mode 100644
index 000000000000..37f887cd310e
--- /dev/null
+++ b/core/tests/coretests/src/android/text/method/WordIteratorTest.java
@@ -0,0 +1,458 @@
+/*
+ * Copyright (C) 2015 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 android.text.method;
+
+import android.test.AndroidTestCase;
+
+import java.text.BreakIterator;
+import java.util.Locale;
+
+// TODO(Bug: 24062099): Add more tests for non-ascii text.
+public class WordIteratorTest extends AndroidTestCase {
+
+ public void testSetCharSequence() {
+ final String text = "text";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+
+ try {
+ wordIterator.setCharSequence(text, 100, 100);
+ fail("setCharSequence with invalid start and end values should throw "
+ + "IndexOutOfBoundsException.");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ try {
+ wordIterator.setCharSequence(text, -100, -100);
+ fail("setCharSequence with invalid start and end values should throw "
+ + "IndexOutOfBoundsException.");
+ } catch (IndexOutOfBoundsException e) {
+ }
+
+ wordIterator.setCharSequence(text, 0, text.length());
+ wordIterator.setCharSequence(text, 0, 0);
+ wordIterator.setCharSequence(text, text.length(), text.length());
+ }
+
+ public void testPreceding() {
+ final String text = "abc def-ghi. jkl";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ try {
+ wordIterator.preceding(-1);
+ fail("preceding with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.preceding(text.length() + 1);
+ fail("preceding with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+
+ assertEquals(BreakIterator.DONE, wordIterator.preceding(text.indexOf('a')));
+ assertEquals(text.indexOf('a'), wordIterator.preceding(text.indexOf('c')));
+ assertEquals(text.indexOf('a'), wordIterator.preceding(text.indexOf('d')));
+ assertEquals(text.indexOf('d'), wordIterator.preceding(text.indexOf('e')));
+ assertEquals(text.indexOf('d'), wordIterator.preceding(text.indexOf('g')));
+ assertEquals(text.indexOf('g'), wordIterator.preceding(text.indexOf('h')));
+ assertEquals(text.indexOf('g'), wordIterator.preceding(text.indexOf('j')));
+ assertEquals(text.indexOf('j'), wordIterator.preceding(text.indexOf('l')));
+ }
+
+ public void testFollowing() {
+ final String text = "abc def-ghi. jkl";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ try {
+ wordIterator.following(-1);
+ fail("following with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.following(text.length() + 1);
+ fail("following with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+
+ assertEquals(text.indexOf('c') + 1, wordIterator.following(text.indexOf('a')));
+ assertEquals(text.indexOf('c') + 1, wordIterator.following(text.indexOf('c')));
+ assertEquals(text.indexOf('f') + 1, wordIterator.following(text.indexOf('c') + 1));
+ assertEquals(text.indexOf('f') + 1, wordIterator.following(text.indexOf('d')));
+ assertEquals(text.indexOf('i') + 1, wordIterator.following(text.indexOf('-')));
+ assertEquals(text.indexOf('i') + 1, wordIterator.following(text.indexOf('g')));
+ assertEquals(text.length(), wordIterator.following(text.indexOf('j')));
+ assertEquals(BreakIterator.DONE, wordIterator.following(text.length()));
+ }
+
+ public void testIsBoundary() {
+ final String text = "abc def-ghi. jkl";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ try {
+ wordIterator.isBoundary(-1);
+ fail("isBoundary with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.isBoundary(text.length() + 1);
+ fail("isBoundary with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+
+ assertTrue(wordIterator.isBoundary(text.indexOf('a')));
+ assertFalse(wordIterator.isBoundary(text.indexOf('b')));
+ assertTrue(wordIterator.isBoundary(text.indexOf('c') + 1));
+ assertTrue(wordIterator.isBoundary(text.indexOf('d')));
+ assertTrue(wordIterator.isBoundary(text.indexOf('-')));
+ assertTrue(wordIterator.isBoundary(text.indexOf('g')));
+ assertTrue(wordIterator.isBoundary(text.indexOf('.')));
+ assertTrue(wordIterator.isBoundary(text.indexOf('j')));
+ assertTrue(wordIterator.isBoundary(text.length()));
+ }
+
+ public void testNextBoundary() {
+ final String text = "abc def-ghi. jkl";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ try {
+ wordIterator.nextBoundary(-1);
+ fail("nextBoundary with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.nextBoundary(text.length() + 1);
+ fail("nextBoundary with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+
+
+ int currentOffset = 0;
+ currentOffset = wordIterator.nextBoundary(currentOffset);
+ assertEquals(text.indexOf('c') + 1, currentOffset);
+
+ currentOffset = wordIterator.nextBoundary(currentOffset);
+ assertEquals(text.indexOf('d'), currentOffset);
+
+ currentOffset = wordIterator.nextBoundary(currentOffset);
+ assertEquals(text.indexOf('f') + 1, currentOffset);
+
+ currentOffset = wordIterator.nextBoundary(currentOffset);
+ assertEquals(text.indexOf('g'), currentOffset);
+
+ currentOffset = wordIterator.nextBoundary(currentOffset);
+ assertEquals(text.indexOf('i') + 1, currentOffset);
+
+ currentOffset = wordIterator.nextBoundary(currentOffset);
+ assertEquals(text.indexOf('.') + 1, currentOffset);
+
+ currentOffset = wordIterator.nextBoundary(currentOffset);
+ assertEquals(text.indexOf('j'), currentOffset);
+
+ currentOffset = wordIterator.nextBoundary(currentOffset);
+ assertEquals(text.length(), currentOffset);
+
+ currentOffset = wordIterator.nextBoundary(currentOffset);
+ assertEquals(BreakIterator.DONE, currentOffset);
+ }
+
+ public void testPrevBoundary() {
+ final String text = "abc def-ghi. jkl";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ try {
+ wordIterator.prevBoundary(-1);
+ fail("prevBoundary with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.prevBoundary(text.length() + 1);
+ fail("prevBoundary with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+
+ int currentOffset = text.length();
+ currentOffset = wordIterator.prevBoundary(currentOffset);
+ assertEquals(text.indexOf('j'), currentOffset);
+
+ currentOffset = wordIterator.prevBoundary(currentOffset);
+ assertEquals(text.indexOf('.') + 1, currentOffset);
+
+ currentOffset = wordIterator.prevBoundary(currentOffset);
+ assertEquals(text.indexOf('i') + 1, currentOffset);
+
+ currentOffset = wordIterator.prevBoundary(currentOffset);
+ assertEquals(text.indexOf('g'), currentOffset);
+
+ currentOffset = wordIterator.prevBoundary(currentOffset);
+ assertEquals(text.indexOf('f') + 1, currentOffset);
+
+ currentOffset = wordIterator.prevBoundary(currentOffset);
+ assertEquals(text.indexOf('d'), currentOffset);
+
+ currentOffset = wordIterator.prevBoundary(currentOffset);
+ assertEquals(text.indexOf('c') + 1, currentOffset);
+
+ currentOffset = wordIterator.prevBoundary(currentOffset);
+ assertEquals(text.indexOf('a'), currentOffset);
+
+ currentOffset = wordIterator.prevBoundary(currentOffset);
+ assertEquals(BreakIterator.DONE, currentOffset);
+ }
+
+ public void testGetBeginning() {
+ {
+ final String text = "abc def-ghi. jkl";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+ try {
+ wordIterator.getBeginning(-1);
+ fail("getBeginning with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.getBeginning(text.length() + 1);
+ fail("getBeginning with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.getPrevWordBeginningOnTwoWordsBoundary(-1);
+ fail("getPrevWordBeginningOnTwoWordsBoundary with invalid offset should throw "
+ + "IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.length() + 1);
+ fail("getPrevWordBeginningOnTwoWordsBoundary with invalid offset should throw "
+ + "IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ {
+ final String text = "abc def-ghi. jkl";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ assertEquals(text.indexOf('a'), wordIterator.getBeginning(text.indexOf('a')));
+ assertEquals(text.indexOf('a'), wordIterator.getBeginning(text.indexOf('c')));
+ assertEquals(text.indexOf('a'), wordIterator.getBeginning(text.indexOf('c') + 1));
+ assertEquals(text.indexOf('d'), wordIterator.getBeginning(text.indexOf('d')));
+ assertEquals(text.indexOf('d'), wordIterator.getBeginning(text.indexOf('-')));
+ assertEquals(text.indexOf('g'), wordIterator.getBeginning(text.indexOf('g')));
+ assertEquals(text.indexOf('g'), wordIterator.getBeginning(text.indexOf('.')));
+ assertEquals(BreakIterator.DONE, wordIterator.getBeginning(text.indexOf('.') + 1));
+ assertEquals(text.indexOf('j'), wordIterator.getBeginning(text.indexOf('j')));
+ assertEquals(text.indexOf('j'), wordIterator.getBeginning(text.indexOf('l') + 1));
+
+ for (int i = 0; i < text.length(); i++) {
+ assertEquals(wordIterator.getBeginning(i),
+ wordIterator.getPrevWordBeginningOnTwoWordsBoundary(i));
+ }
+ }
+
+ {
+ // Japanese HIRAGANA letter + KATAKANA letters
+ final String text = "\u3042\u30A2\u30A3\u30A4";
+ WordIterator wordIterator = new WordIterator(Locale.JAPANESE);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ assertEquals(text.indexOf('\u3042'), wordIterator.getBeginning(text.indexOf('\u3042')));
+ assertEquals(text.indexOf('\u30A2'), wordIterator.getBeginning(text.indexOf('\u30A2')));
+ assertEquals(text.indexOf('\u30A2'), wordIterator.getBeginning(text.indexOf('\u30A4')));
+ assertEquals(text.indexOf('\u30A2'), wordIterator.getBeginning(text.length()));
+
+ assertEquals(text.indexOf('\u3042'),
+ wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('\u3042')));
+ assertEquals(text.indexOf('\u3042'),
+ wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('\u30A2')));
+ assertEquals(text.indexOf('\u30A2'),
+ wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.indexOf('\u30A4')));
+ assertEquals(text.indexOf('\u30A2'),
+ wordIterator.getPrevWordBeginningOnTwoWordsBoundary(text.length()));
+ }
+ }
+
+ public void testGetEnd() {
+ {
+ final String text = "abc def-ghi. jkl";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+ try {
+ wordIterator.getEnd(-1);
+ fail("getEnd with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.getEnd(text.length() + 1);
+ fail("getEnd with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.getNextWordEndOnTwoWordBoundary(-1);
+ fail("getNextWordEndOnTwoWordBoundary with invalid offset should throw "
+ + "IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.getNextWordEndOnTwoWordBoundary(text.length() + 1);
+ fail("getNextWordEndOnTwoWordBoundary with invalid offset should throw "
+ + "IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ {
+ final String text = "abc def-ghi. jkl";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ assertEquals(text.indexOf('c') + 1, wordIterator.getEnd(text.indexOf('a')));
+ assertEquals(text.indexOf('c') + 1, wordIterator.getEnd(text.indexOf('c')));
+ assertEquals(text.indexOf('c') + 1, wordIterator.getEnd(text.indexOf('c') + 1));
+ assertEquals(text.indexOf('f') + 1, wordIterator.getEnd(text.indexOf('d')));
+ assertEquals(text.indexOf('f') + 1, wordIterator.getEnd(text.indexOf('f') + 1));
+ assertEquals(text.indexOf('i') + 1, wordIterator.getEnd(text.indexOf('g')));
+ assertEquals(text.indexOf('i') + 1, wordIterator.getEnd(text.indexOf('i') + 1));
+ assertEquals(BreakIterator.DONE, wordIterator.getEnd(text.indexOf('.') + 1));
+ assertEquals(text.indexOf('l') + 1, wordIterator.getEnd(text.indexOf('j')));
+ assertEquals(text.indexOf('l') + 1, wordIterator.getEnd(text.indexOf('l') + 1));
+
+ for (int i = 0; i < text.length(); i++) {
+ assertEquals(wordIterator.getEnd(i),
+ wordIterator.getNextWordEndOnTwoWordBoundary(i));
+ }
+ }
+
+ {
+ // Japanese HIRAGANA letter + KATAKANA letters
+ final String text = "\u3042\u30A2\u30A3\u30A4";
+ WordIterator wordIterator = new WordIterator(Locale.JAPANESE);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ assertEquals(text.indexOf('\u3042') + 1, wordIterator.getEnd(text.indexOf('\u3042')));
+ assertEquals(text.indexOf('\u3042') + 1, wordIterator.getEnd(text.indexOf('\u30A2')));
+ assertEquals(text.indexOf('\u30A4') + 1, wordIterator.getEnd(text.indexOf('\u30A4')));
+ assertEquals(text.indexOf('\u30A4') + 1,
+ wordIterator.getEnd(text.indexOf('\u30A4') + 1));
+
+ assertEquals(text.indexOf('\u3042') + 1,
+ wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('\u3042')));
+ assertEquals(text.indexOf('\u30A4') + 1,
+ wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('\u30A2')));
+ assertEquals(text.indexOf('\u30A4') + 1,
+ wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('\u30A4')));
+ assertEquals(text.indexOf('\u30A4') + 1,
+ wordIterator.getNextWordEndOnTwoWordBoundary(text.indexOf('\u30A4') + 1));
+ }
+ }
+
+ public void testGetPunctuationBeginning() {
+ final String text = "abc!? (^^;) def";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ // TODO: Shouldn't this throw an exception?
+ assertEquals(BreakIterator.DONE, wordIterator.getPunctuationBeginning(BreakIterator.DONE));
+
+ try {
+ wordIterator.getPunctuationBeginning(-2);
+ fail("getPunctuationBeginning with invalid offset should throw "
+ + "IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.getPunctuationBeginning(text.length() + 1);
+ fail("getPunctuationBeginning with invalid offset should throw "
+ + "IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+
+ assertEquals(BreakIterator.DONE, wordIterator.getPunctuationBeginning(text.indexOf('a')));
+ assertEquals(BreakIterator.DONE, wordIterator.getPunctuationBeginning(text.indexOf('c')));
+ assertEquals(text.indexOf('!'), wordIterator.getPunctuationBeginning(text.indexOf('!')));
+ assertEquals(text.indexOf('!'),
+ wordIterator.getPunctuationBeginning(text.indexOf('?') + 1));
+ assertEquals(text.indexOf(';'), wordIterator.getPunctuationBeginning(text.indexOf(';')));
+ assertEquals(text.indexOf(';'), wordIterator.getPunctuationBeginning(text.indexOf(')')));
+ assertEquals(text.indexOf(';'), wordIterator.getPunctuationBeginning(text.length()));
+ }
+
+ public void testGetPunctuationEnd() {
+ final String text = "abc!? (^^;) def";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ // TODO: Shouldn't this throw an exception?
+ assertEquals(BreakIterator.DONE, wordIterator.getPunctuationEnd(BreakIterator.DONE));
+
+ try {
+ wordIterator.getPunctuationEnd(-2);
+ fail("getPunctuationEnd with invalid offset should throw IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ wordIterator.getPunctuationEnd(text.length() + 1);
+ fail("getPunctuationBeginning with invalid offset should throw "
+ + "IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {
+ }
+
+ assertEquals(text.indexOf('?') + 1, wordIterator.getPunctuationEnd(text.indexOf('a')));
+ assertEquals(text.indexOf('?') + 1, wordIterator.getPunctuationEnd(text.indexOf('?') + 1));
+ assertEquals(text.indexOf('(') + 1, wordIterator.getPunctuationEnd(text.indexOf('(')));
+ assertEquals(text.indexOf(')') + 1, wordIterator.getPunctuationEnd(text.indexOf('(') + 2));
+ assertEquals(text.indexOf(')') + 1, wordIterator.getPunctuationEnd(text.indexOf(')') + 1));
+ assertEquals(BreakIterator.DONE, wordIterator.getPunctuationEnd(text.indexOf('d')));
+ assertEquals(BreakIterator.DONE, wordIterator.getPunctuationEnd(text.length()));
+ }
+
+ public void testIsAfterPunctuation() {
+ final String text = "abc!? (^^;) def";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ assertFalse(wordIterator.isAfterPunctuation(text.indexOf('a')));
+ assertFalse(wordIterator.isAfterPunctuation(text.indexOf('!')));
+ assertTrue(wordIterator.isAfterPunctuation(text.indexOf('?')));
+ assertTrue(wordIterator.isAfterPunctuation(text.indexOf('?') + 1));
+ assertFalse(wordIterator.isAfterPunctuation(text.indexOf('d')));
+
+ assertFalse(wordIterator.isAfterPunctuation(BreakIterator.DONE));
+ assertFalse(wordIterator.isAfterPunctuation(text.length() + 1));
+ }
+
+ public void testIsOnPunctuation() {
+ final String text = "abc!? (^^;) def";
+ WordIterator wordIterator = new WordIterator(Locale.ENGLISH);
+ wordIterator.setCharSequence(text, 0, text.length());
+
+ assertFalse(wordIterator.isOnPunctuation(text.indexOf('a')));
+ assertTrue(wordIterator.isOnPunctuation(text.indexOf('!')));
+ assertTrue(wordIterator.isOnPunctuation(text.indexOf('?')));
+ assertFalse(wordIterator.isOnPunctuation(text.indexOf('?') + 1));
+ assertTrue(wordIterator.isOnPunctuation(text.indexOf(')')));
+ assertFalse(wordIterator.isOnPunctuation(text.indexOf(')') + 1));
+ assertFalse(wordIterator.isOnPunctuation(text.indexOf('d')));
+
+ assertFalse(wordIterator.isOnPunctuation(BreakIterator.DONE));
+ assertFalse(wordIterator.isOnPunctuation(text.length()));
+ assertFalse(wordIterator.isOnPunctuation(text.length() + 1));
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 54117df35a01..b37688f0e212 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -16,8 +16,10 @@
package android.widget;
+import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.pressKey;
import static android.support.test.espresso.action.ViewActions.typeTextIntoFocusedView;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
@@ -27,6 +29,7 @@ import com.android.frameworks.coretests.R;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
+import android.view.KeyEvent;
/**
* Tests the TextView widget from an Activity
@@ -47,4 +50,17 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV
onView(withId(R.id.textview)).check(matches(withText(helloWorld)));
}
+
+ @SmallTest
+ public void testPositionCursorAtTextAtIndex() throws Exception {
+ getActivity();
+
+ final String helloWorld = "Hello world!";
+ onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+ onView(withId(R.id.textview)).perform(clickOnTextAtIndex(helloWorld.indexOf("world")));
+
+ // Delete text at specified index and see if we got the right one.
+ onView(withId(R.id.textview)).perform(pressKey(KeyEvent.KEYCODE_FORWARD_DEL));
+ onView(withId(R.id.textview)).check(matches(withText("Hello orld!")));
+ }
}
diff --git a/core/tests/coretests/src/android/widget/TextViewWordLimitsTest.java b/core/tests/coretests/src/android/widget/TextViewWordLimitsTest.java
deleted file mode 100644
index 4b6616430e43..000000000000
--- a/core/tests/coretests/src/android/widget/TextViewWordLimitsTest.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2010 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 android.widget;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.Suppress;
-import android.text.InputType;
-import android.text.Selection;
-import android.text.Spannable;
-import android.text.SpannableString;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-/**
- * TextViewPatchTest tests {@link TextView}'s definition of word. Finds and
- * verifies word limits to be in strings containing different kinds of
- * characters.
- */
-@Suppress // Failing.
-public class TextViewWordLimitsTest extends AndroidTestCase {
-
- TextView mTv = null;
- Method mGetWordLimits, mSelectCurrentWord;
- Field mContextMenuTriggeredByKey, mSelectionControllerEnabled;
-
-
- /**
- * Sets up common fields used in all test cases.
- * @throws NoSuchFieldException
- * @throws SecurityException
- */
- @Override
- protected void setUp() throws NoSuchMethodException, SecurityException, NoSuchFieldException {
- mTv = new TextView(getContext());
- mTv.setInputType(InputType.TYPE_CLASS_TEXT);
-
- mGetWordLimits = mTv.getClass().getDeclaredMethod("getWordLimitsAt",
- new Class[] {int.class});
- mGetWordLimits.setAccessible(true);
-
- mSelectCurrentWord = mTv.getClass().getDeclaredMethod("selectCurrentWord", new Class[] {});
- mSelectCurrentWord.setAccessible(true);
-
- mContextMenuTriggeredByKey = mTv.getClass().getDeclaredField("mContextMenuTriggeredByKey");
- mContextMenuTriggeredByKey.setAccessible(true);
- mSelectionControllerEnabled = mTv.getClass().getDeclaredField("mSelectionControllerEnabled");
- mSelectionControllerEnabled.setAccessible(true);
- }
-
- /**
- * Calculate and verify word limits. Depends on the TextView implementation.
- * Uses a private method and internal data representation.
- *
- * @param text Text to select a word from
- * @param pos Position to expand word around
- * @param correctStart Correct start position for the word
- * @param correctEnd Correct end position for the word
- * @throws InvocationTargetException
- * @throws IllegalAccessException
- * @throws IllegalArgumentException
- * @throws InvocationTargetException
- * @throws IllegalAccessException
- */
- private void verifyWordLimits(String text, int pos, int correctStart, int correctEnd)
- throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
- mTv.setText(text, TextView.BufferType.SPANNABLE);
-
- long limits = (Long)mGetWordLimits.invoke(mTv, new Object[] {new Integer(pos)});
- int actualStart = (int)(limits >>> 32);
- int actualEnd = (int)(limits & 0x00000000FFFFFFFFL);
- assertEquals(correctStart, actualStart);
- assertEquals(correctEnd, actualEnd);
- }
-
-
- private void verifySelectCurrentWord(Spannable text, int selectionStart, int selectionEnd, int correctStart,
- int correctEnd) throws InvocationTargetException, IllegalAccessException {
- mTv.setText(text, TextView.BufferType.SPANNABLE);
-
- Selection.setSelection((Spannable)mTv.getText(), selectionStart, selectionEnd);
- mContextMenuTriggeredByKey.setBoolean(mTv, true);
- mSelectionControllerEnabled.setBoolean(mTv, true);
- mSelectCurrentWord.invoke(mTv);
-
- assertEquals(correctStart, mTv.getSelectionStart());
- assertEquals(correctEnd, mTv.getSelectionEnd());
- }
-
-
- /**
- * Corner cases for string length.
- */
- @LargeTest
- public void testLengths() throws Exception {
- final String ONE_TWO = "one two";
- final String EMPTY = "";
- final String TOOLONG = "ThisWordIsTooLongToBeDefinedAsAWordInTheSenseUsedInTextView";
-
- // Select first word
- verifyWordLimits(ONE_TWO, 0, 0, 3);
- verifyWordLimits(ONE_TWO, 3, 0, 3);
-
- // Select last word
- verifyWordLimits(ONE_TWO, 4, 4, 7);
- verifyWordLimits(ONE_TWO, 7, 4, 7);
-
- // Empty string
- verifyWordLimits(EMPTY, 0, -1, -1);
-
- // Too long word
- verifyWordLimits(TOOLONG, 0, -1, -1);
- }
-
- /**
- * Unicode classes included.
- */
- @LargeTest
- public void testIncludedClasses() throws Exception {
- final String LOWER = "njlj";
- final String UPPER = "NJLJ";
- final String TITLECASE = "\u01C8\u01CB\u01F2"; // Lj Nj Dz
- final String OTHER = "\u3042\u3044\u3046"; // Hiragana AIU
- final String MODIFIER = "\u02C6\u02CA\u02CB"; // Circumflex Acute Grave
-
- // Each string contains a single valid word
- verifyWordLimits(LOWER, 1, 0, 4);
- verifyWordLimits(UPPER, 1, 0, 4);
- verifyWordLimits(TITLECASE, 1, 0, 3);
- verifyWordLimits(OTHER, 1, 0, 3);
- verifyWordLimits(MODIFIER, 1, 0, 3);
- }
-
- /**
- * Unicode classes included if combined with a letter.
- */
- @LargeTest
- public void testPartlyIncluded() throws Exception {
- final String NUMBER = "123";
- final String NUMBER_LOWER = "1st";
- final String APOSTROPHE = "''";
- final String APOSTROPHE_LOWER = "'Android's'";
-
- // Pure decimal number is ignored
- verifyWordLimits(NUMBER, 1, -1, -1);
-
- // Number with letter is valid
- verifyWordLimits(NUMBER_LOWER, 1, 0, 3);
-
- // Stand apostrophes are ignore
- verifyWordLimits(APOSTROPHE, 1, -1, -1);
-
- // Apostrophes are accepted if they are a part of a word
- verifyWordLimits(APOSTROPHE_LOWER, 1, 0, 11);
- }
-
- /**
- * Unicode classes included if combined with a letter.
- */
- @LargeTest
- public void testFinalSeparator() throws Exception {
- final String PUNCTUATION = "abc, def.";
-
- // Starting from the comma
- verifyWordLimits(PUNCTUATION, 3, 0, 3);
- verifyWordLimits(PUNCTUATION, 4, 0, 4);
-
- // Starting from the final period
- verifyWordLimits(PUNCTUATION, 8, 5, 8);
- verifyWordLimits(PUNCTUATION, 9, 5, 9);
- }
-
- /**
- * Unicode classes other than listed in testIncludedClasses and
- * testPartlyIncluded act as word separators.
- */
- @LargeTest
- public void testNotIncluded() throws Exception {
- // Selection of character classes excluded
- final String MARK_NONSPACING = "a\u030A"; // a Combining ring above
- final String PUNCTUATION_OPEN_CLOSE = "(c)"; // Parenthesis
- final String PUNCTUATION_DASH = "non-fiction"; // Hyphen
- final String PUNCTUATION_OTHER = "b&b"; // Ampersand
- final String SYMBOL_OTHER = "Android\u00AE"; // Registered
- final String SEPARATOR_SPACE = "one two"; // Space
-
- // "a"
- verifyWordLimits(MARK_NONSPACING, 1, 0, 1);
-
- // "c"
- verifyWordLimits(PUNCTUATION_OPEN_CLOSE, 1, 1, 2);
-
- // "non-"
- verifyWordLimits(PUNCTUATION_DASH, 3, 0, 3);
- verifyWordLimits(PUNCTUATION_DASH, 4, 4, 11);
-
- // "b"
- verifyWordLimits(PUNCTUATION_OTHER, 0, 0, 1);
- verifyWordLimits(PUNCTUATION_OTHER, 1, 0, 1);
- verifyWordLimits(PUNCTUATION_OTHER, 2, 0, 3); // & is considered a punctuation sign.
- verifyWordLimits(PUNCTUATION_OTHER, 3, 2, 3);
-
- // "Android"
- verifyWordLimits(SYMBOL_OTHER, 7, 0, 7);
- verifyWordLimits(SYMBOL_OTHER, 8, -1, -1);
-
- // "one"
- verifyWordLimits(SEPARATOR_SPACE, 1, 0, 3);
- }
-
- /**
- * Surrogate characters are treated as their code points.
- */
- @LargeTest
- public void testSurrogate() throws Exception {
- final String SURROGATE_LETTER = "\uD800\uDC00\uD800\uDC01\uD800\uDC02"; // Linear B AEI
- final String SURROGATE_SYMBOL = "\uD83D\uDE01\uD83D\uDE02\uD83D\uDE03"; // Three smileys
-
- // Letter Other is included even when coded as surrogate pairs
- verifyWordLimits(SURROGATE_LETTER, 0, 0, 6);
- verifyWordLimits(SURROGATE_LETTER, 1, 0, 6);
- verifyWordLimits(SURROGATE_LETTER, 2, 0, 6);
- verifyWordLimits(SURROGATE_LETTER, 3, 0, 6);
- verifyWordLimits(SURROGATE_LETTER, 4, 0, 6);
- verifyWordLimits(SURROGATE_LETTER, 5, 0, 6);
- verifyWordLimits(SURROGATE_LETTER, 6, 0, 6);
-
- // Not included classes are ignored even when coded as surrogate pairs
- verifyWordLimits(SURROGATE_SYMBOL, 0, -1, -1);
- verifyWordLimits(SURROGATE_SYMBOL, 1, -1, -1);
- verifyWordLimits(SURROGATE_SYMBOL, 2, -1, -1);
- verifyWordLimits(SURROGATE_SYMBOL, 3, -1, -1);
- verifyWordLimits(SURROGATE_SYMBOL, 4, -1, -1);
- verifyWordLimits(SURROGATE_SYMBOL, 5, -1, -1);
- verifyWordLimits(SURROGATE_SYMBOL, 6, -1, -1);
- }
-
- /**
- * Selection is used if present and valid word.
- */
- @LargeTest
- public void testSelectCurrentWord() throws Exception {
- SpannableString textLower = new SpannableString("first second");
- SpannableString textOther = new SpannableString("\u3042\3044\3046"); // Hiragana AIU
- SpannableString textDash = new SpannableString("non-fiction"); // Hyphen
- SpannableString textPunctOther = new SpannableString("b&b"); // Ampersand
- SpannableString textSymbolOther = new SpannableString("Android\u00AE"); // Registered
-
- // Valid selection - Letter, Lower
- verifySelectCurrentWord(textLower, 2, 5, 0, 5);
-
- // Adding the space spreads to the second word
- verifySelectCurrentWord(textLower, 2, 6, 0, 12);
-
- // Valid selection -- Letter, Other
- verifySelectCurrentWord(textOther, 1, 2, 0, 5);
-
- // Zero-width selection is interpreted as a cursor and the selection is ignored
- verifySelectCurrentWord(textLower, 2, 2, 0, 5);
-
- // Hyphen is part of selection
- verifySelectCurrentWord(textDash, 2, 5, 0, 11);
-
- // Ampersand part of selection or not
- verifySelectCurrentWord(textPunctOther, 1, 2, 0, 3);
- verifySelectCurrentWord(textPunctOther, 1, 3, 0, 3);
-
- // (R) part of the selection
- verifySelectCurrentWord(textSymbolOther, 2, 7, 0, 7);
- verifySelectCurrentWord(textSymbolOther, 2, 8, 0, 8);
- }
-}
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
new file mode 100644
index 000000000000..425dccdf4936
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2015 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 android.widget.espresso;
+
+import static android.support.test.espresso.action.ViewActions.actionWithAssertions;
+
+import android.content.res.Resources;
+import android.support.test.espresso.PerformException;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.action.CoordinatesProvider;
+import android.support.test.espresso.action.GeneralClickAction;
+import android.support.test.espresso.action.Press;
+import android.support.test.espresso.action.Tap;
+import android.support.test.espresso.util.HumanReadables;
+import android.text.Layout;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.TextView;
+
+/**
+ * A collection of actions on a {@link android.widget.TextView}.
+ */
+public final class TextViewActions {
+
+ private TextViewActions() {}
+
+ /**
+ * Returns an action that clicks on text at an index on the text view.<br>
+ * <br>
+ * View constraints:
+ * <ul>
+ * <li>must be a text view displayed on screen
+ * <ul>
+ */
+ public static ViewAction clickOnTextAtIndex(int index) {
+ return actionWithAssertions(
+ new GeneralClickAction(Tap.SINGLE, new TextCoordinates(index), Press.FINGER));
+ }
+
+ /**
+ * A provider of the x, y coordinates of the text at the specified index in a text view.
+ */
+ private static final class TextCoordinates implements CoordinatesProvider {
+
+ private final int mIndex;
+ private final String mActionDescription;
+
+ public TextCoordinates(int index) {
+ mIndex = index;
+ mActionDescription = "Could not locate text at index: " + mIndex;
+ }
+
+ @Override
+ public float[] calculateCoordinates(View view) {
+ try {
+ return locateTextAtIndex((TextView) view, mIndex);
+ } catch (ClassCastException e) {
+ throw new PerformException.Builder()
+ .withActionDescription(mActionDescription)
+ .withViewDescription(HumanReadables.describe(view))
+ .withCause(e)
+ .build();
+ } catch (StringIndexOutOfBoundsException e) {
+ throw new PerformException.Builder()
+ .withActionDescription(mActionDescription)
+ .withViewDescription(HumanReadables.describe(view))
+ .withCause(e)
+ .build();
+ }
+ }
+
+ /**
+ * @throws StringIndexOutOfBoundsException
+ */
+ private float[] locateTextAtIndex(TextView textView, int index) {
+ if (index < 0 || index > textView.getText().length()) {
+ throw new StringIndexOutOfBoundsException(index);
+ }
+ final int[] xy = new int[2];
+ textView.getLocationOnScreen(xy);
+ final Layout layout = textView.getLayout();
+ final int line = layout.getLineForOffset(index);
+ final float x = textView.getTotalPaddingLeft() - textView.getScrollX()
+ + layout.getPrimaryHorizontal(index);
+ final float y = textView.getTotalPaddingTop() - textView.getScrollY()
+ + layout.getLineTop(line);
+ return new float[]{x + xy[0], y + xy[1]};
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
index 5e7f127fd4a6..c279c8f24362 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
@@ -1075,4 +1075,120 @@ public class InputMethodUtilsTest extends InstrumentationTestCase {
assertTrue(subtypes2.isEmpty());
}
}
+
+ @SmallTest
+ public void testbuildInputMethodsAndSubtypesString() {
+ {
+ ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+ assertEquals("", InputMethodUtils.buildInputMethodsAndSubtypesString(map));
+ }
+ {
+ ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+ map.put("ime0", new ArraySet<String>());
+ assertEquals("ime0", InputMethodUtils.buildInputMethodsAndSubtypesString(map));
+ }
+ {
+ ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+ ArraySet<String> subtypes1 = new ArraySet<>();
+ subtypes1.add("subtype0");
+ map.put("ime0", subtypes1);
+ assertEquals("ime0;subtype0", InputMethodUtils.buildInputMethodsAndSubtypesString(map));
+ }
+ {
+ ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+ ArraySet<String> subtypes1 = new ArraySet<>();
+ subtypes1.add("subtype0");
+ subtypes1.add("subtype1");
+ map.put("ime0", subtypes1);
+
+ // We do not expect what order will be used to concatenate items in
+ // InputMethodUtils.buildInputMethodsAndSubtypesString() hence enumerate all possible
+ // permutations here.
+ ArraySet<String> validSequences = new ArraySet<>();
+ validSequences.add("ime0;subtype0;subtype1");
+ validSequences.add("ime0;subtype1;subtype0");
+ assertTrue(validSequences.contains(
+ InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
+ }
+ {
+ ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+ map.put("ime0", new ArraySet<String>());
+ map.put("ime1", new ArraySet<String>());
+
+ ArraySet<String> validSequences = new ArraySet<>();
+ validSequences.add("ime0:ime1");
+ validSequences.add("ime1:ime0");
+ assertTrue(validSequences.contains(
+ InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
+ }
+ {
+ ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+ ArraySet<String> subtypes1 = new ArraySet<>();
+ subtypes1.add("subtype0");
+ map.put("ime0", subtypes1);
+ map.put("ime1", new ArraySet<String>());
+
+ ArraySet<String> validSequences = new ArraySet<>();
+ validSequences.add("ime0;subtype0:ime1");
+ validSequences.add("ime1;ime0;subtype0");
+ assertTrue(validSequences.contains(
+ InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
+ }
+ {
+ ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+ ArraySet<String> subtypes1 = new ArraySet<>();
+ subtypes1.add("subtype0");
+ subtypes1.add("subtype1");
+ map.put("ime0", subtypes1);
+ map.put("ime1", new ArraySet<String>());
+
+ ArraySet<String> validSequences = new ArraySet<>();
+ validSequences.add("ime0;subtype0;subtype1:ime1");
+ validSequences.add("ime0;subtype1;subtype0:ime1");
+ validSequences.add("ime1:ime0;subtype0;subtype1");
+ validSequences.add("ime1:ime0;subtype1;subtype0");
+ assertTrue(validSequences.contains(
+ InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
+ }
+ {
+ ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+ ArraySet<String> subtypes1 = new ArraySet<>();
+ subtypes1.add("subtype0");
+ map.put("ime0", subtypes1);
+
+ ArraySet<String> subtypes2 = new ArraySet<>();
+ subtypes2.add("subtype1");
+ map.put("ime1", subtypes2);
+
+ ArraySet<String> validSequences = new ArraySet<>();
+ validSequences.add("ime0;subtype0:ime1;subtype1");
+ validSequences.add("ime1;subtype1:ime0;subtype0");
+ assertTrue(validSequences.contains(
+ InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
+ }
+ {
+ ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
+ ArraySet<String> subtypes1 = new ArraySet<>();
+ subtypes1.add("subtype0");
+ subtypes1.add("subtype1");
+ map.put("ime0", subtypes1);
+
+ ArraySet<String> subtypes2 = new ArraySet<>();
+ subtypes2.add("subtype2");
+ subtypes2.add("subtype3");
+ map.put("ime1", subtypes2);
+
+ ArraySet<String> validSequences = new ArraySet<>();
+ validSequences.add("ime0;subtype0;subtype1:ime1;subtype2;subtype3");
+ validSequences.add("ime0;subtype1;subtype0:ime1;subtype2;subtype3");
+ validSequences.add("ime0;subtype0;subtype1:ime1;subtype3;subtype2");
+ validSequences.add("ime0;subtype1;subtype0:ime1;subtype3;subtype2");
+ validSequences.add("ime1;subtype2;subtype3:ime0;subtype0;subtype1");
+ validSequences.add("ime2;subtype3;subtype2:ime0;subtype0;subtype1");
+ validSequences.add("ime3;subtype2;subtype3:ime0;subtype1;subtype0");
+ validSequences.add("ime4;subtype3;subtype2:ime0;subtype1;subtype0");
+ assertTrue(validSequences.contains(
+ InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
+ }
+ }
}
diff --git a/docs/html/guide/topics/renderscript/reference/rs_for_each.jd b/docs/html/guide/topics/renderscript/reference/rs_for_each.jd
index abbbda78ba3a..9ba56149f142 100644
--- a/docs/html/guide/topics/renderscript/reference/rs_for_each.jd
+++ b/docs/html/guide/topics/renderscript/reference/rs_for_each.jd
@@ -205,7 +205,7 @@ locality when the processing is distributed over multiple cores.
<span class='normal'>: Handle to a kernel invocation context</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: const struct rs_kernel_context_t *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23</a>
+<p>A typedef of: const struct rs_kernel_context_t *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23</a>
</p>
<p> The kernel context contains common characteristics of the allocations being iterated
over, like dimensions. It also contains rarely used indices of the currently processed
diff --git a/docs/html/guide/topics/renderscript/reference/rs_graphics.jd b/docs/html/guide/topics/renderscript/reference/rs_graphics.jd
index 1b115fe9a793..b04451c7a526 100644
--- a/docs/html/guide/topics/renderscript/reference/rs_graphics.jd
+++ b/docs/html/guide/topics/renderscript/reference/rs_graphics.jd
@@ -502,7 +502,7 @@ page.title=RenderScript Graphics Functions and Types
</h4>
<div class='jd-details-descr'>
<p>An enum with the following values:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
- When compiling for 32 bits. <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16 - 22</a>
+When compiling for 32 bits. <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16 - 22</a>
</p>
<table class='jd-tagtable'><tbody>
<tr><th>RS_BLEND_DST_ZERO = 0</th><td></td></tr>
@@ -527,7 +527,7 @@ page.title=RenderScript Graphics Functions and Types
</h4>
<div class='jd-details-descr'>
<p>An enum with the following values:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
- When compiling for 32 bits. <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16 - 22</a>
+When compiling for 32 bits. <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16 - 22</a>
</p>
<table class='jd-tagtable'><tbody>
<tr><th>RS_BLEND_SRC_ZERO = 0</th><td></td></tr>
@@ -553,7 +553,7 @@ page.title=RenderScript Graphics Functions and Types
</h4>
<div class='jd-details-descr'>
<p>An enum with the following values:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
- When compiling for 32 bits. <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16 - 22</a>
+When compiling for 32 bits. <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16 - 22</a>
</p>
<table class='jd-tagtable'><tbody>
<tr><th>RS_CULL_BACK = 0</th><td></td></tr>
@@ -573,7 +573,7 @@ page.title=RenderScript Graphics Functions and Types
</h4>
<div class='jd-details-descr'>
<p>An enum with the following values:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
- When compiling for 32 bits. <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16 - 22</a>
+When compiling for 32 bits. <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16 - 22</a>
</p>
<table class='jd-tagtable'><tbody>
<tr><th>RS_DEPTH_FUNC_ALWAYS = 0</th><td>Always drawn</td></tr>
@@ -599,11 +599,7 @@ depth to that found in the depth buffer.
<span class='normal'>: Handle to a Font</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE __attribute__((
-#if (defined(RS_VERSION) && (RS_VERSION >= 1))
-deprecated
-#endif
-))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
+<p>When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
</p>
<p><b>Deprecated.</b> Do not use.</p>
<p> Opaque handle to a RenderScript font object.
@@ -619,11 +615,7 @@ See: android.renderscript.Font
<span class='normal'>: Handle to a Mesh</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE __attribute__((
-#if (defined(RS_VERSION) && (RS_VERSION >= 1))
-deprecated
-#endif
-))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
+<p>When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
</p>
<p><b>Deprecated.</b> Do not use.</p>
<p> Opaque handle to a RenderScript mesh object.
@@ -640,7 +632,7 @@ See: android.renderscript.Mesh
</h4>
<div class='jd-details-descr'>
<p>An enum with the following values:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
- When compiling for 32 bits. <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16 - 22</a>
+When compiling for 32 bits. <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16 - 22</a>
</p>
<table class='jd-tagtable'><tbody>
<tr><th>RS_PRIMITIVE_POINT = 0</th><td>Vertex data will be rendered as a series of points</td></tr>
@@ -664,11 +656,7 @@ See: android.renderscript.Mesh
<span class='normal'>: Handle to a ProgramFragment</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE __attribute__((
-#if (defined(RS_VERSION) && (RS_VERSION >= 1))
-deprecated
-#endif
-))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
+<p>When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
</p>
<p><b>Deprecated.</b> Do not use.</p>
<p> Opaque handle to a RenderScript ProgramFragment object.
@@ -684,11 +672,7 @@ See: android.renderscript.ProgramFragment
<span class='normal'>: Handle to a ProgramRaster</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE __attribute__((
-#if (defined(RS_VERSION) && (RS_VERSION >= 1))
-deprecated
-#endif
-))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
+<p>When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
</p>
<p><b>Deprecated.</b> Do not use.</p>
<p> Opaque handle to a RenderScript ProgramRaster object.
@@ -704,11 +688,7 @@ See: android.renderscript.ProgramRaster
<span class='normal'>: Handle to a ProgramStore</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE __attribute__((
-#if (defined(RS_VERSION) && (RS_VERSION >= 1))
-deprecated
-#endif
-))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
+<p>When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
</p>
<p><b>Deprecated.</b> Do not use.</p>
<p> Opaque handle to a RenderScript ProgramStore object.
@@ -724,11 +704,7 @@ See: android.renderscript.ProgramStore
<span class='normal'>: Handle to a ProgramVertex</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE __attribute__((
-#if (defined(RS_VERSION) && (RS_VERSION >= 1))
-deprecated
-#endif
-))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
+<p>When compiling for 32 bits. Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23 and higher</a>
</p>
<p><b>Deprecated.</b> Do not use.</p>
<p> Opaque handle to a RenderScript ProgramVertex object.
diff --git a/docs/html/guide/topics/renderscript/reference/rs_object_types.jd b/docs/html/guide/topics/renderscript/reference/rs_object_types.jd
index f3428966c7de..ac4745482d1f 100644
--- a/docs/html/guide/topics/renderscript/reference/rs_object_types.jd
+++ b/docs/html/guide/topics/renderscript/reference/rs_object_types.jd
@@ -99,7 +99,7 @@ elements, and scripts. Most of these object are created using the Java RenderSc
<span class='normal'>: Handle to an allocation</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
+<p></p>
<p> An opaque handle to a RenderScript allocation.
</p>
@@ -116,7 +116,7 @@ elements, and scripts. Most of these object are created using the Java RenderSc
</h4>
<div class='jd-details-descr'>
<p>An enum with the following values:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
- Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 14</a>
+Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 14</a>
</p>
<table class='jd-tagtable'><tbody>
<tr><th>RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X = 0</th><td></td></tr>
@@ -139,7 +139,7 @@ elements, and scripts. Most of these object are created using the Java RenderSc
</h4>
<div class='jd-details-descr'>
<p>An enum with the following values:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
- Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 14</a>
+Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 14</a>
</p>
<table class='jd-tagtable'><tbody>
<tr><th>RS_ALLOCATION_USAGE_SCRIPT = 0x0001</th><td>Allocation is bound to and accessed by scripts.</td></tr>
@@ -165,7 +165,7 @@ relevant to an allocation or an operation on an allocation.
</h4>
<div class='jd-details-descr'>
<p>An enum with the following values:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
- Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16</a>
+Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16</a>
</p>
<table class='jd-tagtable'><tbody>
<tr><th>RS_KIND_USER = 0</th><td>No special interpretation.</td></tr>
@@ -202,7 +202,7 @@ texture formats.
</h4>
<div class='jd-details-descr'>
<p>An enum with the following values:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
- Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16</a>
+Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16</a>
</p>
<table class='jd-tagtable'><tbody>
<tr><th>RS_TYPE_NONE = 0</th><td>Element is a complex type, i.e. a struct.</td></tr>
@@ -253,7 +253,7 @@ as a single unit for packing and alignment purposes.
<span class='normal'>: Handle to an element</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
+<p></p>
<p> An opaque handle to a RenderScript element.
</p>
@@ -269,7 +269,7 @@ as a single unit for packing and alignment purposes.
<span class='normal'>: Handle to a Sampler</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
+<p></p>
<p> An opaque handle to a RenderScript sampler object.
</p>
@@ -286,7 +286,7 @@ as a single unit for packing and alignment purposes.
</h4>
<div class='jd-details-descr'>
<p>An enum with the following values:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
- Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16</a>
+Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 16</a>
</p>
<table class='jd-tagtable'><tbody>
<tr><th>RS_SAMPLER_NEAREST = 0</th><td></td></tr>
@@ -308,7 +308,7 @@ as a single unit for packing and alignment purposes.
<span class='normal'>: Handle to a Script</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
+<p></p>
<p> An opaque handle to a RenderScript script object.
</p>
@@ -324,7 +324,7 @@ as a single unit for packing and alignment purposes.
<span class='normal'>: Handle to a Type</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: _RS_HANDLE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
+<p></p>
<p> An opaque handle to a RenderScript type.
</p>
diff --git a/docs/html/guide/topics/renderscript/reference/rs_time.jd b/docs/html/guide/topics/renderscript/reference/rs_time.jd
index 27044a3d3126..a1cedfb954db 100644
--- a/docs/html/guide/topics/renderscript/reference/rs_time.jd
+++ b/docs/html/guide/topics/renderscript/reference/rs_time.jd
@@ -78,9 +78,9 @@ system up time. It is not recommended to call these functions inside of a kerne
<span class='normal'>: Seconds since January 1, 1970</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; When compiling for 32 bits.
+<p>A typedef of: int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;When compiling for 32 bits.
</p>
-<p>A typedef of: long&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; When compiling for 64 bits.
+<p>A typedef of: long&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;When compiling for 64 bits.
</p>
<p> Calendar time interpreted as seconds elapsed since the Epoch (00:00:00 on
January 1, 1970, Coordinated Universal Time (UTC)).
diff --git a/docs/html/guide/topics/renderscript/reference/rs_value_types.jd b/docs/html/guide/topics/renderscript/reference/rs_value_types.jd
index d53caf92d74a..2bd49dc5cf7c 100644
--- a/docs/html/guide/topics/renderscript/reference/rs_value_types.jd
+++ b/docs/html/guide/topics/renderscript/reference/rs_value_types.jd
@@ -644,7 +644,7 @@ with a 128 bit alignment.
<span class='normal'>: 16 bit floating point value</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: __fp16&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23</a>
+<p>A typedef of: __fp16&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23</a>
</p>
<p> A 16 bit floating point value.
</p>
@@ -658,7 +658,7 @@ with a 128 bit alignment.
<span class='normal'>: Two 16 bit floats</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: half __attribute__((ext_vector_type(2)))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23</a>
+<p>A typedef of: half __attribute__((ext_vector_type(2)))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23</a>
</p>
<p> Vector version of the half float type. Provides two half fields packed
into a single 32 bit field with 32 bit alignment.
@@ -673,7 +673,7 @@ into a single 32 bit field with 32 bit alignment.
<span class='normal'>: Three 16 bit floats</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: half __attribute__((ext_vector_type(3)))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23</a>
+<p>A typedef of: half __attribute__((ext_vector_type(3)))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23</a>
</p>
<p> Vector version of the half float type. Provides three half fields packed
into a single 64 bit field with 64 bit alignment.
@@ -688,7 +688,7 @@ into a single 64 bit field with 64 bit alignment.
<span class='normal'>: Four 16 bit floats</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: half __attribute__((ext_vector_type(4)))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23</a>
+<p>A typedef of: half __attribute__((ext_vector_type(4)))&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 23</a>
</p>
<p> Vector version of the half float type. Provides four half fields packed
into a single 64 bit field with 64 bit alignment.
@@ -771,9 +771,9 @@ with a 128 bit alignment.
<span class='normal'>: 64 bit signed integer</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: long long&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 21 and higher</a>
+<p>A typedef of: long long&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 21 and higher</a>
</p>
-<p>A typedef of: long&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 21</a>
+<p>A typedef of: long&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 21</a>
</p>
<p> A 64 bit signed integer type.
</p>
@@ -960,9 +960,9 @@ with a 64 bit alignment.
<span class='normal'>: Unsigned size type</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: uint64_t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; When compiling for 64 bits.
+<p>A typedef of: uint64_t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;When compiling for 64 bits.
</p>
-<p>A typedef of: uint32_t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; When compiling for 32 bits.
+<p>A typedef of: uint32_t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;When compiling for 32 bits.
</p>
<p> Unsigned size type. The number of bits depend on the compilation flags.
</p>
@@ -976,9 +976,9 @@ with a 64 bit alignment.
<span class='normal'>: Signed size type</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: int64_t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; When compiling for 64 bits.
+<p>A typedef of: int64_t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;When compiling for 64 bits.
</p>
-<p>A typedef of: int32_t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; When compiling for 32 bits.
+<p>A typedef of: int32_t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;When compiling for 32 bits.
</p>
<p> Signed size type. The number of bits depend on the compilation flags.
</p>
@@ -1128,9 +1128,9 @@ with a 128 bit alignment.
<span class='normal'>: 64 bit unsigned integer</span>
</h4>
<div class='jd-details-descr'>
-<p>A typedef of: unsigned long long&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 21 and higher</a>
+<p>A typedef of: unsigned long long&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Removed from <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 21 and higher</a>
</p>
-<p>A typedef of: unsigned long&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 21</a>
+<p>A typedef of: unsigned long&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Added in <a href='http://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels'>API level 21</a>
</p>
<p> A 64 bit unsigned integer type.
</p>
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index a705bcc37406..090be88561c4 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -589,6 +589,9 @@ static status_t decode(int fd, int64_t offset, int64_t length,
ALOGV("format changed to: %s", AMediaFormat_toString(format));
} else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
ALOGV("no output buffer right now");
+ } else if (status <= AMEDIA_ERROR_BASE) {
+ ALOGE("decode error: %d", status);
+ break;
} else {
ALOGV("unexpected info code: %d", status);
}
diff --git a/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml b/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml
index 6959c65e2d3a..6dcbb38cb950 100644
--- a/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml
+++ b/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml
@@ -17,7 +17,8 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_activated="true"
- android:color="?android:attr/colorControlHighlight" />
+ android:color="?android:attr/colorAccent"
+ android:alpha="0.1" />
<item
android:state_enabled="false"
android:color="?android:attr/colorBackground"
diff --git a/packages/DocumentsUI/res/values/attrs.xml b/packages/DocumentsUI/res/values/attrs.xml
new file mode 100644
index 000000000000..0afc3a2e4d8b
--- /dev/null
+++ b/packages/DocumentsUI/res/values/attrs.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+<resources>
+ <declare-styleable name="DocumentsBaseTheme">
+ <attr name="colorActionMode" format="color"/>
+ </declare-styleable>
+</resources>
diff --git a/packages/DocumentsUI/res/values/colors.xml b/packages/DocumentsUI/res/values/colors.xml
index 6002dde5040a..cb6957dd3463 100644
--- a/packages/DocumentsUI/res/values/colors.xml
+++ b/packages/DocumentsUI/res/values/colors.xml
@@ -19,18 +19,20 @@
<color name="material_grey_300">#ffeeeeee</color>
<color name="material_grey_600">#ff757575</color>
<color name="material_grey_800">#ff424242</color>
- <color name="material_blue_700">#ff1976d2</color>
- <color name="material_blue_500">#ff2196f3</color>
<color name="primary_dark">@*android:color/material_blue_grey_900</color>
<color name="primary">@*android:color/material_blue_grey_800</color>
<color name="accent">@*android:color/material_deep_teal_500</color>
+ <color name="platform_blue_100">#ffd0d9ff</color>
+ <color name="platform_blue_500">#ff5677fc</color>
+ <color name="platform_blue_700">#ff455ede</color>
+ <color name="platform_blue_a100">#ffa6baff</color>
+ <color name="platform_blue_a200">#ffff5177</color>
+
<color name="directory_background">@color/material_grey_300</color>
<color name="item_doc_grid_background">#FFFFFFFF</color>
<color name="item_doc_grid_protect_background">#88000000</color>
- <color name="status_bar_background">@color/material_blue_700</color>
- <color name="action_mode_status_bar_background">@color/material_grey_800</color>
<color name="band_select_background">#88ffffff</color>
<color name="band_select_border">#44000000</color>
</resources>
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index e67f956112ce..8301816edaea 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -28,6 +28,7 @@
<item name="android:colorPrimaryDark">@color/primary_dark</item>
<item name="android:colorPrimary">@color/primary</item>
<item name="android:colorAccent">@color/accent</item>
+ <item name="colorActionMode">@color/material_grey_800</item>
<item name="android:listDivider">@*android:drawable/list_divider_material</item>
@@ -43,6 +44,7 @@
<item name="actionBarWidgetTheme">@null</item>
<item name="actionBarTheme">@style/ActionBarTheme</item>
<item name="actionBarPopupTheme">@style/ActionBarPopupTheme</item>
+ <item name="colorActionMode">@color/material_grey_800</item>
<item name="android:listDivider">@*android:drawable/list_divider_material</item>
@@ -68,16 +70,21 @@
</style>
<style name="AlertDialogTheme" parent="@android:style/Theme.Material.Light.Dialog.Alert">
- <item name="android:colorAccent">@color/material_blue_700</item>
+ <item name="android:colorAccent">@color/platform_blue_700</item>
</style>
<style name="FilesTheme" parent="@style/DocumentsBaseTheme.FullScreen">
- <item name="android:colorPrimaryDark">@color/material_blue_700</item>
- <item name="android:colorPrimary">@color/material_blue_500</item>
- <item name="android:colorAccent">@color/material_blue_700</item>
- <item name="android:actionModeStyle">@style/ActionModeStyle</item>
+ <item name="android:colorPrimaryDark">@color/platform_blue_700</item>
+ <item name="android:colorPrimary">@color/platform_blue_500</item>
+ <item name="android:colorAccent">@color/platform_blue_700</item>
+ <item name="android:actionModeStyle">@style/FilesActionModeStyle</item>
+ <item name="colorActionMode">@color/platform_blue_700</item>
<item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
</style>
+ <style name="FilesActionModeStyle" parent="@android:style/Widget.Material.Light.ActionMode">
+ <item name="android:background">@color/platform_blue_100</item>
+ </style>
+
</resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index cfd4ecb96b59..046f3df3b5d4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -637,27 +637,25 @@ public class DirectoryFragment extends Fragment {
@Override
public void onSelectionChanged() {
mModel.getSelection(mSelected);
+ TypedValue color = new TypedValue();
if (mSelected.size() > 0) {
if (DEBUG) Log.d(TAG, "Maybe starting action mode.");
if (mActionMode == null) {
if (DEBUG) Log.d(TAG, "Yeah. Starting action mode.");
mActionMode = getActivity().startActionMode(this);
- getActivity().getWindow().setStatusBarColor(
- getResources().getColor(R.color.action_mode_status_bar_background));
}
+ getActivity().getTheme().resolveAttribute(
+ R.attr.colorActionMode, color, true);
updateActionMenu();
} else {
if (DEBUG) Log.d(TAG, "Finishing action mode.");
if (mActionMode != null) {
mActionMode.finish();
}
- // Obtain the original status bar color from the theme, and restore it.
- TypedValue color = new TypedValue();
getActivity().getTheme().resolveAttribute(
android.R.attr.colorPrimaryDark, color, true);
- getActivity().getWindow().setStatusBarColor(color.data);
-
}
+ getActivity().getWindow().setStatusBarColor(color.data);
if (mActionMode != null) {
mActionMode.setTitle(TextUtils.formatSelectedCount(mSelected.size()));
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index 8f9025a1cdeb..450def799a1a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -90,10 +90,6 @@ public class FilesActivity extends BaseActivity {
mClipper = new DocumentClipper(this);
mDrawer = DrawerController.create(this);
- if (mDrawer.isPresent()) {
- setTheme(R.style.DocumentsNonDialogTheme);
- }
-
RootsFragment.show(getFragmentManager(), null);
if (!mState.restored) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
index d4c4331267a0..0d4265a6417c 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java
@@ -91,12 +91,12 @@ class DocumentLoader {
return task.createCursor(mResolver, columnNames);
}
- synchronized void clearTasks(int deviceId) {
- mTaskList.clearTaskForDevice(deviceId);
+ synchronized void clearTasks() {
+ mTaskList.clear();
}
synchronized void clearCompletedTasks() {
- mTaskList.clearCompletedTask();
+ mTaskList.clearCompletedTasks();
}
synchronized void clearTask(Identifier parentIdentifier) {
@@ -162,18 +162,7 @@ class DocumentLoader {
return null;
}
- void clearTaskForDevice(int deviceId) {
- int i = 0;
- while (i < size()) {
- if (get(i).mIdentifier.mDeviceId == deviceId) {
- remove(i);
- } else {
- i++;
- }
- }
- }
-
- void clearCompletedTask() {
+ void clearCompletedTasks() {
int i = 0;
while (i < size()) {
if (get(i).completed()) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 78ed161d47f4..a1a43c18a72f 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -34,6 +34,8 @@ import com.android.internal.annotations.VisibleForTesting;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
/**
* DocumentsProvider for MTP devices.
@@ -56,8 +58,8 @@ public class MtpDocumentsProvider extends DocumentsProvider {
private MtpManager mMtpManager;
private ContentResolver mResolver;
- private PipeManager mPipeManager;
- private DocumentLoader mDocumentLoader;
+ private Map<Integer, DeviceToolkit> mDeviceToolkits;
+ private DocumentLoader mDocumentLoaders;
private RootScanner mRootScanner;
/**
@@ -72,8 +74,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
sSingleton = this;
mMtpManager = new MtpManager(getContext());
mResolver = getContext().getContentResolver();
- mPipeManager = new PipeManager();
- mDocumentLoader = new DocumentLoader(mMtpManager, mResolver);
+ mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
mRootScanner = new RootScanner(mResolver, mMtpManager);
return true;
}
@@ -82,7 +83,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
void onCreateForTesting(MtpManager mtpManager, ContentResolver resolver) {
mMtpManager = mtpManager;
mResolver = resolver;
- mDocumentLoader = new DocumentLoader(mMtpManager, mResolver);
+ mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
mRootScanner = new RootScanner(mResolver, mMtpManager);
}
@@ -158,7 +159,8 @@ public class MtpDocumentsProvider extends DocumentsProvider {
}
final Identifier parentIdentifier = Identifier.createFromDocumentId(parentDocumentId);
try {
- return mDocumentLoader.queryChildDocuments(projection, parentIdentifier);
+ return getDocumentLoader(parentIdentifier).queryChildDocuments(
+ projection, parentIdentifier);
} catch (IOException exception) {
throw new FileNotFoundException(exception.getMessage());
}
@@ -172,11 +174,12 @@ public class MtpDocumentsProvider extends DocumentsProvider {
try {
switch (mode) {
case "r":
- return mPipeManager.readDocument(mMtpManager, identifier);
+ return getPipeManager(identifier).readDocument(mMtpManager, identifier);
case "w":
// TODO: Clear the parent document loader task (if exists) and call notify
// when writing is completed.
- return mPipeManager.writeDocument(getContext(), mMtpManager, identifier);
+ return getPipeManager(identifier).writeDocument(
+ getContext(), mMtpManager, identifier);
default:
// TODO: Add support for seekable files.
throw new UnsupportedOperationException(
@@ -195,7 +198,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
final Identifier identifier = Identifier.createFromDocumentId(documentId);
try {
return new AssetFileDescriptor(
- mPipeManager.readThumbnail(mMtpManager, identifier),
+ getPipeManager(identifier).readThumbnail(mMtpManager, identifier),
0, // Start offset.
AssetFileDescriptor.UNKNOWN_LENGTH);
} catch (IOException error) {
@@ -212,7 +215,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
mMtpManager.deleteDocument(identifier.mDeviceId, identifier.mObjectHandle);
final Identifier parentIdentifier = new Identifier(
identifier.mDeviceId, identifier.mStorageId, parentHandle);
- mDocumentLoader.clearTask(parentIdentifier);
+ getDocumentLoader(parentIdentifier).clearTask(parentIdentifier);
notifyChildDocumentsChange(parentIdentifier.toDocumentId());
} catch (IOException error) {
throw new FileNotFoundException(error.getMessage());
@@ -221,7 +224,9 @@ public class MtpDocumentsProvider extends DocumentsProvider {
@Override
public void onTrimMemory(int level) {
- mDocumentLoader.clearCompletedTasks();
+ for (final DeviceToolkit toolkit : mDeviceToolkits.values()) {
+ toolkit.mDocumentLoader.clearCompletedTasks();
+ }
}
@Override
@@ -241,7 +246,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
.build(), pipe[1]);
final String documentId = new Identifier(parentId.mDeviceId, parentId.mStorageId,
objectHandle).toDocumentId();
- mDocumentLoader.clearTask(parentId);
+ getDocumentLoader(parentId).clearTask(parentId);
notifyChildDocumentsChange(parentDocumentId);
return documentId;
} catch (IOException error) {
@@ -252,12 +257,15 @@ public class MtpDocumentsProvider extends DocumentsProvider {
void openDevice(int deviceId) throws IOException {
mMtpManager.openDevice(deviceId);
+ mDeviceToolkits.put(deviceId, new DeviceToolkit(mMtpManager, mResolver));
mRootScanner.scanNow();
}
void closeDevice(int deviceId) throws IOException {
+ // TODO: Flush the device before closing (if not closed externally).
+ getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
+ mDeviceToolkits.remove(deviceId);
mMtpManager.closeDevice(deviceId);
- mDocumentLoader.clearTasks(deviceId);
mRootScanner.scanNow();
}
@@ -266,7 +274,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
for (int deviceId : mMtpManager.getOpenedDeviceIds()) {
try {
mMtpManager.closeDevice(deviceId);
- mDocumentLoader.clearTasks(deviceId);
+ getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
closed = true;
} catch (IOException d) {
Log.d(TAG, "Failed to close the MTP device: " + deviceId);
@@ -287,4 +295,30 @@ public class MtpDocumentsProvider extends DocumentsProvider {
null,
false);
}
+
+ private DeviceToolkit getDeviceToolkit(int deviceId) throws FileNotFoundException {
+ final DeviceToolkit toolkit = mDeviceToolkits.get(deviceId);
+ if (toolkit == null) {
+ throw new FileNotFoundException();
+ }
+ return toolkit;
+ }
+
+ private PipeManager getPipeManager(Identifier identifier) throws FileNotFoundException {
+ return getDeviceToolkit(identifier.mDeviceId).mPipeManager;
+ }
+
+ private DocumentLoader getDocumentLoader(Identifier identifier) throws FileNotFoundException {
+ return getDeviceToolkit(identifier.mDeviceId).mDocumentLoader;
+ }
+
+ private static class DeviceToolkit {
+ public final PipeManager mPipeManager;
+ public final DocumentLoader mDocumentLoader;
+
+ public DeviceToolkit(MtpManager manager, ContentResolver resolver) {
+ mPipeManager = new PipeManager();
+ mDocumentLoader = new DocumentLoader(manager, resolver);
+ }
+ }
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
index ca78a3e1b775..7cc7413bab3d 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -42,7 +42,7 @@ class MtpManager {
private final SparseArray<MtpDevice> mDevices = new SparseArray<>();
MtpManager(Context context) {
- mManager = (UsbManager)context.getSystemService(Context.USB_SERVICE);
+ mManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
}
synchronized void openDevice(int deviceId) throws IOException {
@@ -96,78 +96,96 @@ class MtpManager {
return result;
}
- synchronized MtpRoot[] getRoots(int deviceId) throws IOException {
+ MtpRoot[] getRoots(int deviceId) throws IOException {
final MtpDevice device = getDevice(deviceId);
- final int[] storageIds = device.getStorageIds();
- if (storageIds == null) {
- throw new IOException("Failed to obtain storage IDs.");
- }
- final MtpRoot[] results = new MtpRoot[storageIds.length];
- for (int i = 0; i < storageIds.length; i++) {
- results[i] = new MtpRoot(deviceId, device.getStorageInfo(storageIds[i]));
+ synchronized (device) {
+ final int[] storageIds = device.getStorageIds();
+ if (storageIds == null) {
+ throw new IOException("Failed to obtain storage IDs.");
+ }
+ final MtpRoot[] results = new MtpRoot[storageIds.length];
+ for (int i = 0; i < storageIds.length; i++) {
+ results[i] = new MtpRoot(deviceId, device.getStorageInfo(storageIds[i]));
+ }
+ return results;
}
- return results;
}
- synchronized MtpObjectInfo getObjectInfo(int deviceId, int objectHandle)
+ MtpObjectInfo getObjectInfo(int deviceId, int objectHandle)
throws IOException {
final MtpDevice device = getDevice(deviceId);
- return device.getObjectInfo(objectHandle);
+ synchronized (device) {
+ return device.getObjectInfo(objectHandle);
+ }
}
- synchronized int[] getObjectHandles(int deviceId, int storageId, int parentObjectHandle)
+ int[] getObjectHandles(int deviceId, int storageId, int parentObjectHandle)
throws IOException {
final MtpDevice device = getDevice(deviceId);
- return device.getObjectHandles(storageId, 0 /* all format */, parentObjectHandle);
+ synchronized (device) {
+ return device.getObjectHandles(storageId, 0 /* all format */, parentObjectHandle);
+ }
}
- synchronized byte[] getObject(int deviceId, int objectHandle, int expectedSize)
+ byte[] getObject(int deviceId, int objectHandle, int expectedSize)
throws IOException {
final MtpDevice device = getDevice(deviceId);
- return device.getObject(objectHandle, expectedSize);
+ synchronized (device) {
+ return device.getObject(objectHandle, expectedSize);
+ }
}
- synchronized byte[] getThumbnail(int deviceId, int objectHandle) throws IOException {
+ byte[] getThumbnail(int deviceId, int objectHandle) throws IOException {
final MtpDevice device = getDevice(deviceId);
- return device.getThumbnail(objectHandle);
+ synchronized (device) {
+ return device.getThumbnail(objectHandle);
+ }
}
- synchronized void deleteDocument(int deviceId, int objectHandle) throws IOException {
+ void deleteDocument(int deviceId, int objectHandle) throws IOException {
final MtpDevice device = getDevice(deviceId);
- if (!device.deleteObject(objectHandle)) {
- throw new IOException("Failed to delete document");
+ synchronized (device) {
+ if (!device.deleteObject(objectHandle)) {
+ throw new IOException("Failed to delete document");
+ }
}
}
- synchronized int createDocument(int deviceId, MtpObjectInfo objectInfo,
+ int createDocument(int deviceId, MtpObjectInfo objectInfo,
ParcelFileDescriptor source) throws IOException {
final MtpDevice device = getDevice(deviceId);
- final MtpObjectInfo sendObjectInfoResult = device.sendObjectInfo(objectInfo);
- if (sendObjectInfoResult == null) {
- throw new IOException("Failed to create a document");
- }
- if (objectInfo.getFormat() != MtpConstants.FORMAT_ASSOCIATION) {
- if (!device.sendObject(sendObjectInfoResult.getObjectHandle(),
- sendObjectInfoResult.getCompressedSize(), source)) {
- throw new IOException("Failed to send contents of a document");
+ synchronized (device) {
+ final MtpObjectInfo sendObjectInfoResult = device.sendObjectInfo(objectInfo);
+ if (sendObjectInfoResult == null) {
+ throw new IOException("Failed to create a document");
+ }
+ if (objectInfo.getFormat() != MtpConstants.FORMAT_ASSOCIATION) {
+ if (!device.sendObject(sendObjectInfoResult.getObjectHandle(),
+ sendObjectInfoResult.getCompressedSize(), source)) {
+ throw new IOException("Failed to send contents of a document");
+ }
}
+ return sendObjectInfoResult.getObjectHandle();
}
- return sendObjectInfoResult.getObjectHandle();
}
- synchronized int getParent(int deviceId, int objectHandle) throws IOException {
+ int getParent(int deviceId, int objectHandle) throws IOException {
final MtpDevice device = getDevice(deviceId);
- final int result = (int) device.getParent(objectHandle);
- if (result < 0) {
- throw new FileNotFoundException("Not found parent object");
+ synchronized (device) {
+ final int result = (int) device.getParent(objectHandle);
+ if (result < 0) {
+ throw new FileNotFoundException("Not found parent object");
+ }
+ return result;
}
- return result;
}
- synchronized void importFile(int deviceId, int objectHandle, ParcelFileDescriptor target)
+ void importFile(int deviceId, int objectHandle, ParcelFileDescriptor target)
throws IOException {
final MtpDevice device = getDevice(deviceId);
- device.importFile(objectHandle, target);
+ synchronized (device) {
+ device.importFile(objectHandle, target);
+ }
}
private MtpDevice getDevice(int deviceId) throws IOException {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java
index cd38f1e852b5..affaebd05c16 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java
@@ -33,7 +33,7 @@ class PipeManager {
final ExecutorService mExecutor;
PipeManager() {
- this(Executors.newCachedThreadPool());
+ this(Executors.newSingleThreadExecutor());
}
PipeManager(ExecutorService executor) {
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 9b316be48a15..4b3a5cd061bf 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -39,7 +39,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
private TestMtpManager mMtpManager;
@Override
- public void setUp() {
+ public void setUp() throws IOException {
mResolver = new TestContentResolver();
mMtpManager = new TestMtpManager(getContext());
mProvider = new MtpDocumentsProvider();
@@ -207,6 +207,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
}
public void testQueryDocument() throws IOException {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
.setObjectHandle(2)
.setFormat(MtpConstants.FORMAT_EXIF_JPEG)
@@ -232,6 +234,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
}
public void testQueryDocument_directory() throws IOException {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
.setObjectHandle(2)
.setFormat(MtpConstants.FORMAT_ASSOCIATION)
@@ -255,6 +259,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
}
public void testQueryDocument_forRoot() throws IOException {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
mMtpManager.setRoots(0, new MtpRoot[] {
new MtpRoot(
0 /* deviceId */,
@@ -277,6 +283,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
}
public void testQueryChildDocuments() throws Exception {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 });
mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
@@ -303,6 +311,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
}
public void testQueryChildDocuments_cursorError() throws Exception {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
try {
mProvider.queryChildDocuments("0_0_0", null, null);
fail();
@@ -312,6 +322,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
}
public void testQueryChildDocuments_documentError() throws Exception {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 });
try {
mProvider.queryChildDocuments("0_0_0", null, null);
@@ -321,7 +333,9 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
}
}
- public void testDeleteDocument() throws FileNotFoundException {
+ public void testDeleteDocument() throws IOException {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
.setObjectHandle(1)
.setParent(2)
@@ -332,7 +346,9 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
MtpDocumentsProvider.AUTHORITY, "0_0_2")));
}
- public void testDeleteDocument_error() {
+ public void testDeleteDocument_error() throws IOException {
+ mMtpManager.addValidDevice(0);
+ mProvider.openDevice(0);
mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
.setObjectHandle(2)
.build());
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 3514c5de0d09..87dfab93dd55 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -155,6 +155,7 @@ public class KeyguardViewMediator extends SystemUI {
private static final int NOTIFY_STARTED_WAKING_UP = 21;
private static final int NOTIFY_SCREEN_TURNED_ON = 22;
private static final int NOTIFY_SCREEN_TURNED_OFF = 23;
+ private static final int NOTIFY_STARTED_GOING_TO_SLEEP = 24;
/**
* The default amount of time we stay awake (used for all key input)
@@ -677,6 +678,7 @@ public class KeyguardViewMediator extends SystemUI {
playSounds(true);
}
}
+ notifyStartedGoingToSleep();
}
public void onFinishedGoingToSleep(int why) {
@@ -1098,6 +1100,11 @@ public class KeyguardViewMediator extends SystemUI {
mHandler.sendEmptyMessage(VERIFY_UNLOCK);
}
+ private void notifyStartedGoingToSleep() {
+ if (DEBUG) Log.d(TAG, "notifyStartedGoingToSleep");
+ mHandler.sendEmptyMessage(NOTIFY_STARTED_GOING_TO_SLEEP);
+ }
+
private void notifyFinishedGoingToSleep() {
if (DEBUG) Log.d(TAG, "notifyFinishedGoingToSleep");
mHandler.sendEmptyMessage(NOTIFY_FINISHED_GOING_TO_SLEEP);
@@ -1209,6 +1216,9 @@ public class KeyguardViewMediator extends SystemUI {
case VERIFY_UNLOCK:
handleVerifyUnlock();
break;
+ case NOTIFY_STARTED_GOING_TO_SLEEP:
+ handleNotifyStartedGoingToSleep();
+ break;
case NOTIFY_FINISHED_GOING_TO_SLEEP:
handleNotifyFinishedGoingToSleep();
break;
@@ -1546,6 +1556,13 @@ public class KeyguardViewMediator extends SystemUI {
}
}
+ private void handleNotifyStartedGoingToSleep() {
+ synchronized (KeyguardViewMediator.this) {
+ if (DEBUG) Log.d(TAG, "handleNotifyStartedGoingToSleep");
+ mStatusBarKeyguardViewManager.onStartedGoingToSleep();
+ }
+ }
+
/**
* Handle message sent by {@link #notifyFinishedGoingToSleep()}
* @see #NOTIFY_FINISHED_GOING_TO_SLEEP
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
index 21ab7e582762..28f111fce4eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
@@ -69,7 +69,8 @@ import java.util.List;
* Navigation bar contains both pinned and unpinned apps: pinned in the left part, unpinned in the
* right part, with no separator in between.
*/
-class NavigationBarApps extends LinearLayout {
+class NavigationBarApps extends LinearLayout
+ implements NavigationBarAppsModel.OnAppsChangedListener {
public final static boolean DEBUG = false;
private final static String TAG = "NavigationBarApps";
@@ -120,11 +121,20 @@ class NavigationBarApps extends LinearLayout {
// has a child that will be moved to make the menu to appear where we need it.
private final ViewGroup mPopupAnchor;
private final PopupMenu mPopupMenu;
+
+ /**
+ * True if popup menu code is busy with a popup operation.
+ * Attempting to show a popup menu or to add menu items while it's returning true will
+ * corrupt/crash the app.
+ */
+ private boolean mIsPopupInUse = false;
private final int [] mClickedIconLocation = new int[2];
public NavigationBarApps(Context context, AttributeSet attrs) {
super(context, attrs);
- sAppsModel = new NavigationBarAppsModel(context);
+ if (sAppsModel == null) {
+ sAppsModel = new NavigationBarAppsModel(context);
+ }
mPackageManager = context.getPackageManager();
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
@@ -278,6 +288,7 @@ class NavigationBarApps extends LinearLayout {
mContext.registerReceiver(mBroadcastReceiver, filter);
mAppPackageMonitor.register(mContext, null, UserHandle.ALL, true);
+ sAppsModel.addOnAppsChangedListener(this);
}
@Override
@@ -285,6 +296,16 @@ class NavigationBarApps extends LinearLayout {
super.onDetachedFromWindow();
mContext.unregisterReceiver(mBroadcastReceiver);
mAppPackageMonitor.unregister();
+ sAppsModel.removeOnAppsChangedListener(this);
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ if (mIsPopupInUse && !isShown()) {
+ // Hide the popup if current view became invisible.
+ shutdownPopupMenu();
+ }
}
private void addAppButton(AppButtonData appButtonData) {
@@ -299,6 +320,19 @@ class NavigationBarApps extends LinearLayout {
new GetActivityIconTask(mPackageManager, button).execute(appButtonData);
}
+ private List<AppInfo> getPinnedApps() {
+ List<AppInfo> apps = new ArrayList<AppInfo>();
+ int childCount = getChildCount();
+ for (int i = 0; i != childCount; ++i) {
+ View child = getChildAt(i);
+ AppButtonData appButtonData = (AppButtonData)child.getTag();
+ if (appButtonData == null) continue; // Skip the drag placeholder.
+ if(!appButtonData.pinned) continue;
+ apps.add(appButtonData.appInfo);
+ }
+ return apps;
+ }
+
/**
* Creates an ImageView icon for each pinned app. Removes any existing icons. May be called
* to synchronize the current view with the shared data mode.
@@ -319,16 +353,7 @@ class NavigationBarApps extends LinearLayout {
* Saves pinned apps stored in app icons into the data model.
*/
private void savePinnedApps() {
- List<AppInfo> apps = new ArrayList<AppInfo>();
- int childCount = getChildCount();
- for (int i = 0; i != childCount; ++i) {
- View child = getChildAt(i);
- AppButtonData appButtonData = (AppButtonData)child.getTag();
- if (appButtonData == null) continue; // Skip the drag placeholder.
- if(!appButtonData.pinned) continue;
- apps.add(appButtonData.appInfo);
- }
- sAppsModel.setApps(apps);
+ sAppsModel.setApps(getPinnedApps());
}
/**
@@ -671,14 +696,12 @@ class NavigationBarApps extends LinearLayout {
}
/**
- * Returns true if popup menu code is busy with a popup operation.
- * Attempting to show a popup menu or to add menu items while it's returning true will
- * corrupt/crash the app.
+ * Brings the menu popup to closed state.
+ * Can be called at any stage of the asynchronous process of showing a menu.
*/
- boolean isPopupInUse() {
- // mPopupAnchor's parent will be set to non-null/null by mWindowManager.add/RemoveView
- // correspondingly.
- return mPopupAnchor.getParent() != null;
+ private void shutdownPopupMenu() {
+ mWindowManager.removeView(mPopupAnchor);
+ mPopupMenu.dismiss();
}
/**
@@ -712,14 +735,19 @@ class NavigationBarApps extends LinearLayout {
mPopupMenu.setOnDismissListener(new PopupMenu.OnDismissListener() {
@Override
public void onDismiss(PopupMenu menu) {
+ // FYU: thorough testing for closing menu either by the user or via
+ // shutdownPopupMenu() called at various moments of the menu creation, revealed that
+ // 'onDismiss' is guaranteed to be called after each invocation of showPopupMenu.
mWindowManager.removeView(mPopupAnchor);
anchorButton.removeOnAttachStateChangeListener(onAttachStateChangeListener);
mPopupMenu.setOnDismissListener(null);
mPopupMenu.getMenu().clear();
+ mIsPopupInUse = false;
}
});
mWindowManager.addView(mPopupAnchor, mPopupAnchorLayoutParams);
+ mIsPopupInUse = true;
}
private void activateTask(int taskPersistentId) {
@@ -740,10 +768,6 @@ class NavigationBarApps extends LinearLayout {
private void populateLaunchMenu(AppButtonData appButtonData) {
Menu menu = mPopupMenu.getMenu();
int taskCount = appButtonData.getTaskCount();
- if (taskCount == 0) {
- menu.add("- TBD MENU ITEM -"); // adding something so that the menu is not empty.
- return;
- }
for (int i = 0; i < taskCount; ++i) {
final RecentTaskInfo taskInfo = appButtonData.tasks.get(i);
MenuItem item = menu.add(getActivityForTask(taskInfo).flattenToShortString());
@@ -758,10 +782,14 @@ class NavigationBarApps extends LinearLayout {
}
/**
- * Shows a task selection menu for an apps icon.
+ * Shows a task selection menu for clicked or hovered-over apps that have more than 1 running
+ * tasks.
*/
- private void showLaunchMenu(ImageView appIcon) {
+ void maybeShowLaunchMenu(ImageView appIcon) {
+ if (mIsPopupInUse) return;
AppButtonData appButtonData = (AppButtonData) appIcon.getTag();
+ if (appButtonData.getTaskCount() <= 1) return;
+
populateLaunchMenu(appButtonData);
showPopupMenu(appIcon);
}
@@ -779,8 +807,7 @@ class NavigationBarApps extends LinearLayout {
mShowMenuCallback = new Runnable() {
@Override
public void run() {
- if (isPopupInUse()) return;
- showLaunchMenu((ImageView) v);
+ maybeShowLaunchMenu((ImageView) v);
}
};
}
@@ -824,17 +851,6 @@ class NavigationBarApps extends LinearLayout {
mContext.startActivityAsUser(launchIntent, optsBundle, appInfo.getUser());
}
- /**
- * Shows a task selection menu for clicked apps that have more than 1 running tasks.
- */
- void maybeShowLaunchMenu(ImageView appIcon) {
- if (isPopupInUse()) return;
- AppButtonData appButtonData = (AppButtonData) appIcon.getTag();
- if (appButtonData.getTaskCount() <= 1) return;
-
- showLaunchMenu(appIcon);
- }
-
@Override
public void onClick(View v) {
AppButtonData appButtonData = (AppButtonData) v.getTag();
@@ -901,7 +917,7 @@ class NavigationBarApps extends LinearLayout {
@Override
public boolean onContextClick(View v) {
- if (isPopupInUse()) return true;
+ if (mIsPopupInUse) return true;
ImageView appIcon = (ImageView) v;
populateContextMenu(appIcon);
showPopupMenu(appIcon);
@@ -1095,4 +1111,11 @@ class NavigationBarApps extends LinearLayout {
});
}
}
+
+ @Override
+ public void onPinnedAppsChanged() {
+ if (getPinnedApps().equals(sAppsModel.getApps())) return;
+ recreatePinnedAppButtons();
+ updateRecentApps();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
index 832a3e9f38bb..badb846694c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarAppsModel.java
@@ -44,6 +44,10 @@ import java.util.Set;
* ComponentName.
*/
class NavigationBarAppsModel {
+ public interface OnAppsChangedListener {
+ void onPinnedAppsChanged();
+ }
+
private final static String TAG = "NavigationBarAppsModel";
// Default number of apps to load initially.
@@ -79,6 +83,9 @@ class NavigationBarAppsModel {
// Apps are represented as an ordered list of app infos.
private List<AppInfo> mApps = new ArrayList<AppInfo>();
+ private List<OnAppsChangedListener> mOnAppsChangedListeners =
+ new ArrayList<OnAppsChangedListener>();
+
// Id of the current user.
private int mCurrentUserId = -1;
@@ -167,6 +174,14 @@ class NavigationBarAppsModel {
return null;
}
+ public void addOnAppsChangedListener(OnAppsChangedListener listener) {
+ mOnAppsChangedListeners.add(listener);
+ }
+
+ public void removeOnAppsChangedListener(OnAppsChangedListener listener) {
+ mOnAppsChangedListeners.remove(listener);
+ }
+
/**
* Reinitializes the model for a new user.
*/
@@ -239,6 +254,11 @@ class NavigationBarAppsModel {
public void setApps(List<AppInfo> apps) {
mApps = apps;
savePrefs();
+
+ int size = mOnAppsChangedListeners.size();
+ for (int i = 0; i < size; ++i) {
+ mOnAppsChangedListeners.get(i).onPinnedAppsChanged();
+ }
}
/** Saves the current model to disk. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index d3af8f03fa73..ea6681e48537 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -315,6 +315,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
boolean mLeaveOpenOnKeyguardHide;
KeyguardIndicationController mKeyguardIndicationController;
+ // Keyguard is going away soon.
+ private boolean mKeyguardGoingAway;
+ // Keyguard is actually fading away now.
private boolean mKeyguardFadingAway;
private long mKeyguardFadingAwayDelay;
private long mKeyguardFadingAwayDuration;
@@ -488,12 +491,18 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
private boolean mLaunchTransitionFadingAway;
private ExpandableNotificationRow mDraggedDownRow;
private boolean mLaunchCameraOnScreenTurningOn;
+ private boolean mLaunchCameraOnFinishedGoingToSleep;
private PowerManager.WakeLock mGestureWakeLock;
private Vibrator mVibrator;
// Fingerprint (as computed by getLoggingFingerprint() of the last logged state.
private int mLastLoggedStateFingerprint;
+ /**
+ * If set, the device has started going to sleep but isn't fully non-interactive yet.
+ */
+ protected boolean mStartedGoingToSleep;
+
private static final int VISIBLE_LOCATIONS = StackViewState.LOCATION_FIRST_CARD
| StackViewState.LOCATION_MAIN_AREA;
@@ -3596,6 +3605,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
// Treat Keyguard exit animation as an app transition to achieve nice transition for status
// bar.
+ mKeyguardGoingAway = true;
mIconController.appTransitionPending();
}
@@ -3627,6 +3637,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
*/
public void finishKeyguardFadingAway() {
mKeyguardFadingAway = false;
+ mKeyguardGoingAway = false;
}
public void stopWaitingForKeyguardExit() {
@@ -3958,16 +3969,33 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
disable(mDisabledUnmodified1, mDisabledUnmodified2, true /* animate */);
}
+ public void onStartedGoingToSleep() {
+ mStartedGoingToSleep = true;
+ }
+
public void onFinishedGoingToSleep() {
mNotificationPanel.onAffordanceLaunchEnded();
releaseGestureWakeLock();
mLaunchCameraOnScreenTurningOn = false;
+ mStartedGoingToSleep = false;
mDeviceInteractive = false;
mWakeUpComingFromTouch = false;
mWakeUpTouchLocation = null;
mLockedPhoneAnalytics.onScreenOff();
mStackScroller.setAnimationsEnabled(false);
updateVisibleToUser();
+ if (mLaunchCameraOnFinishedGoingToSleep) {
+ mLaunchCameraOnFinishedGoingToSleep = false;
+
+ // This gets executed before we will show Keyguard, so post it in order that the state
+ // is correct.
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ onCameraLaunchGestureDetected();
+ }
+ });
+ }
}
public void onStartedWakingUp() {
@@ -3988,7 +4016,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
private void vibrateForCameraGesture() {
- mVibrator.vibrate(750L);
+ // Make sure to pass -1 for repeat so VibratorService doesn't stop us when going to sleep.
+ mVibrator.vibrate(new long[] { 0, 750L }, -1 /* repeat */);
}
public void onScreenTurnedOn() {
@@ -4137,9 +4166,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
public void appTransitionStarting(long startTime, long duration) {
// Use own timings when Keyguard is going away, see keyguardGoingAway and
- // setKeyguardFadingAway. When duration is 0, skip this one because no animation is really
- // playing.
- if (!mKeyguardFadingAway && duration > 0) {
+ // setKeyguardFadingAway.
+ if (!mKeyguardGoingAway) {
mIconController.appTransitionStarting(startTime, duration);
}
if (mIconPolicy != null) {
@@ -4149,6 +4177,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
@Override
public void onCameraLaunchGestureDetected() {
+ if (mStartedGoingToSleep) {
+ mLaunchCameraOnFinishedGoingToSleep = true;
+ return;
+ }
if (!mNotificationPanel.canCameraGestureBeLaunched(
mStatusBarKeyguardViewManager.isShowing() && mExpandedVisible)) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index e26f42301830..394ff3f8245c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -164,6 +164,10 @@ public class StatusBarKeyguardViewManager {
}
}
+ public void onStartedGoingToSleep() {
+ mPhoneStatusBar.onStartedGoingToSleep();
+ }
+
public void onFinishedGoingToSleep() {
mDeviceInteractive = false;
mPhoneStatusBar.onFinishedGoingToSleep();
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
index af7ee0856cb6..f156607fb26d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
@@ -139,6 +139,7 @@ public class ZenFooter extends LinearLayout {
}
public void onConfigurationChanged() {
+ mEndNowButton.setText(mContext.getString(R.string.volume_zen_end_now));
mSpTexts.update();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java
index e7a40d7fc10b..0ae0cf674644 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarAppsModelTest.java
@@ -412,4 +412,19 @@ public class NavigationBarAppsModelTest extends AndroidTestCase {
verify(mMockEdit).apply();
verifyNoMoreInteractions(mMockEdit);
}
+
+ /** Tests the apps-changed listener. */
+ public void testAppsChangedListeners() {
+ NavigationBarAppsModel.OnAppsChangedListener listener =
+ mock(NavigationBarAppsModel.OnAppsChangedListener.class);
+
+ mModel.addOnAppsChangedListener(listener);
+ mModel.setApps(new ArrayList<AppInfo>());
+ verify(listener).onPinnedAppsChanged();
+ verifyNoMoreInteractions(listener);
+
+ mModel.removeOnAppsChangedListener(listener);
+ mModel.setApps(new ArrayList<AppInfo>());
+ verifyNoMoreInteractions(listener);
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 7051b52cad4e..91c3d48d1274 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -650,6 +650,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
userState.mIsTouchExplorationEnabled = false;
userState.mIsEnhancedWebAccessibilityEnabled = false;
userState.mIsDisplayMagnificationEnabled = false;
+ userState.mIsAutoclickEnabled = false;
userState.mInstalledServices.add(accessibilityServiceInfo);
userState.mEnabledServices.clear();
userState.mEnabledServices.add(sFakeAccessibilityServiceComponentName);
@@ -700,6 +701,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
userState.mIsTouchExplorationEnabled = touchExplorationEnabled;
userState.mIsEnhancedWebAccessibilityEnabled = false;
userState.mIsDisplayMagnificationEnabled = false;
+ userState.mIsAutoclickEnabled = false;
userState.mEnabledServices.clear();
userState.mEnabledServices.add(service);
userState.mBindingServices.clear();
@@ -1281,6 +1283,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (userState.mIsFilterKeyEventsEnabled) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS;
}
+ if (userState.mIsAutoclickEnabled) {
+ flags |= AccessibilityInputFilter.FLAG_FEATURE_AUTOCLICK;
+ }
if (flags != 0) {
if (!mHasInputFilter) {
mHasInputFilter = true;
@@ -1485,6 +1490,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
somthingChanged |= readHighTextContrastEnabledSettingLocked(userState);
somthingChanged |= readEnhancedWebAccessibilityEnabledChangedLocked(userState);
somthingChanged |= readDisplayMagnificationEnabledSettingLocked(userState);
+ somthingChanged |= readAutoclickEnabledSettingLocked(userState);
somthingChanged |= readDisplayColorAdjustmentSettingsLocked(userState);
return somthingChanged;
}
@@ -1523,6 +1529,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return false;
}
+ private boolean readAutoclickEnabledSettingLocked(UserState userState) {
+ final boolean autoclickEnabled = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED,
+ 0, userState.mUserId) == 1;
+ if (autoclickEnabled != userState.mIsAutoclickEnabled) {
+ userState.mIsAutoclickEnabled = autoclickEnabled;
+ return true;
+ }
+ return false;
+ }
+
private boolean readEnhancedWebAccessibilityEnabledChangedLocked(UserState userState) {
final boolean enhancedWeAccessibilityEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION,
@@ -1671,6 +1689,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
pw.append(", touchExplorationEnabled=" + userState.mIsTouchExplorationEnabled);
pw.append(", displayMagnificationEnabled="
+ userState.mIsDisplayMagnificationEnabled);
+ pw.append(", autoclickEnabled=" + userState.mIsAutoclickEnabled);
if (userState.mUiAutomationService != null) {
pw.append(", ");
userState.mUiAutomationService.dump(fd, pw, args);
@@ -3777,6 +3796,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public boolean mIsTextHighContrastEnabled;
public boolean mIsEnhancedWebAccessibilityEnabled;
public boolean mIsDisplayMagnificationEnabled;
+ public boolean mIsAutoclickEnabled;
public boolean mIsFilterKeyEventsEnabled;
public boolean mHasDisplayColorAdjustment;
public boolean mAccessibilityFocusOnlyInActiveWindow;
@@ -3841,6 +3861,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
mIsTouchExplorationEnabled = false;
mIsEnhancedWebAccessibilityEnabled = false;
mIsDisplayMagnificationEnabled = false;
+ mIsAutoclickEnabled = false;
}
public void destroyUiAutomationService() {
@@ -3865,6 +3886,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private final Uri mDisplayMagnificationEnabledUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
+ private final Uri mAutoclickEnabledUri = Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED);
+
private final Uri mEnabledAccessibilityServicesUri = Settings.Secure.getUriFor(
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
@@ -3897,6 +3921,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(mDisplayMagnificationEnabledUri,
false, this, UserHandle.USER_ALL);
+ contentResolver.registerContentObserver(mAutoclickEnabledUri,
+ false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(mEnabledAccessibilityServicesUri,
false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
@@ -3938,6 +3964,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (readDisplayMagnificationEnabledSettingLocked(userState)) {
onUserStateChangedLocked(userState);
}
+ } else if (mAutoclickEnabledUri.equals(uri)) {
+ if (readAutoclickEnabledSettingLocked(userState)) {
+ onUserStateChangedLocked(userState);
+ }
} else if (mEnabledAccessibilityServicesUri.equals(uri)) {
if (readEnabledAccessibilityServicesLocked(userState)) {
onUserStateChangedLocked(userState);
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index fa87270d6984..23850605a9c8 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -363,7 +363,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
// ... and see if these are hosts we've been awaiting.
// NOTE: We are backing up and restoring only the owner.
- if (newPackageAdded && userId == UserHandle.USER_OWNER) {
+ // TODO: http://b/22388012
+ if (newPackageAdded && userId == UserHandle.USER_SYSTEM) {
final int uid = getUidForPackage(pkgName, userId);
if (uid >= 0 ) {
resolveHostUidLocked(pkgName, uid);
@@ -2729,7 +2730,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
Host host = lookupHostLocked(oldHostId);
if (host != null) {
final int uid = getUidForPackage(NEW_KEYGUARD_HOST_PACKAGE,
- UserHandle.USER_OWNER);
+ UserHandle.USER_SYSTEM);
if (uid >= 0) {
host.id = new HostId(uid, KEYGUARD_HOST_ID, NEW_KEYGUARD_HOST_PACKAGE);
}
@@ -2750,7 +2751,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
private static AtomicFile getSavedStateFile(int userId) {
File dir = Environment.getUserSystemDirectory(userId);
File settingsFile = getStateFile(userId);
- if (!settingsFile.exists() && userId == UserHandle.USER_OWNER) {
+ if (!settingsFile.exists() && userId == UserHandle.USER_SYSTEM) {
if (!dir.exists()) {
dir.mkdirs();
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index d5c4a41f07b2..fd67b4148b89 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -271,7 +271,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
int sysUiUid = -1;
try {
sysUiUid = mContext.getPackageManager().getPackageUid("com.android.systemui",
- UserHandle.USER_OWNER);
+ UserHandle.USER_SYSTEM);
} catch (PackageManager.NameNotFoundException e) {
// Some platforms, such as wearables do not have a system ui.
Log.w(TAG, "Unable to resolve SystemUI's UID.", e);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d1e1683d6478..6190a5ab357e 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2270,8 +2270,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
mNetworkRequestInfoLogs.log("REGISTER " + nri);
if (!nri.isRequest) {
for (NetworkAgentInfo network : mNetworkAgentInfos.values()) {
- if (network.satisfiesImmutableCapabilitiesOf(nri.request)) {
- updateSignalStrengthThresholds(network);
+ if (nri.request.networkCapabilities.hasSignalStrength() &&
+ network.satisfiesImmutableCapabilitiesOf(nri.request)) {
+ updateSignalStrengthThresholds(network, "REGISTER", nri.request);
}
}
}
@@ -2388,8 +2389,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
// if this listen request applies and remove it.
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
nai.networkRequests.remove(nri.request.requestId);
- if (nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
- updateSignalStrengthThresholds(nai);
+ if (nri.request.networkCapabilities.hasSignalStrength() &&
+ nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
+ updateSignalStrengthThresholds(nai, "RELEASE", nri.request);
}
}
}
@@ -3639,9 +3641,24 @@ public class ConnectivityService extends IConnectivityManager.Stub
return new ArrayList<Integer>(thresholds);
}
- private void updateSignalStrengthThresholds(NetworkAgentInfo nai) {
+ private void updateSignalStrengthThresholds(
+ NetworkAgentInfo nai, String reason, NetworkRequest request) {
+ ArrayList<Integer> thresholdsArray = getSignalStrengthThresholds(nai);
Bundle thresholds = new Bundle();
- thresholds.putIntegerArrayList("thresholds", getSignalStrengthThresholds(nai));
+ thresholds.putIntegerArrayList("thresholds", thresholdsArray);
+
+ // TODO: Switch to VDBG.
+ if (DBG) {
+ String detail;
+ if (request != null && request.networkCapabilities.hasSignalStrength()) {
+ detail = reason + " " + request.networkCapabilities.getSignalStrength();
+ } else {
+ detail = reason;
+ }
+ log(String.format("updateSignalStrengthThresholds: %s, sending %s to %s",
+ detail, Arrays.toString(thresholdsArray.toArray()), nai.name()));
+ }
+
nai.asyncChannel.sendMessage(
android.net.NetworkAgent.CMD_SET_SIGNAL_STRENGTH_THRESHOLDS,
0, 0, thresholds);
@@ -4624,7 +4641,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// so we could decide to tear it down immediately afterwards. That's fine though - on
// disconnection NetworkAgents should stop any signal strength monitoring they have been
// doing.
- updateSignalStrengthThresholds(networkAgent);
+ updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
// Consider network even though it is not yet validated.
rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP);
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 46fd28a62108..2f4ad3ff0e29 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -1689,7 +1689,7 @@ public class DeviceIdleController extends SystemService
}
if (args != null) {
- int userId = UserHandle.USER_OWNER;
+ int userId = UserHandle.USER_SYSTEM;
for (int i=0; i<args.length; i++) {
String arg = args[i];
if ("-h".equals(arg)) {
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 69f0cef6d39e..bd7d4b27e02c 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -101,8 +101,8 @@ public class GestureLauncherService extends SystemService {
* Whether camera double tap power button gesture is currently enabled;
*/
private boolean mCameraDoubleTapPowerEnabled;
- private long mLastPowerDownWhileNonInteractive = 0;
-
+ private long mLastPowerDownWhileNonInteractive;
+ private long mLastPowerDownWhileInteractive;
public GestureLauncherService(Context context) {
super(context);
@@ -251,23 +251,30 @@ public class GestureLauncherService extends SystemService {
public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive) {
boolean launched = false;
+ boolean intercept = false;
synchronized (this) {
if (!mCameraDoubleTapPowerEnabled) {
mLastPowerDownWhileNonInteractive = 0;
+ mLastPowerDownWhileInteractive = 0;
return false;
}
if (event.getEventTime() - mLastPowerDownWhileNonInteractive
< CAMERA_POWER_DOUBLE_TAP_TIME_MS) {
launched = true;
+ intercept = true;
+ } else if (event.getEventTime() - mLastPowerDownWhileInteractive
+ < CAMERA_POWER_DOUBLE_TAP_TIME_MS) {
+ launched = true;
}
mLastPowerDownWhileNonInteractive = interactive ? 0 : event.getEventTime();
+ mLastPowerDownWhileInteractive = interactive ? event.getEventTime() : 0;
}
if (launched) {
Slog.i(TAG, "Power button double tap gesture detected, launching camera.");
launched = handleCameraLaunchGesture(false /* useWakelock */,
MetricsLogger.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE);
}
- return launched;
+ return intercept && launched;
}
/**
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index b418abaae4d4..9dad7a181a06 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -541,7 +541,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
prevSubtypes.addAll(entry.getValue());
}
- final String mergedImesAndSubtypesString = buildInputMethodsAndSubtypesString(prevMap);
+ final String mergedImesAndSubtypesString =
+ InputMethodUtils.buildInputMethodsAndSubtypesString(prevMap);
if (DEBUG_RESTORE) {
Slog.i(TAG, "Merged IME string:");
Slog.i(TAG, " " + mergedImesAndSubtypesString);
@@ -550,23 +551,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
Settings.Secure.ENABLED_INPUT_METHODS, mergedImesAndSubtypesString);
}
- // TODO: Move this method to InputMethodUtils with adding unit tests.
- static String buildInputMethodsAndSubtypesString(ArrayMap<String, ArraySet<String>> map) {
- // we want to use the canonical InputMethodSettings implementation,
- // so we convert data structures first.
- List<Pair<String, ArrayList<String>>> imeMap = new ArrayList<>(4);
- for (ArrayMap.Entry<String, ArraySet<String>> entry : map.entrySet()) {
- final String imeName = entry.getKey();
- final ArraySet<String> subtypeSet = entry.getValue();
- final ArrayList<String> subtypes = new ArrayList<>(2);
- if (subtypeSet != null) {
- subtypes.addAll(subtypeSet);
- }
- imeMap.add(new Pair<>(imeName, subtypes));
- }
- return InputMethodSettings.buildInputMethodsSettingString(imeMap);
- }
-
class MyPackageMonitor extends PackageMonitor {
private boolean isChangingPackagesOfCurrentUser() {
final int userId = getChangingUserId();
@@ -3504,7 +3488,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
throw new NullPointerException("methodMap is null");
}
mMethodMap = methodMap;
- final File systemDir = userId == UserHandle.USER_OWNER
+ final File systemDir = userId == UserHandle.USER_SYSTEM
? new File(Environment.getDataDirectory(), SYSTEM_PATH)
: Environment.getUserSystemDirectory(userId);
final File inputMethodDir = new File(systemDir, INPUT_METHOD_PATH);
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index c7e0c98ba9cf..da8152861edf 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -145,7 +145,8 @@ public class LockSettingsService extends ILockSettings.Stub {
} catch (RemoteException e) {
Slog.e(TAG, "Failure retrieving IGateKeeperService", e);
}
- mStorage.prefetchUser(UserHandle.USER_OWNER);
+ // TODO: maybe skip this for split system user mode.
+ mStorage.prefetchUser(UserHandle.USER_SYSTEM);
}
private void migrateOldData() {
diff --git a/services/core/java/com/android/server/LockSettingsStrongAuth.java b/services/core/java/com/android/server/LockSettingsStrongAuth.java
index c023f4aa17a0..0e4d5a7f2a7b 100644
--- a/services/core/java/com/android/server/LockSettingsStrongAuth.java
+++ b/services/core/java/com/android/server/LockSettingsStrongAuth.java
@@ -132,7 +132,7 @@ public class LockSettingsStrongAuth {
}
public void requireStrongAuth(int strongAuthReason, int userId) {
- if (userId == UserHandle.USER_ALL || userId >= UserHandle.USER_OWNER) {
+ if (userId == UserHandle.USER_ALL || userId >= UserHandle.USER_SYSTEM) {
mHandler.obtainMessage(MSG_REQUIRE_STRONG_AUTH, strongAuthReason,
userId).sendToTarget();
} else {
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index e0352e091af7..33f92344f615 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -500,7 +500,7 @@ public class MmsServiceBroker extends SystemService {
private Uri adjustUriForUserAndGrantPermission(Uri contentUri, String action,
int permission) {
final int callingUserId = UserHandle.getCallingUserId();
- if (callingUserId != UserHandle.USER_OWNER) {
+ if (callingUserId != UserHandle.USER_SYSTEM) {
contentUri = ContentProvider.maybeAddUserId(contentUri, callingUserId);
}
long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index aace66c7745d..fc3a322670ee 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -99,7 +99,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiver(new TextServicesBroadcastReceiver(), broadcastFilter);
- int userId = UserHandle.USER_OWNER;
+ int userId = UserHandle.USER_SYSTEM;
try {
ActivityManagerNative.getDefault().registerUserSwitchObserver(
new IUserSwitchObserver.Stub() {
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 30f4dce310ca..c2284224502d 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -59,6 +59,7 @@ public class VibratorService extends IVibratorService.Stub
implements InputManager.InputDeviceListener {
private static final String TAG = "VibratorService";
private static final boolean DEBUG = false;
+ private static final String SYSTEM_UI_PACKAGE = "com.android.systemui";
private final LinkedList<Vibration> mVibrations;
private final LinkedList<VibrationInfo> mPreviousVibrations;
@@ -147,7 +148,8 @@ public class VibratorService extends IVibratorService.Stub
}
public boolean isSystemHapticFeedback() {
- return (mUid == Process.SYSTEM_UID || mUid == 0) && mRepeat < 0;
+ return (mUid == Process.SYSTEM_UID || mUid == 0 || SYSTEM_UI_PACKAGE.equals(mOpPkg))
+ && mRepeat < 0;
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 14376d1bdb80..b21e90279308 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -19,7 +19,6 @@ package com.android.server.am;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.DOCKED_STACK_ID;
import static android.app.ActivityManager.HOME_STACK_ID;
import static android.app.ActivityManager.INVALID_STACK_ID;
@@ -215,7 +214,6 @@ import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.Trace;
import android.os.UpdateLock;
import android.os.UserHandle;
import android.os.UserManager;
@@ -8652,7 +8650,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
@Override
- public void resizeTask(int taskId, Rect bounds) {
+ public void resizeTask(int taskId, Rect bounds, boolean resizedByUser) {
enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
"resizeTask()");
long ident = Binder.clearCallingIdentity();
@@ -8663,7 +8661,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
return;
}
- mStackSupervisor.resizeTaskLocked(task, bounds);
+ mStackSupervisor.resizeTaskLocked(task, bounds, resizedByUser);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -9053,6 +9051,35 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ /**
+ * Moves the input task to the docked stack.
+ *
+ * @param taskId Id of task to move.
+ * @param createMode The mode the docked stack should be created in if it doesn't exist
+ * already. See
+ * {@link android.app.ActivityManager#DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT}
+ * and
+ * {@link android.app.ActivityManager#DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT}
+ * @param toTop If the task and stack should be moved to the top.
+ */
+ @Override
+ public void moveTaskToDockedStack(int taskId, int createMode, boolean toTop) {
+ enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+ "moveTaskToDockedStack()");
+ synchronized (this) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToDockedStack: moving task=" + taskId
+ + " to createMode=" + createMode + " toTop=" + toTop);
+ mWindowManager.setDockedStackCreateMode(createMode);
+ mStackSupervisor.moveTaskToStackLocked(
+ taskId, DOCKED_STACK_ID, toTop, !FORCE_FOCUS);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
@Override
public void resizeStack(int stackId, Rect bounds) {
enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
@@ -17633,7 +17660,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
if (starting != null) {
- kept = mainStack.ensureActivityConfigurationLocked(starting, changes);
+ kept = mainStack.ensureActivityConfigurationLocked(starting, changes, false);
// And we need to make sure at this point that all other activities
// are made visible with the correct configuration.
mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index a8a007378d53..3351226172ea 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1427,7 +1427,7 @@ final class ActivityStack {
// First: if this is not the current activity being started, make
// sure it matches the current configuration.
if (r != starting) {
- ensureActivityConfigurationLocked(r, 0);
+ ensureActivityConfigurationLocked(r, 0, false);
}
if (r.app == null || r.app.thread == null) {
@@ -3500,7 +3500,7 @@ final class ActivityStack {
final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
if (DEBUG_SWITCH || DEBUG_CLEANUP) Slog.v(TAG_SWITCH,
"Removing activity from " + reason + ": token=" + r
- + ", app=" + (r.app != null ? r.app.processName : "(null)"));
+ + ", app=" + (r.app != null ? r.app.processName : "(null)"));
EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
r.userId, System.identityHashCode(r),
r.task.taskId, r.shortComponentName, reason);
@@ -3977,7 +3977,8 @@ final class ActivityStack {
* for whatever reason. Ensures the HistoryRecord is updated with the
* correct configuration and all other bookkeeping is handled.
*/
- final boolean ensureActivityConfigurationLocked(ActivityRecord r, int globalChanges) {
+ final boolean ensureActivityConfigurationLocked(ActivityRecord r, int globalChanges,
+ boolean preserveWindow) {
if (mConfigWillChange) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Skipping config check (will change): " + r);
@@ -4062,12 +4063,14 @@ final class ActivityStack {
// "restart!".
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Config is relaunching resumed " + r);
- relaunchActivityLocked(r, r.configChangeFlags, true);
+ relaunchActivityLocked(r, r.configChangeFlags, true,
+ preserveWindow && isResizeOnlyChange(changes));
r.configChangeFlags = 0;
} else {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Config is relaunching non-resumed " + r);
- relaunchActivityLocked(r, r.configChangeFlags, false);
+ relaunchActivityLocked(r, r.configChangeFlags, false,
+ preserveWindow && isResizeOnlyChange(changes));
r.configChangeFlags = 0;
}
@@ -4160,7 +4163,13 @@ final class ActivityStack {
return taskChanges;
}
- private boolean relaunchActivityLocked(ActivityRecord r, int changes, boolean andResume) {
+ private static boolean isResizeOnlyChange(int change) {
+ return (change & ~(ActivityInfo.CONFIG_SCREEN_SIZE
+ | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) == 0;
+ }
+
+ private boolean relaunchActivityLocked(ActivityRecord r, int changes, boolean andResume,
+ boolean preserveWindow) {
List<ResultInfo> results = null;
List<ReferrerIntent> newIntents = null;
if (andResume) {
@@ -4169,7 +4178,7 @@ final class ActivityStack {
}
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
"Relaunching: " + r + " with results=" + results + " newIntents=" + newIntents
- + " andResume=" + andResume);
+ + " andResume=" + andResume + " preserveWindow=" + preserveWindow);
EventLog.writeEvent(andResume ? EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY
: EventLogTags.AM_RELAUNCH_ACTIVITY, r.userId, System.identityHashCode(r),
r.task.taskId, r.shortComponentName);
@@ -4184,7 +4193,7 @@ final class ActivityStack {
r.forceNewConfig = false;
r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, changes,
!andResume, new Configuration(mService.mConfiguration),
- new Configuration(r.task.mOverrideConfig));
+ new Configuration(r.task.mOverrideConfig), preserveWindow);
// Note: don't need to call pauseIfSleepingLocked() here, because
// the caller will only pass in 'andResume' if this activity is
// currently resumed, which implies we aren't sleeping.
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 8ab4ae59a201..d3cea8de1442 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3045,7 +3045,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
stack.setBounds(bounds);
if (r != null) {
- final boolean updated = stack.ensureActivityConfigurationLocked(r, 0);
+ final boolean updated = stack.ensureActivityConfigurationLocked(r, 0, false);
// And we need to make sure at this point that all other activities
// are made visible with the correct configuration.
ensureActivitiesVisibleLocked(r, 0);
@@ -3055,7 +3055,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
}
- void resizeTaskLocked(TaskRecord task, Rect bounds) {
+ void resizeTaskLocked(TaskRecord task, Rect bounds, boolean resizedByUser) {
if (!task.mResizeable) {
Slog.w(TAG, "resizeTask: task " + task + " not resizeable.");
return;
@@ -3088,7 +3088,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
&& stackId != FREEFORM_WORKSPACE_STACK_ID && stackId != DOCKED_STACK_ID) {
stackId = FREEFORM_WORKSPACE_STACK_ID;
}
- if (stackId != task.stack.mStackId) {
+ final boolean changedStacks = stackId != task.stack.mStackId;
+ if (changedStacks) {
moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, !FORCE_FOCUS, "resizeTask");
}
@@ -3101,20 +3102,22 @@ public final class ActivityStackSupervisor implements DisplayListener {
ActivityRecord r = task.topRunningActivityLocked(null);
if (r != null) {
final ActivityStack stack = task.stack;
- kept = stack.ensureActivityConfigurationLocked(r, 0);
+ final boolean preserveWindow = resizedByUser && !changedStacks;
+ kept = stack.ensureActivityConfigurationLocked(r, 0, preserveWindow);
// All other activities must be made visible with their correct configuration.
ensureActivitiesVisibleLocked(r, 0);
if (!kept) {
resumeTopActivitiesLocked(stack, null, null);
- // We are about to relaunch the activity because its configuration changed due
- // to size change. The activity will first remove the old window and then add a
- // new one. This call will tell window manager about this, so it can preserve
- // the old window until the new one is drawn. This prevents having a gap between
- // the removal and addition, in which no window is visible. If we also changed
- // the stack to the fullscreen stack, i.e. maximized the window, we will animate
- // the transition.
- mWindowManager.setReplacingWindow(r.appToken,
- stackId == FULLSCREEN_WORKSPACE_STACK_ID /* animate */);
+ if (changedStacks && stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ // We are about to relaunch the activity because its configuration changed
+ // due to being maximized, i.e. size change. The activity will first
+ // remove the old window and then add a new one. This call will tell window
+ // manager about this, so it can preserve the old window until the new
+ // one is drawn. This prevents having a gap between the removal and
+ // addition, in which no window is visible. We also want the entrace of the
+ // new window to be properly animated.
+ mWindowManager.setReplacingWindow(r.appToken, true /* animate */);
+ }
}
}
}
@@ -3240,12 +3243,12 @@ public final class ActivityStackSupervisor implements DisplayListener {
// Make sure the task has the appropriate bounds/size for the stack it is in.
if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) {
- resizeTaskLocked(task, stack.mBounds);
+ resizeTaskLocked(task, stack.mBounds, false);
} else if (stackId == FREEFORM_WORKSPACE_STACK_ID
&& task.mBounds == null && task.mLastNonFullscreenBounds != null) {
- resizeTaskLocked(task, task.mLastNonFullscreenBounds);
+ resizeTaskLocked(task, task.mLastNonFullscreenBounds, false);
} else if (stackId == DOCKED_STACK_ID) {
- resizeTaskLocked(task, stack.mBounds);
+ resizeTaskLocked(task, stack.mBounds, false);
}
// The task might have already been running and its visibility needs to be synchronized with
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java
index 814e8b42e9d7..424ceb1a457c 100644
--- a/services/core/java/com/android/server/am/CompatModePackages.java
+++ b/services/core/java/com/android/server/am/CompatModePackages.java
@@ -344,7 +344,7 @@ public final class CompatModePackages {
}
if (starting != null) {
- stack.ensureActivityConfigurationLocked(starting, 0);
+ stack.ensureActivityConfigurationLocked(starting, 0, false);
// And we need to make sure at this point that all other activities
// are made visible with the correct configuration.
stack.ensureActivitiesVisibleLocked(starting, 0);
diff --git a/services/core/java/com/android/server/am/ProviderMap.java b/services/core/java/com/android/server/am/ProviderMap.java
index ed8b1dd7fcd5..878c0e7aba50 100644
--- a/services/core/java/com/android/server/am/ProviderMap.java
+++ b/services/core/java/com/android/server/am/ProviderMap.java
@@ -211,7 +211,7 @@ public final class ProviderMap {
boolean doit, boolean evenPersistent, int userId,
ArrayList<ContentProviderRecord> result) {
boolean didSomething = false;
- if (userId == UserHandle.USER_ALL || userId == UserHandle.USER_OWNER) {
+ if (userId == UserHandle.USER_ALL || userId == UserHandle.USER_SYSTEM) {
didSomething = collectPackageProvidersLocked(packageName, filterByClasses,
doit, evenPersistent, mSingletonByClass, result);
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 1475f2f292cc..99ca050c9ae7 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1560,7 +1560,7 @@ public class AudioService extends IAudioService.Stub {
} finally {
Binder.restoreCallingIdentity(ident);
}
- return UserHandle.USER_OWNER;
+ return UserHandle.USER_SYSTEM;
}
// UI update and Broadcast Intent
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
index aca699152e02..5fd39c02a10a 100644
--- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -18,6 +18,7 @@ package com.android.server.connectivity;
import static android.system.OsConstants.*;
+import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkUtils;
@@ -27,6 +28,7 @@ import android.system.ErrnoException;
import android.system.Os;
import android.system.StructTimeval;
import android.text.TextUtils;
+import android.util.Pair;
import com.android.internal.util.IndentingPrintWriter;
@@ -149,6 +151,8 @@ public class NetworkDiagnostics {
}
private final Map<InetAddress, Measurement> mIcmpChecks = new HashMap<>();
+ private final Map<Pair<InetAddress, InetAddress>, Measurement> mExplicitSourceIcmpChecks =
+ new HashMap<>();
private final Map<InetAddress, Measurement> mDnsUdpChecks = new HashMap<>();
private final String mDescription;
@@ -178,7 +182,11 @@ public class NetworkDiagnostics {
for (RouteInfo route : mLinkProperties.getRoutes()) {
if (route.hasGateway()) {
- prepareIcmpMeasurement(route.getGateway());
+ InetAddress gateway = route.getGateway();
+ prepareIcmpMeasurement(gateway);
+ if (route.isIPv6Default()) {
+ prepareExplicitSourceIcmpMeasurements(gateway);
+ }
}
}
for (InetAddress nameserver : mLinkProperties.getDnsServers()) {
@@ -213,6 +221,20 @@ public class NetworkDiagnostics {
}
}
+ private void prepareExplicitSourceIcmpMeasurements(InetAddress target) {
+ for (LinkAddress l : mLinkProperties.getLinkAddresses()) {
+ InetAddress source = l.getAddress();
+ if (source instanceof Inet6Address && l.isGlobalPreferred()) {
+ Pair<InetAddress, InetAddress> srcTarget = new Pair<>(source, target);
+ if (!mExplicitSourceIcmpChecks.containsKey(srcTarget)) {
+ Measurement measurement = new Measurement();
+ measurement.thread = new Thread(new IcmpCheck(source, target, measurement));
+ mExplicitSourceIcmpChecks.put(srcTarget, measurement);
+ }
+ }
+ }
+ }
+
private void prepareDnsMeasurement(InetAddress target) {
if (!mDnsUdpChecks.containsKey(target)) {
Measurement measurement = new Measurement();
@@ -222,13 +244,16 @@ public class NetworkDiagnostics {
}
private int totalMeasurementCount() {
- return mIcmpChecks.size() + mDnsUdpChecks.size();
+ return mIcmpChecks.size() + mExplicitSourceIcmpChecks.size() + mDnsUdpChecks.size();
}
private void startMeasurements() {
for (Measurement measurement : mIcmpChecks.values()) {
measurement.thread.start();
}
+ for (Measurement measurement : mExplicitSourceIcmpChecks.values()) {
+ measurement.thread.start();
+ }
for (Measurement measurement : mDnsUdpChecks.values()) {
measurement.thread.start();
}
@@ -261,6 +286,10 @@ public class NetworkDiagnostics {
pw.println(entry.getValue().toString());
}
}
+ for (Map.Entry<Pair<InetAddress, InetAddress>, Measurement> entry :
+ mExplicitSourceIcmpChecks.entrySet()) {
+ pw.println(entry.getValue().toString());
+ }
for (Map.Entry<InetAddress, Measurement> entry : mDnsUdpChecks.entrySet()) {
if (entry.getKey() instanceof Inet4Address) {
pw.println(entry.getValue().toString());
@@ -276,13 +305,15 @@ public class NetworkDiagnostics {
private class SimpleSocketCheck implements Closeable {
+ protected final InetAddress mSource; // Usually null.
protected final InetAddress mTarget;
protected final int mAddressFamily;
protected final Measurement mMeasurement;
protected FileDescriptor mFileDescriptor;
protected SocketAddress mSocketAddress;
- protected SimpleSocketCheck(InetAddress target, Measurement measurement) {
+ protected SimpleSocketCheck(
+ InetAddress source, InetAddress target, Measurement measurement) {
mMeasurement = measurement;
if (target instanceof Inet6Address) {
@@ -301,6 +332,14 @@ public class NetworkDiagnostics {
mTarget = target;
mAddressFamily = AF_INET;
}
+
+ // We don't need to check the scope ID here because we currently only do explicit-source
+ // measurements from global IPv6 addresses.
+ mSource = source;
+ }
+
+ protected SimpleSocketCheck(InetAddress target, Measurement measurement) {
+ this(null, target, measurement);
}
protected void setupSocket(
@@ -314,6 +353,9 @@ public class NetworkDiagnostics {
SOL_SOCKET, SO_RCVTIMEO, StructTimeval.fromMillis(readTimeout));
// TODO: Use IP_RECVERR/IPV6_RECVERR, pending OsContants availability.
mNetwork.bindSocket(mFileDescriptor);
+ if (mSource != null) {
+ Os.bind(mFileDescriptor, mSource, 0);
+ }
Os.connect(mFileDescriptor, mTarget, dstPort);
mSocketAddress = Os.getsockname(mFileDescriptor);
}
@@ -343,8 +385,8 @@ public class NetworkDiagnostics {
private final int mProtocol;
private final int mIcmpType;
- public IcmpCheck(InetAddress target, Measurement measurement) {
- super(target, measurement);
+ public IcmpCheck(InetAddress source, InetAddress target, Measurement measurement) {
+ super(source, target, measurement);
if (mAddressFamily == AF_INET6) {
mProtocol = IPPROTO_ICMPV6;
@@ -359,6 +401,10 @@ public class NetworkDiagnostics {
mMeasurement.description += " dst{" + mTarget.getHostAddress() + "}";
}
+ public IcmpCheck(InetAddress target, Measurement measurement) {
+ this(null, target, measurement);
+ }
+
@Override
public void run() {
// Check if this measurement has already failed during setup.
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index ea7d85e36944..ec7c1c437d92 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -337,7 +337,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
return;
}
stopPendingOperations(true);
- mEnrollClient = new ClientMonitor(token, receiver, groupId, restricted);
+ mEnrollClient = new ClientMonitor(token, receiver, groupId, restricted, token.toString());
final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
try {
final int result = daemon.enroll(cryptoToken, groupId, timeout);
@@ -417,14 +417,15 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
}
void startAuthentication(IBinder token, long opId, int groupId,
- IFingerprintServiceReceiver receiver, int flags, boolean restricted) {
+ IFingerprintServiceReceiver receiver, int flags, boolean restricted,
+ String opPackageName) {
IFingerprintDaemon daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "startAuthentication: no fingeprintd!");
return;
}
stopPendingOperations(true);
- mAuthClient = new ClientMonitor(token, receiver, groupId, restricted);
+ mAuthClient = new ClientMonitor(token, receiver, groupId, restricted, opPackageName);
if (inLockoutMode()) {
Slog.v(TAG, "In lockout mode; disallowing authentication");
if (!mAuthClient.sendError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) {
@@ -481,7 +482,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
}
stopPendingOperations(true);
- mRemoveClient = new ClientMonitor(token, receiver, userId, restricted);
+ mRemoveClient = new ClientMonitor(token, receiver, userId, restricted, token.toString());
// The fingerprint template ids will be removed when we get confirmation from the HAL
try {
final int result = daemon.remove(fingerId, userId);
@@ -574,11 +575,11 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
}
if (mAppOps.noteOp(AppOpsManager.OP_USE_FINGERPRINT, uid, opPackageName)
!= AppOpsManager.MODE_ALLOWED) {
- Slog.v(TAG, "Rejecting " + opPackageName + " ; permission denied");
+ Slog.w(TAG, "Rejecting " + opPackageName + " ; permission denied");
return false;
}
if (foregroundOnly && !isForegroundActivity(uid, pid)) {
- Slog.v(TAG, "Rejecting " + opPackageName + " ; not in foreground");
+ Slog.w(TAG, "Rejecting " + opPackageName + " ; not in foreground");
return false;
}
return true;
@@ -606,13 +607,15 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
IFingerprintServiceReceiver receiver;
int userId;
boolean restricted; // True if client does not have MANAGE_FINGERPRINT permission
+ String owner;
public ClientMonitor(IBinder token, IFingerprintServiceReceiver receiver, int userId,
- boolean restricted) {
+ boolean restricted, String owner) {
this.token = token;
this.receiver = receiver;
this.userId = userId;
this.restricted = restricted;
+ this.owner = owner; // name of the client that owns this - for debugging
try {
token.linkToDeath(this, 0);
} catch (RemoteException e) {
@@ -695,6 +698,10 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
if (!authenticated) {
receiver.onAuthenticationFailed(mHalDeviceId);
} else {
+ if (DEBUG) {
+ Slog.v(TAG, "onAuthenticated(owner=" + mAuthClient.owner
+ + ", id=" + fpId + ", gp=" + groupId + ")");
+ }
Fingerprint fp = !restricted ?
new Fingerprint("" /* TODO */, groupId, fpId, mHalDeviceId) : null;
receiver.onAuthenticationSucceeded(mHalDeviceId, fp);
@@ -915,6 +922,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
final IFingerprintServiceReceiver receiver, final int flags,
final String opPackageName) {
if (!canUseFingerprint(opPackageName, true /* foregroundOnly */)) {
+ if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
return;
}
@@ -927,7 +935,8 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
@Override
public void run() {
MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0);
- startAuthentication(token, opId, effectiveGroupId, receiver, flags, restricted);
+ startAuthentication(token, opId, effectiveGroupId, receiver, flags, restricted,
+ opPackageName);
}
});
}
diff --git a/services/core/java/com/android/server/firewall/SenderPackageFilter.java b/services/core/java/com/android/server/firewall/SenderPackageFilter.java
index ec9b5ded511e..dc3edd9c7888 100644
--- a/services/core/java/com/android/server/firewall/SenderPackageFilter.java
+++ b/services/core/java/com/android/server/firewall/SenderPackageFilter.java
@@ -44,7 +44,9 @@ public class SenderPackageFilter implements Filter {
int packageUid = -1;
try {
- packageUid = pm.getPackageUid(mPackageName, UserHandle.USER_OWNER);
+ // USER_SYSTEM here is not important. Only app id is used and getPackageUid() will
+ // return a uid whether the app is installed for a user or not.
+ packageUid = pm.getPackageUid(mPackageName, UserHandle.USER_SYSTEM);
} catch (RemoteException ex) {
// handled below
}
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index 5e5a55cc2249..bc9f5205e1c5 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -22,8 +22,6 @@ import com.android.internal.location.GpsNetInitiatedHandler;
import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
import android.app.AlarmManager;
import android.app.AppOpsManager;
@@ -50,7 +48,10 @@ import android.location.LocationManager;
import android.location.LocationProvider;
import android.location.LocationRequest;
import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
+import android.net.NetworkRequest;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.BatteryStats;
@@ -199,6 +200,8 @@ public class GpsLocationProvider implements LocationProviderInterface {
private static final int DOWNLOAD_XTRA_DATA_FINISHED = 11;
private static final int SUBSCRIPTION_OR_SIM_CHANGED = 12;
private static final int INITIALIZE_HANDLER = 13;
+ private static final int REQUEST_SUPL_CONNECTION = 14;
+ private static final int RELEASE_SUPL_CONNECTION = 15;
// Request setid
private static final int AGPS_RIL_REQUEST_SETID_IMSI = 1;
@@ -295,9 +298,6 @@ public class GpsLocationProvider implements LocationProviderInterface {
// true if we are enabled, protected by this
private boolean mEnabled;
- // true if we have network connectivity
- private boolean mNetworkAvailable;
-
// states for injecting ntp and downloading xtra data
private static final int STATE_PENDING_NETWORK = 0;
private static final int STATE_DOWNLOADING = 1;
@@ -372,10 +372,11 @@ public class GpsLocationProvider implements LocationProviderInterface {
// Handler for processing events
private Handler mHandler;
- private String mAGpsApn;
- private int mApnIpType;
+ /** It must be accessed only inside {@link #mHandler}. */
private int mAGpsDataConnectionState;
+ /** It must be accessed only inside {@link #mHandler}. */
private InetAddress mAGpsDataConnectionIpAddr;
+
private final ConnectivityManager mConnMgr;
private final GpsNetInitiatedHandler mNIHandler;
@@ -431,6 +432,42 @@ public class GpsLocationProvider implements LocationProviderInterface {
return mGpsNavigationMessageProvider;
}
+ /**
+ * Callback used to listen for data connectivity changes.
+ */
+ private final ConnectivityManager.NetworkCallback mNetworkConnectivityCallback =
+ new ConnectivityManager.NetworkCallback() {
+ @Override
+ public void onAvailable(Network network) {
+ requestUtcTime();
+ xtraDownloadRequest();
+ }
+ };
+
+ /**
+ * Callback used to listen for availability of a requested SUPL connection.
+ * It is kept as a separate instance from {@link #mNetworkConnectivityCallback} to be able to
+ * manage the registration/un-registration lifetimes separate.
+ */
+ private final ConnectivityManager.NetworkCallback mSuplConnectivityCallback =
+ new ConnectivityManager.NetworkCallback() {
+ @Override
+ public void onAvailable(Network network) {
+ sendMessage(UPDATE_NETWORK_STATE, 0 /*arg*/, network);
+ }
+
+ @Override
+ public void onLost(Network network) {
+ releaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN);
+ }
+
+ @Override
+ public void onUnavailable() {
+ // timeout, it was not possible to establish the required connection
+ releaseSuplConnection(GPS_AGPS_DATA_CONN_FAILED);
+ }
+ };
+
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
@@ -447,16 +484,6 @@ public class GpsLocationProvider implements LocationProviderInterface {
checkSmsSuplInit(intent);
} else if (action.equals(Intents.WAP_PUSH_RECEIVED_ACTION)) {
checkWapSuplInit(intent);
- } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)
- || action.equals(ConnectivityManager.CONNECTIVITY_ACTION_SUPL)) {
- // retrieve NetworkType result for this UID
- int networkType = intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, -1);
- if (DEBUG) Log.d(TAG, "Connectivity action, type=" + networkType);
- if (networkType == ConnectivityManager.TYPE_MOBILE
- || networkType == ConnectivityManager.TYPE_WIFI
- || networkType == ConnectivityManager.TYPE_MOBILE_SUPL) {
- updateNetworkState(networkType);
- }
} else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)
|| PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)
|| Intent.ACTION_SCREEN_OFF.equals(action)
@@ -574,7 +601,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
native_configuration_update(baos.toString());
Log.d(TAG, "final config = " + baos.toString());
} catch (IOException ex) {
- Log.w(TAG, "failed to dump properties contents");
+ Log.e(TAG, "failed to dump properties contents");
}
} else if (DEBUG) {
Log.d(TAG, "Skipped configuration update because GNSS configuration in GPS HAL is not"
@@ -740,78 +767,117 @@ public class GpsLocationProvider implements LocationProviderInterface {
return PROPERTIES;
}
- public void updateNetworkState(int networkType) {
- sendMessage(UPDATE_NETWORK_STATE, networkType, null /*obj*/);
- }
-
- private void handleUpdateNetworkState(int networkType) {
- ConnectivityManager connectivityManager = (ConnectivityManager) mContext
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo mobileInfo =
- connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
- NetworkInfo wifiInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
- mNetworkAvailable = (mobileInfo != null && mobileInfo.isConnected())
- || (wifiInfo != null && wifiInfo.isConnected());
- NetworkInfo info = connectivityManager.getNetworkInfo(networkType);
- if (DEBUG) {
- Log.d(TAG, "updateNetworkState " + (mNetworkAvailable ? "available" : "unavailable")
- + " info: " + info);
+ private void handleUpdateNetworkState(Network network) {
+ // retrieve NetworkInfo for this UID
+ NetworkInfo info = mConnMgr.getNetworkInfo(network);
+ if (info == null) {
+ return;
}
- if (info != null) {
- if (native_is_agps_ril_supported()) {
- boolean dataEnabled = TelephonyManager.getDefault().getDataEnabled();
- boolean networkAvailable = info.isAvailable() && dataEnabled;
- String defaultApn = getSelectedApn();
- if (defaultApn == null) {
- defaultApn = "dummy-apn";
- }
-
- native_update_network_state(info.isConnected(), info.getType(),
- info.isRoaming(), networkAvailable,
- info.getExtraInfo(), defaultApn);
- } else if (DEBUG) {
- Log.d(TAG, "Skipped network state update because AGPS-RIL in GPS HAL is not"
- + " supported");
+ boolean isConnected = info.isConnected();
+ if (DEBUG) {
+ String message = String.format(
+ "UpdateNetworkState, state=%s, connected=%s, info=%s, capabilities=%S",
+ agpsDataConnStateAsString(),
+ isConnected,
+ info,
+ mConnMgr.getNetworkCapabilities(network));
+ Log.d(TAG, message);
+ }
+
+ if (native_is_agps_ril_supported()) {
+ boolean dataEnabled = TelephonyManager.getDefault().getDataEnabled();
+ boolean networkAvailable = info.isAvailable() && dataEnabled;
+ String defaultApn = getSelectedApn();
+ if (defaultApn == null) {
+ defaultApn = "dummy-apn";
}
+
+ native_update_network_state(
+ isConnected,
+ info.getType(),
+ info.isRoaming(),
+ networkAvailable,
+ info.getExtraInfo(),
+ defaultApn);
+ } else if (DEBUG) {
+ Log.d(TAG, "Skipped network state update because GPS HAL AGPS-RIL is not supported");
}
- if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE_SUPL
- && mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
- if (info.isConnected()) {
+ if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
+ if (isConnected) {
String apnName = info.getExtraInfo();
if (apnName == null) {
- /* Assign a dummy value in the case of C2K as otherwise we will have a runtime
- exception in the following call to native_agps_data_conn_open*/
+ // assign a dummy value in the case of C2K as otherwise we will have a runtime
+ // exception in the following call to native_agps_data_conn_open
apnName = "dummy-apn";
}
- mAGpsApn = apnName;
- mApnIpType = getApnIpType(apnName);
+ int apnIpType = getApnIpType(apnName);
setRouting();
if (DEBUG) {
String message = String.format(
"native_agps_data_conn_open: mAgpsApn=%s, mApnIpType=%s",
- mAGpsApn, mApnIpType);
+ apnName,
+ apnIpType);
Log.d(TAG, message);
}
- native_agps_data_conn_open(mAGpsApn, mApnIpType);
+ native_agps_data_conn_open(apnName, apnIpType);
mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
} else {
- Log.e(TAG, "call native_agps_data_conn_failed, info: " + info);
- mAGpsApn = null;
- mApnIpType = APN_INVALID;
- mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
- native_agps_data_conn_failed();
+ handleReleaseSuplConnection(GPS_AGPS_DATA_CONN_FAILED);
}
}
+ }
- if (mNetworkAvailable) {
- if (mInjectNtpTimePending == STATE_PENDING_NETWORK) {
- sendMessage(INJECT_NTP_TIME, 0, null);
- }
- if (mDownloadXtraDataPending == STATE_PENDING_NETWORK) {
- sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
- }
+ private void handleRequestSuplConnection(InetAddress address) {
+ if (DEBUG) {
+ String message = String.format(
+ "requestSuplConnection, state=%s, address=%s",
+ agpsDataConnStateAsString(),
+ address);
+ Log.d(TAG, message);
+ }
+
+ if (mAGpsDataConnectionState != AGPS_DATA_CONNECTION_CLOSED) {
+ return;
+ }
+ mAGpsDataConnectionIpAddr = address;
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
+
+ NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder();
+ requestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ requestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
+ NetworkRequest request = requestBuilder.build();
+ mConnMgr.requestNetwork(
+ request,
+ mSuplConnectivityCallback,
+ ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS);
+ }
+
+ private void handleReleaseSuplConnection(int agpsDataConnStatus) {
+ if (DEBUG) {
+ String message = String.format(
+ "releaseSuplConnection, state=%s, status=%s",
+ agpsDataConnStateAsString(),
+ agpsDataConnStatusAsString(agpsDataConnStatus));
+ Log.d(TAG, message);
+ }
+
+ if (mAGpsDataConnectionState == AGPS_DATA_CONNECTION_CLOSED) {
+ return;
+ }
+ mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
+
+ mConnMgr.unregisterNetworkCallback(mSuplConnectivityCallback);
+ switch (agpsDataConnStatus) {
+ case GPS_AGPS_DATA_CONN_FAILED:
+ native_agps_data_conn_failed();
+ break;
+ case GPS_RELEASE_AGPS_DATA_CONN:
+ native_agps_data_conn_closed();
+ break;
+ default:
+ Log.e(TAG, "Invalid status to release SUPL connection: " + agpsDataConnStatus);
}
}
@@ -820,7 +886,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
// already downloading data
return;
}
- if (!mNetworkAvailable) {
+ if (!isDataNetworkConnected()) {
// try again when network is up
mInjectNtpTimePending = STATE_PENDING_NETWORK;
return;
@@ -888,7 +954,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
// already downloading data
return;
}
- if (!mNetworkAvailable) {
+ if (!isDataNetworkConnected()) {
// try again when network is up
mDownloadXtraDataPending = STATE_PENDING_NETWORK;
return;
@@ -903,9 +969,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mProperties);
byte[] data = xtraDownloader.downloadXtraData();
if (data != null) {
- if (DEBUG) {
- Log.d(TAG, "calling native_inject_xtra_data");
- }
+ if (DEBUG) Log.d(TAG, "calling native_inject_xtra_data");
native_inject_xtra_data(data, data.length);
mXtraBackOff.reset();
}
@@ -1209,7 +1273,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
if ("delete_aiding_data".equals(command)) {
result = deleteAidingData(extras);
} else if ("force_time_injection".equals(command)) {
- sendMessage(INJECT_NTP_TIME, 0, null);
+ requestUtcTime();
result = true;
} else if ("force_xtra_injection".equals(command)) {
if (mSupportsXtra) {
@@ -1536,51 +1600,20 @@ public class GpsLocationProvider implements LocationProviderInterface {
case GPS_REQUEST_AGPS_DATA_CONN:
if (DEBUG) Log.d(TAG, "GPS_REQUEST_AGPS_DATA_CONN");
Log.v(TAG, "Received SUPL IP addr[]: " + ipaddr);
- // Set mAGpsDataConnectionState before calling startUsingNetworkFeature
- // to avoid a race condition with handleUpdateNetworkState()
- mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
- int result = mConnMgr.startUsingNetworkFeature(
- ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
+ InetAddress connectionIpAddress = null;
if (ipaddr != null) {
try {
- mAGpsDataConnectionIpAddr = InetAddress.getByAddress(ipaddr);
- Log.v(TAG, "IP address converted to: " + mAGpsDataConnectionIpAddr);
+ connectionIpAddress = InetAddress.getByAddress(ipaddr);
+ if (DEBUG) Log.d(TAG, "IP address converted to: " + connectionIpAddress);
} catch (UnknownHostException e) {
Log.e(TAG, "Bad IP Address: " + ipaddr, e);
- mAGpsDataConnectionIpAddr = null;
- }
- }
-
- if (result == PhoneConstants.APN_ALREADY_ACTIVE) {
- if (DEBUG) Log.d(TAG, "PhoneConstants.APN_ALREADY_ACTIVE");
- if (mAGpsApn != null) {
- setRouting();
- native_agps_data_conn_open(mAGpsApn, mApnIpType);
- mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
- } else {
- Log.e(TAG, "mAGpsApn not set when receiving PhoneConstants.APN_ALREADY_ACTIVE");
- mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
- native_agps_data_conn_failed();
}
- } else if (result == PhoneConstants.APN_REQUEST_STARTED) {
- if (DEBUG) Log.d(TAG, "PhoneConstants.APN_REQUEST_STARTED");
- // Nothing to do here
- } else {
- if (DEBUG) Log.d(TAG, "startUsingNetworkFeature failed, value is " +
- result);
- mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
- native_agps_data_conn_failed();
}
+ sendMessage(REQUEST_SUPL_CONNECTION, 0 /*arg*/, connectionIpAddress);
break;
case GPS_RELEASE_AGPS_DATA_CONN:
if (DEBUG) Log.d(TAG, "GPS_RELEASE_AGPS_DATA_CONN");
- if (mAGpsDataConnectionState != AGPS_DATA_CONNECTION_CLOSED) {
- mConnMgr.stopUsingNetworkFeature(
- ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
- native_agps_data_conn_closed();
- mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
- mAGpsDataConnectionIpAddr = null;
- }
+ releaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN);
break;
case GPS_AGPS_DATA_CONNECTED:
if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONNECTED");
@@ -1596,6 +1629,10 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
}
+ private void releaseSuplConnection(int connStatus) {
+ sendMessage(RELEASE_SUPL_CONNECTION, connStatus, null /*obj*/);
+ }
+
/**
* called from native code to report NMEA data received
*/
@@ -1921,8 +1958,8 @@ public class GpsLocationProvider implements LocationProviderInterface {
/**
* Called from native code to request utc time info
*/
-
private void requestUtcTime() {
+ if (DEBUG) Log.d(TAG, "utcTimeRequest");
sendMessage(INJECT_NTP_TIME, 0, null);
}
@@ -1990,7 +2027,13 @@ public class GpsLocationProvider implements LocationProviderInterface {
handleSetRequest(gpsRequest.request, gpsRequest.source);
break;
case UPDATE_NETWORK_STATE:
- handleUpdateNetworkState(msg.arg1);
+ handleUpdateNetworkState((Network) msg.obj);
+ break;
+ case REQUEST_SUPL_CONNECTION:
+ handleRequestSuplConnection((InetAddress) msg.obj);
+ break;
+ case RELEASE_SUPL_CONNECTION:
+ handleReleaseSuplConnection(msg.arg1);
break;
case INJECT_NTP_TIME:
handleInjectNtpTime();
@@ -2007,13 +2050,13 @@ public class GpsLocationProvider implements LocationProviderInterface {
mDownloadXtraDataPending = STATE_IDLE;
break;
case UPDATE_LOCATION:
- handleUpdateLocation((Location)msg.obj);
+ handleUpdateLocation((Location) msg.obj);
break;
case SUBSCRIPTION_OR_SIM_CHANGED:
subscriptionOrSimChanged(mContext);
break;
case INITIALIZE_HANDLER:
- initialize();
+ handleInitialize();
break;
}
if (msg.arg2 == 1) {
@@ -2027,7 +2070,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
* It is in charge of loading properties and registering for events that will be posted to
* this handler.
*/
- private void initialize() {
+ private void handleInitialize() {
// load default GPS configuration
// (this configuration might change in the future based on SIM changes)
reloadGpsProperties(mContext, mProperties);
@@ -2067,8 +2110,6 @@ public class GpsLocationProvider implements LocationProviderInterface {
intentFilter = new IntentFilter();
intentFilter.addAction(ALARM_WAKEUP);
intentFilter.addAction(ALARM_TIMEOUT);
- intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
- intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION_SUPL);
intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
@@ -2076,6 +2117,14 @@ public class GpsLocationProvider implements LocationProviderInterface {
intentFilter.addAction(SIM_STATE_CHANGED);
mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
+ // register for connectivity change events, this is equivalent to the deprecated way of
+ // registering for CONNECTIVITY_ACTION broadcasts
+ NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
+ networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ NetworkRequest networkRequest = networkRequestBuilder.build();
+ mConnMgr.registerNetworkCallback(networkRequest, mNetworkConnectivityCallback);
+
// listen for PASSIVE_PROVIDER updates
LocationManager locManager =
(LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
@@ -2140,15 +2189,11 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
private int getApnIpType(String apn) {
+ ensureInHandlerThread();
if (apn == null) {
return APN_INVALID;
}
- // look for cached data to use
- if (apn.equals(mAGpsApn) && mApnIpType != APN_INVALID) {
- return mApnIpType;
- }
-
String selection = String.format("current = 1 and apn = '%s' and carrier_enabled = 1", apn);
Cursor cursor = null;
try {
@@ -2197,6 +2242,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
return;
}
+ // TODO: replace the use of this deprecated API
boolean result = mConnMgr.requestRouteToHostAddress(
ConnectivityManager.TYPE_MOBILE_SUPL,
mAGpsDataConnectionIpAddr);
@@ -2208,6 +2254,61 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
}
+ /**
+ * @return {@code true} if there is a data network available for outgoing connections,
+ * {@code false} otherwise.
+ */
+ private boolean isDataNetworkConnected() {
+ NetworkInfo activeNetworkInfo = mConnMgr.getActiveNetworkInfo();
+ return activeNetworkInfo != null && activeNetworkInfo.isConnected();
+ }
+
+ /**
+ * Ensures the calling function is running in the thread associated with {@link #mHandler}.
+ */
+ private void ensureInHandlerThread() {
+ if (mHandler != null && Looper.myLooper() == mHandler.getLooper()) {
+ return;
+ }
+ throw new RuntimeException("This method must run on the Handler thread.");
+ }
+
+ /**
+ * @return A string representing the current state stored in {@link #mAGpsDataConnectionState}.
+ */
+ private String agpsDataConnStateAsString() {
+ switch(mAGpsDataConnectionState) {
+ case AGPS_DATA_CONNECTION_CLOSED:
+ return "CLOSED";
+ case AGPS_DATA_CONNECTION_OPEN:
+ return "OPEN";
+ case AGPS_DATA_CONNECTION_OPENING:
+ return "OPENING";
+ default:
+ return "<Unknown>";
+ }
+ }
+
+ /**
+ * @return A string representing the given GPS_AGPS_DATA status.
+ */
+ private String agpsDataConnStatusAsString(int agpsDataConnStatus) {
+ switch (agpsDataConnStatus) {
+ case GPS_AGPS_DATA_CONNECTED:
+ return "CONNECTED";
+ case GPS_AGPS_DATA_CONN_DONE:
+ return "DONE";
+ case GPS_AGPS_DATA_CONN_FAILED:
+ return "FAILED";
+ case GPS_RELEASE_AGPS_DATA_CONN:
+ return "RELEASE";
+ case GPS_REQUEST_AGPS_DATA_CONN:
+ return "REQUEST";
+ default:
+ return "<Unknown>";
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
StringBuilder s = new StringBuilder();
@@ -2343,4 +2444,3 @@ public class GpsLocationProvider implements LocationProviderInterface {
// GNSS Configuration
private static native void native_configuration_update(String configData);
}
-
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index 96a5e0057d61..b5046056e913 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -245,6 +245,8 @@ final class DefaultPermissionGrantPolicy {
if (verifierPackage != null
&& doesPackageSupportRuntimePermissions(verifierPackage)) {
grantRuntimePermissionsLPw(verifierPackage, STORAGE_PERMISSIONS, true, userId);
+ grantRuntimePermissionsLPw(verifierPackage, PHONE_PERMISSIONS, false, userId);
+ grantRuntimePermissionsLPw(verifierPackage, SMS_PERMISSIONS, false, userId);
}
// SetupWizard
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 7024ec8b13b9..8c23648f0f0f 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -217,8 +217,7 @@ final class PackageDexOptimizer {
@Nullable
private String createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet)
throws IOException {
- if ((pkg.isSystemApp() && !pkg.isUpdatedSystemApp()) || pkg.isForwardLocked()
- || pkg.applicationInfo.isExternalAsec()) {
+ if (!pkg.canHaveOatDir()) {
return null;
}
File codePath = new File(pkg.codePath);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d40932585ebb..b7a587b0b7d2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -13851,7 +13851,21 @@ public class PackageManagerService extends IPackageManager.Stub {
// TODO(multiArch): Extend getSizeInfo to look at *all* instruction sets, not
// just the primary.
String[] dexCodeInstructionSets = getDexCodeInstructionSets(getAppDexInstructionSets(ps));
- int res = mInstaller.getSizeInfo(p.volumeUuid, packageName, userHandle, p.baseCodePath,
+
+ String apkPath;
+ File packageDir = new File(p.codePath);
+
+ if (packageDir.isDirectory() && p.canHaveOatDir()) {
+ apkPath = packageDir.getAbsolutePath();
+ // If libDirRoot is inside a package dir, set it to null to avoid it being counted twice
+ if (libDirRoot != null && libDirRoot.startsWith(apkPath)) {
+ libDirRoot = null;
+ }
+ } else {
+ apkPath = p.baseCodePath;
+ }
+
+ int res = mInstaller.getSizeInfo(p.volumeUuid, packageName, userHandle, apkPath,
libDirRoot, publicSrcDir, asecPath, dexCodeInstructionSets, pStats);
if (res < 0) {
return false;
diff --git a/services/core/java/com/android/server/policy/BarController.java b/services/core/java/com/android/server/policy/BarController.java
index 9095f57b175a..051b7fb17d69 100644
--- a/services/core/java/com/android/server/policy/BarController.java
+++ b/services/core/java/com/android/server/policy/BarController.java
@@ -258,7 +258,7 @@ public class BarController {
vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE; // never show transient bars in low profile
}
if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0 ||
- ((vis | oldVis) & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0) {
+ ((vis | oldVis) & View.SYSTEM_UI_TRANSPARENT) != 0) {
mLastTranslucent = SystemClock.uptimeMillis();
}
return vis;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 325196d43056..e5e468fbe7ea 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -20,8 +20,8 @@ import static android.app.ActivityManager.HOME_STACK_ID;
import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerService.TAG;
+import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
-import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region;
import android.util.DisplayMetrics;
@@ -72,6 +72,7 @@ class DisplayContent {
boolean mDisplayScalingDisabled;
private final DisplayInfo mDisplayInfo = new DisplayInfo();
private final Display mDisplay;
+ private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
Rect mBaseDisplayRect = new Rect();
Rect mContentRect = new Rect();
@@ -82,11 +83,11 @@ class DisplayContent {
final boolean isDefaultDisplay;
/** Window tokens that are in the process of exiting, but still on screen for animations. */
- final ArrayList<WindowToken> mExitingTokens = new ArrayList<WindowToken>();
+ final ArrayList<WindowToken> mExitingTokens = new ArrayList<>();
/** Array containing all TaskStacks on this display. Array
* is stored in display order with the current bottom stack at 0. */
- private final ArrayList<TaskStack> mStacks = new ArrayList<TaskStack>();
+ private final ArrayList<TaskStack> mStacks = new ArrayList<>();
/** A special TaskStack with id==HOME_STACK_ID that moves to the bottom whenever any TaskStack
* (except a future lockscreen TaskStack) moves to the top. */
@@ -117,6 +118,7 @@ class DisplayContent {
mDisplay = display;
mDisplayId = display.getDisplayId();
display.getDisplayInfo(mDisplayInfo);
+ display.getMetrics(mDisplayMetrics);
isDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
mService = service;
}
@@ -137,6 +139,10 @@ class DisplayContent {
return mDisplayInfo;
}
+ DisplayMetrics getDisplayMetrics() {
+ return mDisplayMetrics;
+ }
+
/**
* Returns true if the specified UID has access to this display.
*/
@@ -174,6 +180,7 @@ class DisplayContent {
void updateDisplayInfo() {
mDisplay.getDisplayInfo(mDisplayInfo);
+ mDisplay.getMetrics(mDisplayMetrics);
for (int i = mStacks.size() - 1; i >= 0; --i) {
mStacks.get(i).updateDisplayInfo(null);
}
@@ -234,7 +241,7 @@ class DisplayContent {
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
final Task task = tasks.get(taskNdx);
task.getBounds(mTmpRect);
- if (mTmpRect.contains(x, y)) {
+ if (task.inFreeformWorkspace() && mTmpRect.contains(x, y)) {
return task.mTaskId;
}
}
@@ -247,7 +254,7 @@ class DisplayContent {
* falls within. Returns -1 if the touch doesn't fall into a resizing area.
*/
int taskIdForControlPoint(int x, int y) {
- final int delta = calculatePixelFromDp(WindowState.RESIZE_HANDLE_WIDTH_IN_DP);
+ final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
TaskStack stack = mStacks.get(stackNdx);
if (!stack.allowTaskResize()) {
@@ -275,17 +282,10 @@ class DisplayContent {
return -1;
}
- private int calculatePixelFromDp(int dp) {
- final Configuration serviceConfig = mService.mCurConfiguration;
- // TODO(multidisplay): Update Dp to that of display stack is on.
- final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
- return (int)(dp * density);
- }
-
void setTouchExcludeRegion(Task focusedTask) {
mTouchExcludeRegion.set(mBaseDisplayRect);
WindowList windows = getWindowList();
- final int delta = calculatePixelFromDp(WindowState.RESIZE_HANDLE_WIDTH_IN_DP);
+ final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
for (int i = windows.size() - 1; i >= 0; --i) {
final WindowState win = windows.get(i);
final Task task = win.mAppToken != null ? win.getTask() : null;
@@ -299,7 +299,7 @@ class DisplayContent {
* (For freeform focused task, the below logic will first remove the enlarged
* area, then add back the inner area.)
*/
- final boolean isFreeformed = win.inFreeformWorkspace();
+ final boolean isFreeformed = task.inFreeformWorkspace();
if (task != focusedTask || isFreeformed) {
mTmpRect.set(win.mVisibleFrame);
mTmpRect.intersect(win.mVisibleInsets);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 07a850bfe17c..cc9efdbe90af 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -426,6 +426,10 @@ class Task implements DimLayer.DimLayerUser {
return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers;
}
+ boolean inFreeformWorkspace() {
+ return mStack != null && mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
+ }
+
@Override
public boolean isFullscreen() {
return mFullscreen;
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 71b83a5e32ea..7e32b84b3946 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -16,13 +16,14 @@
package com.android.server.wm;
-import com.android.server.input.InputApplicationHandle;
-import com.android.server.input.InputWindowHandle;
-import com.android.server.wm.WindowManagerService.H;
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static com.android.server.wm.WindowManagerService.DEBUG_TASK_POSITIONING;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
+import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
import android.annotation.IntDef;
import android.graphics.Point;
@@ -32,18 +33,30 @@ import android.os.Process;
import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.util.Slog;
-import android.util.TypedValue;
import android.view.Display;
+import android.view.DisplayInfo;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.InputEventReceiver;
import android.view.MotionEvent;
+import android.view.SurfaceControl;
import android.view.WindowManager;
-class TaskPositioner {
+import com.android.server.input.InputApplicationHandle;
+import com.android.server.input.InputWindowHandle;
+import com.android.server.wm.WindowManagerService.H;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+class TaskPositioner implements DimLayer.DimLayerUser {
private static final String TAG = "TaskPositioner";
+ // The margin the pointer position has to be within the side of the screen to be
+ // considered at the side of the screen.
+ private static final int SIDE_MARGIN_DIP = 100;
+
@IntDef(flag = true,
value = {
CTRL_NONE,
@@ -65,8 +78,15 @@ class TaskPositioner {
private WindowPositionerEventReceiver mInputEventReceiver;
private Display mDisplay;
private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+ private DimLayer mDimLayer;
+ @CtrlType
+ private int mCurrentDimSide;
+ private Rect mTmpRect = new Rect();
+ private int mSideMargin;
private int mTaskId;
+ private TaskStack mStack;
+ private boolean mResizing;
private final Rect mWindowOriginalBounds = new Rect();
private final Rect mWindowDragBounds = new Rect();
private float mStartDragX;
@@ -113,7 +133,8 @@ class TaskPositioner {
notifyMoveLocked(newX, newY);
}
try {
- mService.mActivityManager.resizeTask(mTaskId, mWindowDragBounds);
+ mService.mActivityManager.resizeTask(
+ mTaskId, mWindowDragBounds, true /* resizedByUser */);
} catch(RemoteException e) {}
} break;
@@ -133,9 +154,21 @@ class TaskPositioner {
}
if (endDrag) {
+ mResizing = false;
+ try {
+ mService.mActivityManager.resizeTask(
+ mTaskId, mWindowDragBounds, true /* resizedByUser */);
+ } catch(RemoteException e) {}
// Post back to WM to handle clean-ups. We still need the input
// event handler for the last finishInputEvent()!
mService.mH.sendEmptyMessage(H.FINISH_TASK_POSITIONING);
+ if (mCurrentDimSide != CTRL_NONE) {
+ final int createMode = mCurrentDimSide == CTRL_LEFT
+ ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
+ : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+ mService.mActivityManager.moveTaskToDockedStack(
+ mTaskId, createMode, true /*toTop*/);
+ }
}
handled = true;
} catch (Exception e) {
@@ -213,6 +246,9 @@ class TaskPositioner {
Slog.d(TAG, "Pausing rotation during re-position");
}
mService.pauseRotationLocked();
+
+ mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId());
+ mSideMargin = mService.dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
}
void unregister() {
@@ -238,6 +274,12 @@ class TaskPositioner {
mDragApplicationHandle = null;
mDisplay = null;
+ if (mDimLayer != null) {
+ mDimLayer.destroySurface();
+ mDimLayer = null;
+ }
+ mCurrentDimSide = CTRL_NONE;
+
// Resume rotations after a drag.
if (WindowManagerService.DEBUG_ORIENTATION) {
Slog.d(TAG, "Resuming rotation after re-position");
@@ -245,9 +287,13 @@ class TaskPositioner {
mService.resumeRotationLocked();
}
+ boolean isTaskResizing(final Task task) {
+ return mResizing && task != null && mTaskId == task.mTaskId;
+ }
+
void startDragLocked(WindowState win, boolean resize, float startX, float startY) {
- if (DEBUG_TASK_POSITIONING) {Slog.d(TAG,
- "startDragLocked: win=" + win + ", resize=" + resize
+ if (DEBUG_TASK_POSITIONING) {
+ Slog.d(TAG, "startDragLocked: win=" + win + ", resize=" + resize
+ ", {" + startX + ", " + startY + "}");
}
mCtrlType = CTRL_NONE;
@@ -265,10 +311,12 @@ class TaskPositioner {
if (startY > visibleFrame.bottom) {
mCtrlType |= CTRL_BOTTOM;
}
+ mResizing = true;
}
final Task task = win.getTask();
mTaskId = task.mTaskId;
+ mStack = task.mStack;
mStartDragX = startX;
mStartDragY = startY;
@@ -284,10 +332,8 @@ class TaskPositioner {
// This is a resizing operation.
final int deltaX = Math.round(x - mStartDragX);
final int deltaY = Math.round(y - mStartDragY);
- // TODO: fix the min sizes when we have mininum width/height support,
- // use hard-coded min sizes for now.
- final int minSizeX = (int)(dipToPx(96));
- final int minSizeY = (int)(dipToPx(64));
+ final int minSizeX = mService.dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
+ final int minSizeY = mService.dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
int left = mWindowOriginalBounds.left;
int top = mWindowOriginalBounds.top;
int right = mWindowOriginalBounds.right;
@@ -308,9 +354,75 @@ class TaskPositioner {
} else {
// This is a moving operation.
mWindowDragBounds.set(mWindowOriginalBounds);
- mWindowDragBounds.offset(Math.round(x - mStartDragX),
- Math.round(y - mStartDragY));
+ mWindowDragBounds.offset(Math.round(x - mStartDragX), Math.round(y - mStartDragY));
+ updateDimLayerVisibility((int) x);
+ }
+ }
+
+ private void updateDimLayerVisibility(int x) {
+ @CtrlType
+ int dimSide = getDimSide(x);
+ if (dimSide == mCurrentDimSide) {
+ return;
+ }
+
+ mCurrentDimSide = dimSide;
+
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION updateDimLayerVisibility");
+ SurfaceControl.openTransaction();
+ if (mCurrentDimSide == CTRL_NONE) {
+ mDimLayer.hide();
+ } else {
+ showDimLayer();
+ }
+ SurfaceControl.closeTransaction();
+ }
+
+ /**
+ * Returns the side of the screen the dim layer should be shown.
+ * @param x horizontal coordinate used to determine if the dim layer should be shown
+ * @return Returns {@link #CTRL_LEFT} if the dim layer should be shown on the left half of the
+ * screen, {@link #CTRL_RIGHT} if on the right side, or {@link #CTRL_NONE} if the dim layer
+ * shouldn't be shown.
+ */
+ private int getDimSide(int x) {
+ if (mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
+ || !mStack.isFullscreen()
+ || mService.mCurConfiguration.orientation != ORIENTATION_LANDSCAPE) {
+ return CTRL_NONE;
+ }
+
+ mStack.getBounds(mTmpRect);
+ if (x - mSideMargin <= mTmpRect.left) {
+ return CTRL_LEFT;
}
+ if (x + mSideMargin >= mTmpRect.right) {
+ return CTRL_RIGHT;
+ }
+
+ return CTRL_NONE;
+ }
+
+ private void showDimLayer() {
+ mStack.getBounds(mTmpRect);
+ if (mCurrentDimSide == CTRL_LEFT) {
+ mTmpRect.right = mTmpRect.centerX();
+ } else if (mCurrentDimSide == CTRL_RIGHT) {
+ mTmpRect.left = mTmpRect.centerX();
+ }
+
+ mDimLayer.setBounds(mTmpRect);
+ mDimLayer.show(getDragLayerLocked(), 0.5f, 0);
+ }
+
+ @Override /** {@link DimLayer.DimLayerUser} */
+ public boolean isFullscreen() {
+ return false;
+ }
+
+ @Override /** {@link DimLayer.DimLayerUser} */
+ public DisplayInfo getDisplayInfo() {
+ return mStack.getDisplayInfo();
}
private int getDragLayerLocked() {
@@ -318,8 +430,4 @@ class TaskPositioner {
* WindowManagerService.TYPE_LAYER_MULTIPLIER
+ WindowManagerService.TYPE_LAYER_OFFSET;
}
-
- private float dipToPx(float dip) {
- return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, mDisplayMetrics);
- }
-} \ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index ae3bb9b20b04..1e6fab600594 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -353,11 +353,13 @@ public class TaskStack implements DimLayer.DimLayerUser {
private static void getInitialDockedStackBounds(
Rect displayRect, Rect outBounds, int stackId) {
// Docked stack start off occupying half the screen space.
- // TODO(multi-window): Need to support the selecting which half of the screen the
- // docked stack uses for snapping windows to the edge of the screen.
final boolean splitHorizontally = displayRect.width() > displayRect.height();
+ final boolean topOrLeftCreateMode =
+ WindowManagerService.sDockedStackCreateMode == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ final boolean placeTopOrLeft = (stackId == DOCKED_STACK_ID && topOrLeftCreateMode)
+ || (stackId != DOCKED_STACK_ID && !topOrLeftCreateMode);
outBounds.set(displayRect);
- if (stackId == DOCKED_STACK_ID) {
+ if (placeTopOrLeft) {
if (splitHorizontally) {
outBounds.right = displayRect.centerX();
} else {
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index fc23fd1b2f9f..1a946b2c5d17 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -736,7 +736,8 @@ class WallpaperController {
insertionIndex = windows.indexOf(wallpaperTarget);
}
}
- if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
+ if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT
+ || (DEBUG_ADD_REMOVE && oldIndex != insertionIndex)) Slog.v(TAG,
"Moving wallpaper " + wallpaper
+ " from " + oldIndex + " to " + insertionIndex);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 69440b883d00..554587d212f5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -16,6 +16,34 @@
package com.android.server.wm;
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
+import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+
+import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH;
+
import android.Manifest;
import android.animation.ValueAnimator;
import android.app.ActivityManagerNative;
@@ -110,9 +138,7 @@ import android.view.WindowManagerPolicy;
import android.view.WindowManagerPolicy.PointerEventListener;
import android.view.animation.Animation;
-import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH;
import com.android.internal.app.IAssistScreenshotReceiver;
-import com.android.internal.app.IBatteryStats;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
@@ -149,31 +175,6 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
-import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
-import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
-import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
-import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
-import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
-import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
-import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-
/** {@hide} */
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
@@ -458,6 +459,8 @@ public class WindowManagerService extends IWindowManager.Stub
private boolean mKeyguardWaitingForActivityDrawn;
+ static int sDockedStackCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+
class RotationWatcher {
IRotationWatcher watcher;
IBinder.DeathRecipient deathRecipient;
@@ -904,7 +907,6 @@ public class WindowManagerService extends IWindowManager.Stub
SurfaceControl.closeTransaction();
}
- updateCircularDisplayMaskIfNeeded();
showEmulatorDisplayOverlayIfNeeded();
}
@@ -4448,6 +4450,12 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ public void setDockedStackCreateMode(int mode) {
+ synchronized (mWindowMap) {
+ sDockedStackCreateMode = mode;
+ }
+ }
+
/**
* Create a new TaskStack and place it on a DisplayContent.
* @param stackId The unique identifier of the new stack.
@@ -5298,7 +5306,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- public void updateCircularDisplayMaskIfNeeded() {
+ private void updateCircularDisplayMaskIfNeeded() {
// we're fullscreen and not hosted in an ActivityView
if (mContext.getResources().getConfiguration().isScreenRound()
&& mContext.getResources().getBoolean(
@@ -7090,6 +7098,8 @@ public class WindowManagerService extends IWindowManager.Stub
mActivityManager.updateConfiguration(null);
} catch (RemoteException e) {
}
+
+ updateCircularDisplayMaskIfNeeded();
}
private void displayReady(int displayId) {
@@ -9853,6 +9863,10 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ static int dipToPixel(int dip, DisplayMetrics displayMetrics) {
+ return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics);
+ }
+
private final class LocalService extends WindowManagerInternal {
@Override
public void requestTraversalFromDisplayManager() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 789354db1739..1deccb477f55 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
@@ -81,12 +80,14 @@ final class WindowState implements WindowManagerPolicy.WindowState {
static final String TAG = "WindowState";
// The minimal size of a window within the usable area of the freeform stack.
- private static final int MINIMUM_VISIBLE_WIDTH_IN_DP = 48;
- private static final int MINIMUM_VISIBLE_HEIGHT_IN_DP = 32;
+ // TODO(multi-window): fix the min sizes when we have mininum width/height support,
+ // use hard-coded min sizes for now.
+ static final int MINIMUM_VISIBLE_WIDTH_IN_DP = 48;
+ static final int MINIMUM_VISIBLE_HEIGHT_IN_DP = 32;
// The thickness of a window resize handle outside the window bounds on the free form workspace
// to capture touch events in that area.
- static final int RESIZE_HANDLE_WIDTH_IN_DP = 10;
+ static final int RESIZE_HANDLE_WIDTH_IN_DP = 30;
static final boolean BOUNDS_FOR_TOUCH = true;
@@ -557,7 +558,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
final Task task = mAppToken != null ? getTask() : null;
final boolean nonFullscreenTask = task != null && !task.isFullscreen();
- final boolean freeformWorkspace = inFreeformWorkspace();
+ final boolean freeformWorkspace = task != null && task.inFreeformWorkspace();
if (nonFullscreenTask) {
task.getBounds(mContainingFrame);
final WindowState imeWin = mService.mInputMethodWindow;
@@ -689,8 +690,11 @@ final class WindowState implements WindowManagerPolicy.WindowState {
// into a usable area..
final int height = Math.min(mFrame.height(), mContentFrame.height());
final int width = Math.min(mContentFrame.width(), mFrame.width());
- final int minVisibleHeight = calculatePixelFromDp(MINIMUM_VISIBLE_HEIGHT_IN_DP);
- final int minVisibleWidth = calculatePixelFromDp(MINIMUM_VISIBLE_WIDTH_IN_DP);
+ final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
+ final int minVisibleHeight =
+ mService.dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics);
+ final int minVisibleWidth =
+ mService.dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics);
final int top = Math.max(mContentFrame.top,
Math.min(mFrame.top, mContentFrame.bottom - minVisibleHeight));
final int left = Math.max(mContentFrame.left + minVisibleWidth - width,
@@ -897,6 +901,11 @@ final class WindowState implements WindowManagerPolicy.WindowState {
return stack == null ? mDisplayContent : stack.getDisplayContent();
}
+ public DisplayInfo getDisplayInfo() {
+ final DisplayContent displayContent = getDisplayContent();
+ return displayContent != null ? displayContent.getDisplayInfo() : null;
+ }
+
public int getDisplayId() {
final DisplayContent displayContent = getDisplayContent();
if (displayContent == null) {
@@ -935,8 +944,10 @@ final class WindowState implements WindowManagerPolicy.WindowState {
if (task != null) {
task.getBounds(bounds);
if (forTouch == BOUNDS_FOR_TOUCH) {
- if (inFreeformWorkspace()) {
- final int delta = calculatePixelFromDp(RESIZE_HANDLE_WIDTH_IN_DP);
+ if (task.inFreeformWorkspace()) {
+ final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
+ final int delta =
+ mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, displayMetrics);
bounds.inset(-delta, -delta);
}
}
@@ -1668,16 +1679,13 @@ final class WindowState implements WindowManagerPolicy.WindowState {
}
boolean inFreeformWorkspace() {
- final Task task = getTask();
- return task != null && task.mStack != null &&
- task.mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
+ final Task task = mAppToken != null ? getTask() : null;
+ return task != null && task.inFreeformWorkspace();
}
- private int calculatePixelFromDp(int dp) {
- final Configuration serviceConfig = mService.mCurConfiguration;
- // TODO(multidisplay): Update Dp to that of display stack is on.
- final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
- return (int)(dp * density);
+ boolean isDragResizing() {
+ final Task task = mAppToken != null ? getTask() : null;
+ return mService.mTaskPositioner != null && mService.mTaskPositioner.isTaskResizing(task);
}
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index bf1ab8fab7d0..dfc9784249a9 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -115,6 +115,7 @@ class WindowStateAnimator {
Rect mClipRect = new Rect();
Rect mTmpClipRect = new Rect();
Rect mLastClipRect = new Rect();
+ Rect mTmpStackBounds = new Rect();
// Used to save animation distances between the time they are calculated and when they are
// used.
@@ -168,6 +169,11 @@ class WindowStateAnimator {
/** Set when the window has been shown in the screen the first time. */
static final int HAS_DRAWN = 4;
+ // Surface flinger doesn't support crop rectangles where width or height is non-positive.
+ // However, we need to somehow handle the situation where the cropping would completely hide
+ // the window. We achieve this by explicitly hiding the surface and not letting it be shown.
+ private boolean mHiddenForCrop;
+
String drawStateToString() {
switch (mDrawState) {
case NO_SURFACE: return "NO_SURFACE";
@@ -798,8 +804,17 @@ class WindowStateAnimator {
width = w.mRequestedWidth;
height = w.mRequestedHeight;
} else {
- width = w.mCompatFrame.width();
- height = w.mCompatFrame.height();
+ // When we're doing a drag-resizing, request a surface that's fullscreen size,
+ // so that we don't need to reallocate during the process. This also prevents
+ // buffer drops due to size mismatch.
+ final DisplayInfo displayInfo = w.getDisplayInfo();
+ if (displayInfo != null && w.isDragResizing()) {
+ width = displayInfo.logicalWidth;
+ height = displayInfo.logicalHeight;
+ } else {
+ width = w.mCompatFrame.width();
+ height = w.mCompatFrame.height();
+ }
}
// Something is wrong and SurfaceFlinger will not like this,
@@ -1309,9 +1324,15 @@ class WindowStateAnimator {
final boolean fullscreen = w.isFullscreen(displayInfo.appWidth, displayInfo.appHeight);
final Rect clipRect = mTmpClipRect;
- // We use the clip rect as provided by the tranformation for non-fullscreen windows to
- // avoid premature clipping with the system decor rect.
- clipRect.set((mHasClipRect && !fullscreen) ? mClipRect : w.mSystemDecorRect);
+ if (w.isDragResizing()) {
+ // When we're doing a drag-resizing, the surface is set up to cover full screen.
+ // Set the clip rect to be the same size so that we don't get any scaling.
+ clipRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+ } else {
+ // We use the clip rect as provided by the tranformation for non-fullscreen windows to
+ // avoid premature clipping with the system decor rect.
+ clipRect.set((mHasClipRect && !fullscreen) ? mClipRect : w.mSystemDecorRect);
+ }
// Expand the clip rect for surface insets.
final WindowManager.LayoutParams attrs = w.mAttrs;
@@ -1331,12 +1352,20 @@ class WindowStateAnimator {
// so we need to translate to match the actual surface coordinates.
clipRect.offset(attrs.surfaceInsets.left, attrs.surfaceInsets.top);
+ adjustCropToStackBounds(w, clipRect);
+
if (!clipRect.equals(mLastClipRect)) {
mLastClipRect.set(clipRect);
try {
if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
"CROP " + clipRect.toShortString(), null);
- mSurfaceControl.setWindowCrop(clipRect);
+ if (clipRect.width() > 0 && clipRect.height() > 0) {
+ mSurfaceControl.setWindowCrop(clipRect);
+ mHiddenForCrop = false;
+ } else {
+ hide();
+ mHiddenForCrop = true;
+ }
} catch (RuntimeException e) {
Slog.w(TAG, "Error setting crop surface of " + w
+ " crop=" + clipRect.toShortString(), e);
@@ -1347,6 +1376,25 @@ class WindowStateAnimator {
}
}
+ private void adjustCropToStackBounds(WindowState w, Rect clipRect) {
+ if (w.getAttrs().type == LayoutParams.TYPE_BASE_APPLICATION) {
+ TaskStack stack = w.getTask().mStack;
+ stack.getBounds(mTmpStackBounds);
+ final int surfaceX = (int) mSurfaceX;
+ final int surfaceY = (int) mSurfaceY;
+ // We need to do some acrobatics with surface position, because their clip region is
+ // relative to the inside of the surface, but the stack bounds aren't.
+ clipRect.left = Math.max(0,
+ Math.max(mTmpStackBounds.left, surfaceX + clipRect.left) - surfaceX);
+ clipRect.top = Math.max(0,
+ Math.max(mTmpStackBounds.top, surfaceY + clipRect.top) - surfaceY);
+ clipRect.right = Math.max(0,
+ Math.min(mTmpStackBounds.right, surfaceX + clipRect.right) - surfaceX);
+ clipRect.bottom = Math.max(0,
+ Math.min(mTmpStackBounds.bottom, surfaceY + clipRect.bottom) - surfaceY);
+ }
+ }
+
void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
final WindowState w = mWin;
@@ -1358,8 +1406,17 @@ class WindowStateAnimator {
width = w.mRequestedWidth;
height = w.mRequestedHeight;
} else {
- width = w.mCompatFrame.width();
- height = w.mCompatFrame.height();
+ // When we're doing a drag-resizing, request a surface that's fullscreen size,
+ // so that we don't need to reallocate during the process. This also prevents
+ // buffer drops due to size mismatch.
+ final DisplayInfo displayInfo = w.getDisplayInfo();
+ if (displayInfo != null && w.isDragResizing()) {
+ width = displayInfo.logicalWidth;
+ height = displayInfo.logicalHeight;
+ } else {
+ width = w.mCompatFrame.width();
+ height = w.mCompatFrame.height();
+ }
}
// Something is wrong and SurfaceFlinger will not like this,
@@ -1517,7 +1574,7 @@ class WindowStateAnimator {
mDsDx * w.mHScale, mDtDx * w.mVScale,
mDsDy * w.mHScale, mDtDy * w.mVScale);
- if (mLastHidden && mDrawState == HAS_DRAWN) {
+ if (mLastHidden && mDrawState == HAS_DRAWN && !mHiddenForCrop) {
if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
"SHOW (performLayout)", null);
if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + w
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 52efa6803499..86da94f7d2be 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -712,7 +712,12 @@ class WindowSurfacePlacer {
}
}
}
-
+ /*
+ * Updates the shown frame before we set up the surface. This is needed because
+ * the resizing could change the top-left position (in addition to size) of the
+ * window. setSurfaceBoundariesLocked uses mShownFrame to position the surface.
+ */
+ winAnimator.computeShownFrameLocked();
winAnimator.setSurfaceBoundariesLocked(recoveringMemory);
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index aafaf8373cf8..93dc6cbcfe99 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1327,4 +1327,4 @@ public final class SystemServer {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name);
Slog.i(TAG, name);
}
-}
+} \ No newline at end of file
diff --git a/services/usage/java/com/android/server/usage/UnixCalendar.java b/services/usage/java/com/android/server/usage/UnixCalendar.java
index ce06a91bd148..db7b42dca8fb 100644
--- a/services/usage/java/com/android/server/usage/UnixCalendar.java
+++ b/services/usage/java/com/android/server/usage/UnixCalendar.java
@@ -15,40 +15,22 @@
*/
package com.android.server.usage;
-import android.app.usage.UsageStatsManager;
-
/**
* A handy calendar object that knows nothing of Locale's or TimeZones. This simplifies
* interval book-keeping. It is *NOT* meant to be used as a user-facing calendar, as it has
* no concept of Locale or TimeZone.
*/
public class UnixCalendar {
- private static final long DAY_IN_MILLIS = 24 * 60 * 60 * 1000;
- private static final long WEEK_IN_MILLIS = 7 * DAY_IN_MILLIS;
- private static final long MONTH_IN_MILLIS = 30 * DAY_IN_MILLIS;
- private static final long YEAR_IN_MILLIS = 365 * DAY_IN_MILLIS;
+ public static final long DAY_IN_MILLIS = 24 * 60 * 60 * 1000;
+ public static final long WEEK_IN_MILLIS = 7 * DAY_IN_MILLIS;
+ public static final long MONTH_IN_MILLIS = 30 * DAY_IN_MILLIS;
+ public static final long YEAR_IN_MILLIS = 365 * DAY_IN_MILLIS;
private long mTime;
public UnixCalendar(long time) {
mTime = time;
}
- public void truncateToDay() {
- mTime -= mTime % DAY_IN_MILLIS;
- }
-
- public void truncateToWeek() {
- mTime -= mTime % WEEK_IN_MILLIS;
- }
-
- public void truncateToMonth() {
- mTime -= mTime % MONTH_IN_MILLIS;
- }
-
- public void truncateToYear() {
- mTime -= mTime % YEAR_IN_MILLIS;
- }
-
public void addDays(int val) {
mTime += val * DAY_IN_MILLIS;
}
@@ -72,28 +54,4 @@ public class UnixCalendar {
public long getTimeInMillis() {
return mTime;
}
-
- public static void truncateTo(UnixCalendar calendar, int intervalType) {
- switch (intervalType) {
- case UsageStatsManager.INTERVAL_YEARLY:
- calendar.truncateToYear();
- break;
-
- case UsageStatsManager.INTERVAL_MONTHLY:
- calendar.truncateToMonth();
- break;
-
- case UsageStatsManager.INTERVAL_WEEKLY:
- calendar.truncateToWeek();
- break;
-
- case UsageStatsManager.INTERVAL_DAILY:
- calendar.truncateToDay();
- break;
-
- default:
- throw new UnsupportedOperationException("Can't truncate date to interval " +
- intervalType);
- }
- }
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index f8ae03f1107c..0ca4bd8c6c04 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -350,23 +350,6 @@ class UsageStatsDatabase {
}
/**
- * Get the time at which the latest stats begin for this interval type.
- */
- public long getLatestUsageStatsBeginTime(int intervalType) {
- synchronized (mLock) {
- if (intervalType < 0 || intervalType >= mIntervalDirs.length) {
- throw new IllegalArgumentException("Bad interval type " + intervalType);
- }
-
- final int statsFileCount = mSortedStatFiles[intervalType].size();
- if (statsFileCount > 0) {
- return mSortedStatFiles[intervalType].keyAt(statsFileCount - 1);
- }
- return -1;
- }
- }
-
- /**
* Figures out what to extract from the given IntervalStats object.
*/
interface StatCombiner<T> {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index b07b8153279d..5188e5fd1f7d 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -65,6 +65,11 @@ class UserUsageStatsService {
private final String mLogPrefix;
private final int mUserId;
+ private static final long[] INTERVAL_LENGTH = new long[] {
+ UnixCalendar.DAY_IN_MILLIS, UnixCalendar.WEEK_IN_MILLIS,
+ UnixCalendar.MONTH_IN_MILLIS, UnixCalendar.YEAR_IN_MILLIS
+ };
+
interface StatsUpdatedListener {
void onStatsUpdated();
}
@@ -104,18 +109,12 @@ class UserUsageStatsService {
// By calling loadActiveStats, we will
// generate new stats for each bucket.
- loadActiveStats(currentTimeMillis,/*force=*/ false, /*resetBeginIdleTime=*/ false);
+ loadActiveStats(currentTimeMillis, /*resetBeginIdleTime=*/ false);
} else {
// Set up the expiry date to be one day from the latest daily stat.
// This may actually be today and we will rollover on the first event
// that is reported.
- mDailyExpiryDate.setTimeInMillis(
- mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime);
- mDailyExpiryDate.addDays(1);
- mDailyExpiryDate.truncateToDay();
- Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " +
- sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) +
- "(" + mDailyExpiryDate.getTimeInMillis() + ")");
+ updateRolloverDeadline();
}
// Now close off any events that were open at the time this was saved.
@@ -170,7 +169,7 @@ class UserUsageStatsService {
void onTimeChanged(long oldTime, long newTime, boolean resetBeginIdleTime) {
persistActiveStats();
mDatabase.onTimeChanged(newTime - oldTime);
- loadActiveStats(newTime, /* force= */ true, resetBeginIdleTime);
+ loadActiveStats(newTime, resetBeginIdleTime);
}
void reportEvent(UsageEvents.Event event, long deviceUsageTime) {
@@ -237,7 +236,7 @@ class UserUsageStatsService {
new StatCombiner<UsageStats>() {
@Override
public void combine(IntervalStats stats, boolean mutable,
- List<UsageStats> accResult) {
+ List<UsageStats> accResult) {
if (!mutable) {
accResult.addAll(stats.packageStats.values());
return;
@@ -254,7 +253,7 @@ class UserUsageStatsService {
new StatCombiner<ConfigurationStats>() {
@Override
public void combine(IntervalStats stats, boolean mutable,
- List<ConfigurationStats> accResult) {
+ List<ConfigurationStats> accResult) {
if (!mutable) {
accResult.addAll(stats.configurations.values());
return;
@@ -448,7 +447,7 @@ class UserUsageStatsService {
persistActiveStats();
mDatabase.prune(currentTimeMillis);
- loadActiveStats(currentTimeMillis, /*force=*/ false, /*resetBeginIdleTime=*/ false);
+ loadActiveStats(currentTimeMillis, /*resetBeginIdleTime=*/ false);
final int continueCount = continuePreviousDay.size();
for (int i = 0; i < continueCount; i++) {
@@ -474,45 +473,28 @@ class UserUsageStatsService {
}
}
- /**
- * @param force To force all in-memory stats to be reloaded.
- */
- private void loadActiveStats(final long currentTimeMillis, boolean force,
- boolean resetBeginIdleTime) {
- final UnixCalendar tempCal = mDailyExpiryDate;
+ private void loadActiveStats(final long currentTimeMillis, boolean resetBeginIdleTime) {
for (int intervalType = 0; intervalType < mCurrentStats.length; intervalType++) {
- tempCal.setTimeInMillis(currentTimeMillis);
- UnixCalendar.truncateTo(tempCal, intervalType);
-
- if (!force && mCurrentStats[intervalType] != null &&
- mCurrentStats[intervalType].beginTime == tempCal.getTimeInMillis()) {
- // These are the same, no need to load them (in memory stats are always newer
- // than persisted stats).
- continue;
- }
-
- final long lastBeginTime = mDatabase.getLatestUsageStatsBeginTime(intervalType);
- if (lastBeginTime >= tempCal.getTimeInMillis()) {
+ final IntervalStats stats = mDatabase.getLatestUsageStats(intervalType);
+ if (stats != null && currentTimeMillis - 500 >= stats.endTime &&
+ currentTimeMillis < stats.beginTime + INTERVAL_LENGTH[intervalType]) {
if (DEBUG) {
Slog.d(TAG, mLogPrefix + "Loading existing stats @ " +
- sDateFormat.format(lastBeginTime) + "(" + lastBeginTime +
+ sDateFormat.format(stats.beginTime) + "(" + stats.beginTime +
") for interval " + intervalType);
}
- mCurrentStats[intervalType] = mDatabase.getLatestUsageStats(intervalType);
+ mCurrentStats[intervalType] = stats;
} else {
- mCurrentStats[intervalType] = null;
- }
-
- if (mCurrentStats[intervalType] == null) {
+ // No good fit remains.
if (DEBUG) {
Slog.d(TAG, "Creating new stats @ " +
- sDateFormat.format(tempCal.getTimeInMillis()) + "(" +
- tempCal.getTimeInMillis() + ") for interval " + intervalType);
-
+ sDateFormat.format(currentTimeMillis) + "(" +
+ currentTimeMillis + ") for interval " + intervalType);
}
+
mCurrentStats[intervalType] = new IntervalStats();
- mCurrentStats[intervalType].beginTime = tempCal.getTimeInMillis();
- mCurrentStats[intervalType].endTime = currentTimeMillis;
+ mCurrentStats[intervalType].beginTime = currentTimeMillis;
+ mCurrentStats[intervalType].endTime = currentTimeMillis + 1;
}
if (resetBeginIdleTime) {
@@ -522,12 +504,16 @@ class UserUsageStatsService {
}
}
mStatsChanged = false;
- mDailyExpiryDate.setTimeInMillis(currentTimeMillis);
+ updateRolloverDeadline();
+ }
+
+ private void updateRolloverDeadline() {
+ mDailyExpiryDate.setTimeInMillis(
+ mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime);
mDailyExpiryDate.addDays(1);
- mDailyExpiryDate.truncateToDay();
Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " +
sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) + "(" +
- tempCal.getTimeInMillis() + ")");
+ mDailyExpiryDate.getTimeInMillis() + ")");
}
//
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index b868832b087e..e57964a96182 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -326,6 +326,14 @@ public class CarrierConfigManager {
public static final String KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL =
"carrier_force_disable_etws_cmas_test_bool";
+ /**
+ * The default flag specifying whether "Turn on Notifications" option will be always shown in
+ * Settings->More->Emergency broadcasts menu regardless developer options is turned on or not.
+ * @hide
+ */
+ public static final String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL =
+ "always_show_emergency_alert_onoff_bool";
+
/* The following 3 fields are related to carrier visual voicemail. */
/**
@@ -532,6 +540,7 @@ public class CarrierConfigManager {
sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING, "");
sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING, "");
sDefaults.putBoolean(KEY_CSP_ENABLED_BOOL, false);
+ sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null);
sDefaults.putStringArray(KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY, null);
diff --git a/tools/aapt2/Logger.h b/tools/aapt2/Logger.h
index 1d437ebe6492..eed58b83ffc4 100644
--- a/tools/aapt2/Logger.h
+++ b/tools/aapt2/Logger.h
@@ -18,11 +18,11 @@
#define AAPT_LOGGER_H
#include "Source.h"
+#include "StringPiece.h"
#include <memory>
#include <ostream>
#include <string>
-#include <utils/String8.h>
namespace aapt {
@@ -71,11 +71,6 @@ private:
Source mSource;
};
-inline ::std::ostream& operator<<(::std::ostream& out, const std::u16string& str) {
- android::String8 utf8(str.data(), str.size());
- return out.write(utf8.string(), utf8.size());
-}
-
} // namespace aapt
#endif // AAPT_LOGGER_H
diff --git a/tools/aapt2/StringPiece.h b/tools/aapt2/StringPiece.h
index e2a1597caeda..8cbdeae5e892 100644
--- a/tools/aapt2/StringPiece.h
+++ b/tools/aapt2/StringPiece.h
@@ -229,4 +229,9 @@ inline ::std::ostream& operator<<(::std::ostream& out, const BasicStringPiece<ch
} // namespace aapt
+inline ::std::ostream& operator<<(::std::ostream& out, const std::u16string& str) {
+ android::String8 utf8(str.data(), str.size());
+ return out.write(utf8.string(), utf8.size());
+}
+
#endif // AAPT_STRING_PIECE_H
diff --git a/tools/aapt2/Util.cpp b/tools/aapt2/Util.cpp
index 03ecd1aca310..ca352e0e9b61 100644
--- a/tools/aapt2/Util.cpp
+++ b/tools/aapt2/Util.cpp
@@ -175,7 +175,51 @@ StringBuilder& StringBuilder::append(const StringPiece16& str) {
const char16_t* start = str.begin();
const char16_t* current = start;
while (current != end) {
- if (*current == u'"') {
+ if (mLastCharWasEscape) {
+ switch (*current) {
+ case u't':
+ mStr += u'\t';
+ break;
+ case u'n':
+ mStr += u'\n';
+ break;
+ case u'#':
+ mStr += u'#';
+ break;
+ case u'@':
+ mStr += u'@';
+ break;
+ case u'?':
+ mStr += u'?';
+ break;
+ case u'"':
+ mStr += u'"';
+ break;
+ case u'\'':
+ mStr += u'\'';
+ break;
+ case u'\\':
+ mStr += u'\\';
+ break;
+ case u'u': {
+ current++;
+ Maybe<char16_t> c = parseUnicodeCodepoint(&current, end);
+ if (!c) {
+ mError = "invalid unicode escape sequence";
+ return *this;
+ }
+ mStr += c.value();
+ current -= 1;
+ break;
+ }
+
+ default:
+ // Ignore.
+ break;
+ }
+ mLastCharWasEscape = false;
+ start = current + 1;
+ } else if (*current == u'"') {
if (!mQuote && mTrailingSpace) {
// We found an opening quote, and we have
// trailing space, so we should append that
@@ -208,52 +252,7 @@ StringBuilder& StringBuilder::append(const StringPiece16& str) {
}
mStr.append(start, current - start);
start = current + 1;
-
- current++;
- if (current != end) {
- switch (*current) {
- case u't':
- mStr += u'\t';
- break;
- case u'n':
- mStr += u'\n';
- break;
- case u'#':
- mStr += u'#';
- break;
- case u'@':
- mStr += u'@';
- break;
- case u'?':
- mStr += u'?';
- break;
- case u'"':
- mStr += u'"';
- break;
- case u'\'':
- mStr += u'\'';
- break;
- case u'\\':
- mStr += u'\\';
- break;
- case u'u': {
- current++;
- Maybe<char16_t> c = parseUnicodeCodepoint(&current, end);
- if (!c) {
- mError = "invalid unicode escape sequence";
- return *this;
- }
- mStr += c.value();
- current -= 1;
- break;
- }
-
- default:
- // Ignore.
- break;
- }
- start = current + 1;
- }
+ mLastCharWasEscape = true;
} else if (!mQuote) {
// This is not quoted text, so look for whitespace.
if (isspace16(*current)) {
diff --git a/tools/aapt2/Util.h b/tools/aapt2/Util.h
index 9cdb152bf41f..7ec6b030fd85 100644
--- a/tools/aapt2/Util.h
+++ b/tools/aapt2/Util.h
@@ -162,6 +162,7 @@ private:
std::u16string mStr;
bool mQuote = false;
bool mTrailingSpace = false;
+ bool mLastCharWasEscape = false;
std::string mError;
};
diff --git a/tools/aapt2/Util_test.cpp b/tools/aapt2/Util_test.cpp
index 0b08d240cad3..92f2a1c0f1d1 100644
--- a/tools/aapt2/Util_test.cpp
+++ b/tools/aapt2/Util_test.cpp
@@ -38,6 +38,13 @@ TEST(UtilTest, StringStartsWith) {
EXPECT_TRUE(util::stringStartsWith<char>("hello.xml", "he"));
}
+TEST(UtilTest, StringBuilderSplitEscapeSequence) {
+ EXPECT_EQ(StringPiece16(u"this is a new\nline."),
+ util::StringBuilder().append(u"this is a new\\")
+ .append(u"nline.")
+ .str());
+}
+
TEST(UtilTest, StringBuilderWhitespaceRemoval) {
EXPECT_EQ(StringPiece16(u"hey guys this is so cool"),
util::StringBuilder().append(u" hey guys ")