Merge "docs: Clarifying code snippet for View.autoFill()" into oc-dev
diff --git a/core/java/android/hardware/camera2/impl/CallbackProxies.java b/core/java/android/hardware/camera2/impl/CallbackProxies.java
index e6e448e..c9eecf1 100644
--- a/core/java/android/hardware/camera2/impl/CallbackProxies.java
+++ b/core/java/android/hardware/camera2/impl/CallbackProxies.java
@@ -88,7 +88,7 @@
}
@SuppressWarnings("deprecation")
- public static class DeviceCaptureCallbackProxy extends CameraDeviceImpl.CaptureCallback {
+ public static class DeviceCaptureCallbackProxy implements CameraDeviceImpl.CaptureCallback {
private final MethodNameInvoker<CameraDeviceImpl.CaptureCallback> mProxy;
public DeviceCaptureCallbackProxy(
@@ -138,6 +138,13 @@
int sequenceId) {
mProxy.invoke("onCaptureSequenceAborted", camera, sequenceId);
}
+
+ @Override
+ public void onCaptureBufferLost(CameraDevice camera,
+ CaptureRequest request, Surface target, long frameNumber) {
+ mProxy.invoke("onCaptureBufferLost", camera, request, target, frameNumber);
+ }
+
}
public static class SessionStateCallbackProxy
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index 16ffee0..b3938cb 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -452,6 +452,37 @@
private CameraDeviceImpl.CaptureCallback createCaptureCallbackProxy(
Handler handler, CaptureCallback callback) {
CameraDeviceImpl.CaptureCallback localCallback = new CameraDeviceImpl.CaptureCallback() {
+
+ @Override
+ public void onCaptureStarted(CameraDevice camera,
+ CaptureRequest request, long timestamp, long frameNumber) {
+ // Do nothing
+ }
+
+ @Override
+ public void onCapturePartial(CameraDevice camera,
+ CaptureRequest request, android.hardware.camera2.CaptureResult result) {
+ // Do nothing
+ }
+
+ @Override
+ public void onCaptureProgressed(CameraDevice camera,
+ CaptureRequest request, android.hardware.camera2.CaptureResult partialResult) {
+ // Do nothing
+ }
+
+ @Override
+ public void onCaptureCompleted(CameraDevice camera,
+ CaptureRequest request, android.hardware.camera2.TotalCaptureResult result) {
+ // Do nothing
+ }
+
+ @Override
+ public void onCaptureFailed(CameraDevice camera,
+ CaptureRequest request, android.hardware.camera2.CaptureFailure failure) {
+ // Do nothing
+ }
+
@Override
public void onCaptureSequenceCompleted(CameraDevice camera,
int sequenceId, long frameNumber) {
@@ -463,6 +494,13 @@
int sequenceId) {
finishPendingSequence(sequenceId);
}
+
+ @Override
+ public void onCaptureBufferLost(CameraDevice camera,
+ CaptureRequest request, Surface target, long frameNumber) {
+ // Do nothing
+ }
+
};
/*
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index ab87f15..0d5c5e3 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -1112,8 +1112,11 @@
* <p>A callback for tracking the progress of a {@link CaptureRequest}
* submitted to the camera device.</p>
*
+ * An interface instead of an abstract class because this is internal and
+ * we want to make sure we always implement all its callbacks until we reach
+ * the public layer.
*/
- public static abstract class CaptureCallback {
+ public interface CaptureCallback {
/**
* This constant is used to indicate that no images were captured for
@@ -1130,9 +1133,7 @@
* @see android.media.MediaActionSound
*/
public void onCaptureStarted(CameraDevice camera,
- CaptureRequest request, long timestamp, long frameNumber) {
- // default empty implementation
- }
+ CaptureRequest request, long timestamp, long frameNumber);
/**
* This method is called when some results from an image capture are
@@ -1141,9 +1142,7 @@
* @hide
*/
public void onCapturePartial(CameraDevice camera,
- CaptureRequest request, CaptureResult result) {
- // default empty implementation
- }
+ CaptureRequest request, CaptureResult result);
/**
* This method is called when an image capture makes partial forward progress; some
@@ -1151,18 +1150,14 @@
*
*/
public void onCaptureProgressed(CameraDevice camera,
- CaptureRequest request, CaptureResult partialResult) {
- // default empty implementation
- }
+ CaptureRequest request, CaptureResult partialResult);
/**
* This method is called when an image capture has fully completed and all the
* result metadata is available.
*/
public void onCaptureCompleted(CameraDevice camera,
- CaptureRequest request, TotalCaptureResult result) {
- // default empty implementation
- }
+ CaptureRequest request, TotalCaptureResult result);
/**
* This method is called instead of {@link #onCaptureCompleted} when the
@@ -1170,9 +1165,7 @@
* request.
*/
public void onCaptureFailed(CameraDevice camera,
- CaptureRequest request, CaptureFailure failure) {
- // default empty implementation
- }
+ CaptureRequest request, CaptureFailure failure);
/**
* This method is called independently of the others in CaptureCallback,
@@ -1180,9 +1173,7 @@
* or {@link CaptureFailure} for it have been returned via this callback.
*/
public void onCaptureSequenceCompleted(CameraDevice camera,
- int sequenceId, long frameNumber) {
- // default empty implementation
- }
+ int sequenceId, long frameNumber);
/**
* This method is called independently of the others in CaptureCallback,
@@ -1190,14 +1181,16 @@
* or {@link CaptureFailure} for it have been returned via this callback.
*/
public void onCaptureSequenceAborted(CameraDevice camera,
- int sequenceId) {
- // default empty implementation
- }
+ int sequenceId);
+ /**
+ * This method is called independently of the others in CaptureCallback, if an output buffer
+ * is dropped for a particular capture request.
+ *
+ * Loss of metadata is communicated via onCaptureFailed, independently of any buffer loss.
+ */
public void onCaptureBufferLost(CameraDevice camera,
- CaptureRequest request, Surface target, long frameNumber) {
- // default empty implementation
- }
+ CaptureRequest request, Surface target, long frameNumber);
}
/**
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index b00e65a..324a08c 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -932,7 +932,6 @@
} else if (mAuthenticationCallback != null) {
mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
getErrorString(errMsgId, vendorCode));
- mAuthenticationCallback = null;
} else if (mRemovalCallback != null) {
mRemovalCallback.onRemovalError(mRemovalFingerprint, clientErrMsgId,
getErrorString(errMsgId, vendorCode));
@@ -953,7 +952,6 @@
final AuthenticationResult result =
new AuthenticationResult(mCryptoObject, fp, userId);
mAuthenticationCallback.onAuthenticationSucceeded(result);
- mAuthenticationCallback = null;
}
}
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 447f280..d2598c7 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -22,9 +22,12 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.os.UserManager;
import android.text.TextUtils;
import android.util.Log;
+import android.view.Display;
+import android.view.WindowManager;
import libcore.io.Streams;
@@ -570,7 +573,16 @@
// Having set up the BCB (bootloader control block), go ahead and reboot
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- pm.reboot(PowerManager.REBOOT_RECOVERY_UPDATE);
+ String reason = PowerManager.REBOOT_RECOVERY_UPDATE;
+
+ // On TV, reboot quiescently if the screen is off
+ if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
+ WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ if (wm.getDefaultDisplay().getState() != Display.STATE_ON) {
+ reason += ",quiescent";
+ }
+ }
+ pm.reboot(reason);
throw new IOException("Reboot failed (no permissions?)");
}
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 05625c7..1ddbcad 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -52,6 +52,7 @@
import android.accounts.Account;
import android.accounts.AccountManager;
+import android.annotation.MainThread;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Notification;
@@ -126,6 +127,8 @@
* <li>Stops itself if it doesn't have any process left to monitor.
* </ol>
* </ol>
+ *
+ * TODO: There are multiple threads involved. Add synchronization accordingly.
*/
public class BugreportProgressService extends Service {
private static final String TAG = "BugreportProgressService";
@@ -201,11 +204,15 @@
private static final String NOTIFICATION_CHANNEL_ID = "bugreports";
+ private final Object mLock = new Object();
+
/** Managed dumpstate processes (keyed by id) */
private final SparseArray<DumpstateListener> mProcesses = new SparseArray<>();
private Context mContext;
- private ServiceHandler mMainHandler;
+
+ private Handler mMainThreadHandler;
+ private ServiceHandler mServiceHandler;
private ScreenshotHandler mScreenshotHandler;
private final BugreportInfoDialog mInfoDialog = new BugreportInfoDialog();
@@ -234,7 +241,8 @@
@Override
public void onCreate() {
mContext = getApplicationContext();
- mMainHandler = new ServiceHandler("BugreportProgressServiceMainThread");
+ mMainThreadHandler = new Handler(Looper.getMainLooper());
+ mServiceHandler = new ServiceHandler("BugreportProgressServiceMainThread");
mScreenshotHandler = new ScreenshotHandler("BugreportProgressServiceScreenshotThread");
mScreenshotsDir = new File(getFilesDir(), SCREENSHOT_DIR);
@@ -260,10 +268,10 @@
Log.v(TAG, "onStartCommand(): " + dumpIntent(intent));
if (intent != null) {
// Handle it in a separate thread.
- final Message msg = mMainHandler.obtainMessage();
+ final Message msg = mServiceHandler.obtainMessage();
msg.what = MSG_SERVICE_COMMAND;
msg.obj = intent;
- mMainHandler.sendMessage(msg);
+ mServiceHandler.sendMessage(msg);
}
// If service is killed it cannot be recreated because it would not know which
@@ -278,7 +286,7 @@
@Override
public void onDestroy() {
- mMainHandler.getLooper().quit();
+ mServiceHandler.getLooper().quit();
mScreenshotHandler.getLooper().quit();
super.onDestroy();
}
@@ -613,7 +621,7 @@
// ignore it
}
- mInfoDialog.initialize(mContext, info);
+ mMainThreadHandler.post(() -> mInfoDialog.initialize(mContext, info));
}
/**
@@ -652,11 +660,11 @@
private void takeScreenshot(int id, int delay) {
if (delay > 0) {
Log.d(TAG, "Taking screenshot for " + id + " in " + delay + " seconds");
- final Message msg = mMainHandler.obtainMessage();
+ final Message msg = mServiceHandler.obtainMessage();
msg.what = MSG_DELAYED_SCREENSHOT;
msg.arg1 = id;
msg.arg2 = delay - 1;
- mMainHandler.sendMessageDelayed(msg, DateUtils.SECOND_IN_MILLIS);
+ mServiceHandler.sendMessageDelayed(msg, DateUtils.SECOND_IN_MILLIS);
return;
}
@@ -696,7 +704,7 @@
boolean taken = takeScreenshot(mContext, screenshotFile);
setTakingScreenshot(false);
- Message.obtain(mMainHandler, MSG_SCREENSHOT_RESPONSE, requestMsg.arg1, taken ? 1 : 0,
+ Message.obtain(mServiceHandler, MSG_SCREENSHOT_RESPONSE, requestMsg.arg1, taken ? 1 : 0,
screenshotFile).sendToTarget();
}
@@ -1111,6 +1119,12 @@
* description will be saved on {@code description.txt}.
*/
private void addDetailsToZipFile(BugreportInfo info) {
+ synchronized (mLock) {
+ addDetailsToZipFileLocked(info);
+ }
+ }
+
+ private void addDetailsToZipFileLocked(BugreportInfo info) {
if (info.bugreportFile == null) {
// One possible reason is a bug in the Parcelization code.
Log.wtf(TAG, "addDetailsToZipFile(): no bugreportFile on " + info);
@@ -1432,6 +1446,7 @@
/**
* Sets its internal state and displays the dialog.
*/
+ @MainThread
void initialize(final Context context, BugreportInfo info) {
final String dialogTitle =
context.getString(R.string.bugreport_info_dialog_title, info.id);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 3a39e91..3542500 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -813,10 +813,10 @@
public void onDensityOrFontScaleChanged() {
initDimens();
- if (mIsSummaryWithChildren) {
- if (mChildrenContainer != null) {
- mChildrenContainer.reInflateViews(mExpandClickListener, mEntry.notification);
- }
+ // Let's update our childrencontainer. This is intentionally not guarded with
+ // mIsSummaryWithChildren since we might have had children but not anymore.
+ if (mChildrenContainer != null) {
+ mChildrenContainer.reInflateViews(mExpandClickListener, mEntry.notification);
}
if (mGuts != null) {
View oldGuts = mGuts;
@@ -1458,9 +1458,11 @@
public void setUserLocked(boolean userLocked) {
mUserLocked = userLocked;
mPrivateLayout.setUserExpanding(userLocked);
- if (mIsSummaryWithChildren) {
+ // This is intentionally not guarded with mIsSummaryWithChildren since we might have had
+ // children but not anymore.
+ if (mChildrenContainer != null) {
mChildrenContainer.setUserLocked(userLocked);
- if (userLocked || !isGroupExpanded()) {
+ if (mIsSummaryWithChildren && (userLocked || !isGroupExpanded())) {
updateBackgroundForGroupState();
}
}
@@ -2189,4 +2191,9 @@
}
}
}
+
+ @VisibleForTesting
+ protected void setChildrenContainer(NotificationChildrenContainer childrenContainer) {
+ mChildrenContainer = childrenContainer;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index e1acc9b..e0f4429 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -18,6 +18,7 @@
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import android.R.attr;
import android.app.ActivityManager;
import android.app.Dialog;
import android.app.Notification;
@@ -31,6 +32,7 @@
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.graphics.Bitmap;
+import android.graphics.PorterDuff.Mode;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Handler;
@@ -52,6 +54,7 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.util.UserIcons;
import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.GuestResumeSessionReceiver;
import com.android.systemui.R;
@@ -739,7 +742,12 @@
if (item.isAddUser) {
return context.getDrawable(R.drawable.ic_add_circle_qs);
}
- return UserIcons.getDefaultUserIcon(item.resolveId(), /* light= */ true);
+ Drawable icon = UserIcons.getDefaultUserIcon(item.resolveId(), /* light= */ false);
+ if (item.isGuest) {
+ icon.setColorFilter(Utils.getColorAttr(context, android.R.attr.colorForeground),
+ Mode.SRC_IN);
+ }
+ return icon;
}
public void refresh() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 3f211e3..0dbd1d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -29,6 +29,7 @@
import android.widget.RemoteViews;
import android.widget.TextView;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.ExpandableNotificationRow;
@@ -1223,4 +1224,9 @@
}
return getGroupExpandFraction();
}
+
+ @VisibleForTesting
+ public boolean isUserLocked() {
+ return mUserLocked;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
index bc79116..a3a0a6f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
@@ -16,7 +16,9 @@
package com.android.systemui.statusbar;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
@@ -26,6 +28,8 @@
import android.support.test.runner.AndroidJUnit4;
import android.view.View;
+import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
+
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
@@ -65,4 +69,25 @@
== View.VISIBLE);
}
+ @Test
+ public void testUserLockedResetEvenWhenNoChildren() {
+ mGroup.setUserLocked(true);
+ mGroup.removeAllChildren();
+ mGroup.setUserLocked(false);
+ Assert.assertFalse("The childrencontainer should not be userlocked but is, the state "
+ + "seems out of sync.", mGroup.getChildrenContainer().isUserLocked());
+ }
+
+ @Test
+ public void testReinflatedOnDensityChange() {
+ mGroup.setUserLocked(true);
+ mGroup.removeAllChildren();
+ mGroup.setUserLocked(false);
+ NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
+ mGroup.setChildrenContainer(mockContainer);
+ mGroup.onDensityOrFontScaleChanged();
+ verify(mockContainer).reInflateViews(any(), any());
+ }
+
+
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index aa80075..bad8dcf 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -174,13 +174,13 @@
public void send(int resultCode, Bundle resultData) throws RemoteException {
final AssistStructure structure = resultData.getParcelable(KEY_STRUCTURE);
if (structure == null) {
- Slog.wtf(TAG, "no assist structure");
+ Slog.e(TAG, "No assist structure - app might have crashed providing it");
return;
}
final Bundle receiverExtras = resultData.getBundle(KEY_RECEIVER_EXTRAS);
if (receiverExtras == null) {
- Slog.wtf(TAG, "No " + KEY_RECEIVER_EXTRAS + " on receiver");
+ Slog.e(TAG, "No receiver extras - app might have crashed providing it");
return;
}
@@ -682,7 +682,6 @@
removeSelf();
return;
}
- resetViewStatesLocked(dataset, ViewState.STATE_WAITING_DATASET_AUTH);
}
final Parcelable result = data.getParcelable(AutofillManager.EXTRA_AUTHENTICATION_RESULT);
@@ -1362,7 +1361,6 @@
}
// ...or handle authentication.
- // TODO(b/37424539): proper implementation
mService.setDatasetAuthenticationSelected(dataset.getId());
setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, false);
final Intent fillInIntent = createAuthFillInIntent(
@@ -1455,21 +1453,36 @@
}
try {
if (sDebug) Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
+
// Skip null values as a null values means no change
final int entryCount = dataset.getFieldIds().size();
final List<AutofillId> ids = new ArrayList<>(entryCount);
final List<AutofillValue> values = new ArrayList<>(entryCount);
+ boolean waitingDatasetAuth = false;
for (int i = 0; i < entryCount; i++) {
if (dataset.getFieldValues().get(i) == null) {
continue;
}
- ids.add(dataset.getFieldIds().get(i));
+ final AutofillId viewId = dataset.getFieldIds().get(i);
+ ids.add(viewId);
values.add(dataset.getFieldValues().get(i));
+ final ViewState viewState = mViewStates.get(viewId);
+ if (viewState != null
+ && (viewState.getState() & ViewState.STATE_WAITING_DATASET_AUTH) != 0) {
+ if (sVerbose) {
+ Slog.v(TAG, "autofillApp(): view " + viewId + " waiting auth");
+ }
+ waitingDatasetAuth = true;
+ viewState.resetState(ViewState.STATE_WAITING_DATASET_AUTH);
+ }
}
if (!ids.isEmpty()) {
+ if (waitingDatasetAuth) {
+ hideFillUiIfOwnedByMe();
+ }
mClient.autofill(id, ids, values);
+ setViewStatesLocked(null, dataset, ViewState.STATE_AUTOFILLED, false);
}
- setViewStatesLocked(null, dataset, ViewState.STATE_AUTOFILLED, false);
} catch (RemoteException e) {
Slog.w(TAG, "Error autofilling activity: " + e);
}
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index f87fa19..8a52c96 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -174,7 +174,7 @@
* fill UI is ready to be displayed (i.e. when response and bounds are set).
*/
void maybeCallOnFillReady() {
- if ((mState & (STATE_AUTOFILLED | STATE_WAITING_DATASET_AUTH)) != 0) {
+ if ((mState & STATE_AUTOFILLED) != 0) {
if (sDebug) Slog.d(TAG, "Ignoring UI for " + id + " on " + getStateAsString());
return;
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7ae4138..4b12bc4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -175,6 +175,9 @@
implements PendingIntent.OnFinished {
private static final String TAG = ConnectivityService.class.getSimpleName();
+ public static final String DIAG_ARG = "--diag";
+ public static final String SHORT_ARG = "--short";
+
private static final boolean DBG = true;
private static final boolean VDBG = false;
@@ -1852,7 +1855,7 @@
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- if (argsContain(args, "--diag")) {
+ if (argsContain(args, DIAG_ARG)) {
dumpNetworkDiagnostics(pw);
return;
}
@@ -1938,7 +1941,7 @@
pw.decreaseIndent();
}
- if (argsContain(args, "--short") == false) {
+ if (argsContain(args, SHORT_ARG) == false) {
pw.println();
synchronized (mValidationLogs) {
pw.println("mValidationLogs (most recent first):");
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 9fa6624..03e9dd2 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -222,7 +222,9 @@
long[] clickEffectTimings = getLongIntArray(context.getResources(),
com.android.internal.R.array.config_virtualKeyVibePattern);
VibrationEffect clickEffect;
- if (clickEffectTimings.length == 1) {
+ if (clickEffectTimings.length == 0) {
+ clickEffect = null;
+ } else if (clickEffectTimings.length == 1) {
clickEffect = VibrationEffect.createOneShot(
clickEffectTimings[0], VibrationEffect.DEFAULT_AMPLITUDE);
} else {
@@ -232,7 +234,6 @@
new long[] {0, 30, 100, 30} /*timings*/, -1);
mFallbackEffects = new VibrationEffect[] { clickEffect, doubleClickEffect };
-
}
public void systemReady() {
@@ -701,7 +702,7 @@
}
}
final int id = prebaked.getId();
- if (id < 0 || id >= mFallbackEffects.length) {
+ if (id < 0 || id >= mFallbackEffects.length || mFallbackEffects[id] == null) {
Slog.w(TAG, "Failed to play prebaked effect, no fallback");
return 0;
}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 81a1458..901092e 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -20,6 +20,7 @@
import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
+import static com.android.server.ConnectivityService.SHORT_ARG;
import android.app.Notification;
import android.app.NotificationManager;
@@ -47,6 +48,7 @@
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
+import android.net.util.SharedLog;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Bundle;
@@ -62,7 +64,6 @@
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.LocalLog;
import android.util.Log;
import android.util.SparseArray;
@@ -147,9 +148,7 @@
}
}
- private final static int MAX_LOG_RECORDS = 500;
-
- private final LocalLog mLocalLog = new LocalLog(MAX_LOG_RECORDS);
+ private final SharedLog mLog = new SharedLog(TAG);
// used to synchronize public access to members
private final Object mPublicSync;
@@ -180,7 +179,7 @@
public Tethering(Context context, INetworkManagementService nmService,
INetworkStatsService statsService, INetworkPolicyManager policyManager,
Looper looper, MockableSystemProperties systemProperties) {
- mLocalLog.log("CONSTRUCTED");
+ mLog.mark("constructed");
mContext = context;
mNMService = nmService;
mStatsService = statsService;
@@ -195,9 +194,9 @@
mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper);
mTetherMasterSM.start();
- mOffloadController = new OffloadController(mTetherMasterSM.getHandler());
+ mOffloadController = new OffloadController(mTetherMasterSM.getHandler(), mLog);
mUpstreamNetworkMonitor = new UpstreamNetworkMonitor(
- mContext, mTetherMasterSM, TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
+ mContext, mTetherMasterSM, TetherMasterSM.EVENT_UPSTREAM_CALLBACK, mLog);
mForwardedDownstreams = new HashSet<>();
mStateReceiver = new StateReceiver();
@@ -1094,7 +1093,7 @@
addState(mSetDnsForwardersErrorState);
mNotifyList = new ArrayList<>();
- mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList);
+ mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mLog);
setInitialState(mInitialState);
}
@@ -1141,7 +1140,7 @@
try {
mNMService.setIpForwardingEnabled(true);
} catch (Exception e) {
- mLocalLog.log("ERROR " + e);
+ mLog.e(e);
transitionTo(mSetIpForwardingEnabledErrorState);
return false;
}
@@ -1154,12 +1153,12 @@
mNMService.stopTethering();
mNMService.startTethering(cfg.dhcpRanges);
} catch (Exception ee) {
- mLocalLog.log("ERROR " + ee);
+ mLog.e(ee);
transitionTo(mStartTetheringErrorState);
return false;
}
}
- mLocalLog.log("SET master tether settings: ON");
+ mLog.log("SET master tether settings: ON");
return true;
}
@@ -1167,19 +1166,19 @@
try {
mNMService.stopTethering();
} catch (Exception e) {
- mLocalLog.log("ERROR " + e);
+ mLog.e(e);
transitionTo(mStopTetheringErrorState);
return false;
}
try {
mNMService.setIpForwardingEnabled(false);
} catch (Exception e) {
- mLocalLog.log("ERROR " + e);
+ mLog.e(e);
transitionTo(mSetIpForwardingDisabledErrorState);
return false;
}
transitionTo(mInitialState);
- mLocalLog.log("SET master tether settings: OFF");
+ mLog.log("SET master tether settings: OFF");
return true;
}
@@ -1305,13 +1304,13 @@
}
try {
mNMService.setDnsForwarders(network, dnsServers);
- mLocalLog.log(String.format(
- "SET DNS forwarders: network=%s dnsServers=[%s]",
+ mLog.log(String.format(
+ "SET DNS forwarders: network=%s dnsServers=%s",
network, Arrays.toString(dnsServers)));
} catch (Exception e) {
// TODO: Investigate how this can fail and what exactly
// happens if/when such failures occur.
- mLocalLog.log("ERROR setting DNS forwarders failed, " + e);
+ mLog.e("setting DNS forwarders failed, " + e);
transitionTo(mSetDnsForwardersErrorState);
}
}
@@ -1788,12 +1787,23 @@
pw.println("Log:");
pw.increaseIndent();
- mLocalLog.readOnlyLocalLog().dump(fd, pw, args);
+ if (argsContain(args, SHORT_ARG)) {
+ pw.println("<log removed for brevity>");
+ } else {
+ mLog.dump(fd, pw, args);
+ }
pw.decreaseIndent();
pw.decreaseIndent();
}
+ private static boolean argsContain(String[] args, String target) {
+ for (String arg : args) {
+ if (arg.equals(target)) return true;
+ }
+ return false;
+ }
+
@Override
public void notifyInterfaceStateChange(String iface, TetherInterfaceStateMachine who,
int state, int error) {
@@ -1807,8 +1817,7 @@
}
}
- mLocalLog.log(String.format("OBSERVED iface=%s state=%s error=%s",
- iface, state, error));
+ mLog.log(String.format("OBSERVED iface=%s state=%s error=%s", iface, state, error));
try {
// Notify that we're tethering (or not) this interface.
@@ -1846,8 +1855,8 @@
private void trackNewTetherableInterface(String iface, int interfaceType) {
TetherState tetherState;
tetherState = new TetherState(new TetherInterfaceStateMachine(iface, mLooper,
- interfaceType, mNMService, mStatsService, this,
- new IPv6TetheringInterfaceServices(iface, mNMService)));
+ interfaceType, mLog, mNMService, mStatsService, this,
+ new IPv6TetheringInterfaceServices(iface, mNMService, mLog)));
mTetherStates.put(iface, tetherState);
tetherState.stateMachine.start();
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
index 2485654..518f6c1 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
@@ -25,6 +25,7 @@
import android.net.NetworkState;
import android.net.RouteInfo;
import android.net.util.NetworkConstants;
+import android.net.util.SharedLog;
import android.util.Log;
import java.net.Inet6Address;
@@ -64,6 +65,7 @@
}
private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
+ private final SharedLog mLog;
// NOTE: mActiveDownstreams is a list and not a hash data structure because
// we keep active downstreams in arrival order. This is done so /64s can
// be parceled out on a "first come, first served" basis and a /64 used by
@@ -74,8 +76,10 @@
private short mNextSubnetId;
private NetworkState mUpstreamNetworkState;
- public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList) {
+ public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList,
+ SharedLog log) {
mNotifyList = notifyList;
+ mLog = log.forSubComponent(TAG);
mActiveDownstreams = new LinkedList<>();
mUniqueLocalPrefix = generateUniqueLocalPrefix();
mNextSubnetId = 0;
@@ -115,7 +119,7 @@
if (VDBG) {
Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
}
- if (!canTetherIPv6(ns)) {
+ if (!canTetherIPv6(ns, mLog)) {
stopIPv6TetheringOnAllInterfaces();
setUpstreamNetworkState(null);
return;
@@ -150,9 +154,7 @@
null);
}
- if (DBG) {
- Log.d(TAG, "setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState));
- }
+ mLog.log("setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState));
}
private void updateIPv6TetheringInterfaces() {
@@ -206,7 +208,7 @@
return null;
}
- private static boolean canTetherIPv6(NetworkState ns) {
+ private static boolean canTetherIPv6(NetworkState ns, SharedLog sharedLog) {
// Broadly speaking:
//
// [1] does the upstream have an IPv6 default route?
@@ -260,13 +262,11 @@
final boolean outcome = canTether && supportedConfiguration;
- if (VDBG) {
- if (ns == null) {
- Log.d(TAG, "No available upstream.");
- } else {
- Log.d(TAG, String.format("IPv6 tethering is %s for upstream: %s",
- (outcome ? "available" : "not available"), toDebugString(ns)));
- }
+ if (ns == null) {
+ sharedLog.log("No available upstream.");
+ } else {
+ sharedLog.log(String.format("IPv6 tethering is %s for upstream: %s",
+ (outcome ? "available" : "not available"), toDebugString(ns)));
}
return outcome;
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
index c6a7925..adf4af8 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
@@ -28,10 +28,10 @@
import android.net.ip.RouterAdvertisementDaemon;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.net.util.NetdService;
+import android.net.util.SharedLog;
import android.os.INetworkManagementService;
import android.os.ServiceSpecificException;
import android.os.RemoteException;
-import android.util.Log;
import android.util.Slog;
import java.net.Inet6Address;
@@ -54,6 +54,7 @@
private final String mIfName;
private final INetworkManagementService mNMService;
+ private final SharedLog mLog;
private NetworkInterface mNetworkInterface;
private byte[] mHwAddr;
@@ -61,9 +62,11 @@
private RouterAdvertisementDaemon mRaDaemon;
private RaParams mLastRaParams;
- public IPv6TetheringInterfaceServices(String ifname, INetworkManagementService nms) {
+ public IPv6TetheringInterfaceServices(
+ String ifname, INetworkManagementService nms, SharedLog log) {
mIfName = ifname;
mNMService = nms;
+ mLog = log.forSubComponent(mIfName);
}
public boolean start() {
@@ -72,12 +75,12 @@
try {
mNetworkInterface = NetworkInterface.getByName(mIfName);
} catch (SocketException e) {
- Log.e(TAG, "Error looking up NetworkInterfaces for " + mIfName, e);
+ mLog.e("Error looking up NetworkInterfaces: " + e);
stop();
return false;
}
if (mNetworkInterface == null) {
- Log.e(TAG, "Failed to find NetworkInterface for " + mIfName);
+ mLog.e("Failed to find NetworkInterface");
stop();
return false;
}
@@ -85,7 +88,7 @@
try {
mHwAddr = mNetworkInterface.getHardwareAddress();
} catch (SocketException e) {
- Log.e(TAG, "Failed to find hardware address for " + mIfName, e);
+ mLog.e("Failed to find hardware address: " + e);
stop();
return false;
}
@@ -161,11 +164,11 @@
try {
final int removalFailures = mNMService.removeRoutesFromLocalNetwork(toBeRemoved);
if (removalFailures > 0) {
- Log.e(TAG, String.format("Failed to remove %d IPv6 routes from local table.",
+ mLog.e(String.format("Failed to remove %d IPv6 routes from local table.",
removalFailures));
}
} catch (RemoteException e) {
- Log.e(TAG, "Failed to remove IPv6 routes from local table: ", e);
+ mLog.e("Failed to remove IPv6 routes from local table: " + e);
}
}
@@ -195,7 +198,7 @@
// error (EEXIST is silently ignored).
mNMService.addInterfaceToLocalNetwork(mIfName, toBeAdded);
} catch (RemoteException e) {
- Log.e(TAG, "Failed to add IPv6 routes to local table: ", e);
+ mLog.e("Failed to add IPv6 routes to local table: " + e);
}
}
}
@@ -206,7 +209,7 @@
final INetd netd = NetdService.getInstance();
if (netd == null) {
if (newDnses != null) newDnses.clear();
- Log.e(TAG, "No netd service instance available; not setting local IPv6 addresses");
+ mLog.e("No netd service instance available; not setting local IPv6 addresses");
return;
}
@@ -217,7 +220,7 @@
try {
netd.interfaceDelAddress(mIfName, dnsString, RFC7421_PREFIX_LENGTH);
} catch (ServiceSpecificException | RemoteException e) {
- Log.e(TAG, "Failed to remove local dns IP: " + dnsString, e);
+ mLog.e("Failed to remove local dns IP " + dnsString + ": " + e);
}
}
}
@@ -234,7 +237,7 @@
try {
netd.interfaceAddAddress(mIfName, dnsString, RFC7421_PREFIX_LENGTH);
} catch (ServiceSpecificException | RemoteException e) {
- Log.e(TAG, "Failed to add local dns IP: " + dnsString, e);
+ mLog.e("Failed to add local dns IP " + dnsString + ": " + e);
newDnses.remove(dns);
}
}
@@ -243,7 +246,7 @@
try {
netd.tetherApplyDnsInterfaces();
} catch (ServiceSpecificException | RemoteException e) {
- Log.e(TAG, "Failed to update local DNS caching server");
+ mLog.e("Failed to update local DNS caching server");
if (newDnses != null) newDnses.clear();
}
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
index 220e751..8f21d99 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
@@ -18,7 +18,7 @@
import android.net.LinkProperties;
import android.os.Handler;
-import android.util.Log;
+import android.net.util.SharedLog;
/**
* A wrapper around hardware offload interface.
@@ -29,16 +29,18 @@
private static final String TAG = OffloadController.class.getSimpleName();
private final Handler mHandler;
+ private final SharedLog mLog;
private LinkProperties mUpstreamLinkProperties;
- public OffloadController(Handler h) {
+ public OffloadController(Handler h, SharedLog log) {
mHandler = h;
+ mLog = log.forSubComponent(TAG);
}
public void start() {
// TODO: initOffload() and configure callbacks to be handled on our
// preferred Handler.
- Log.d(TAG, "tethering offload not supported");
+ mLog.i("tethering offload not supported");
}
public void stop() {
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index d3cfd87..4a1d405 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -22,6 +22,7 @@
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkUtils;
+import android.net.util.SharedLog;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
@@ -82,6 +83,7 @@
private final State mTetheredState;
private final State mUnavailableState;
+ private final SharedLog mLog;
private final INetworkManagementService mNMService;
private final INetworkStatsService mStatsService;
private final IControlsTethering mTetherController;
@@ -93,10 +95,12 @@
private int mLastError;
private String mMyUpstreamIfaceName; // may change over time
- public TetherInterfaceStateMachine(String ifaceName, Looper looper, int interfaceType,
- INetworkManagementService nMService, INetworkStatsService statsService,
- IControlsTethering tetherController, IPv6TetheringInterfaceServices ipv6Svc) {
+ public TetherInterfaceStateMachine(
+ String ifaceName, Looper looper, int interfaceType, SharedLog log,
+ INetworkManagementService nMService, INetworkStatsService statsService,
+ IControlsTethering tetherController, IPv6TetheringInterfaceServices ipv6Svc) {
super(ifaceName, looper);
+ mLog = log.forSubComponent(ifaceName);
mNMService = nMService;
mStatsService = statsService;
mTetherController = tetherController;
@@ -162,7 +166,7 @@
mNMService.setInterfaceConfig(mIfaceName, ifcg);
}
} catch (Exception e) {
- Log.e(TAG, "Error configuring interface " + mIfaceName, e);
+ mLog.e("Error configuring interface " + e);
return false;
}
@@ -203,7 +207,7 @@
transitionTo(mTetheredState);
break;
default:
- Log.e(TAG, "Invalid tethering interface serving state specified.");
+ mLog.e("Invalid tethering interface serving state specified.");
}
break;
case CMD_INTERFACE_DOWN:
@@ -232,13 +236,13 @@
try {
mNMService.tetherInterface(mIfaceName);
} catch (Exception e) {
- Log.e(TAG, "Error Tethering: " + e.toString());
+ mLog.e("Error Tethering: " + e);
mLastError = ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
return;
}
if (!mIPv6TetherSvc.start()) {
- Log.e(TAG, "Failed to start IPv6TetheringInterfaceServices");
+ mLog.e("Failed to start IPv6TetheringInterfaceServices");
// TODO: Make this a fatal error once Bluetooth IPv6 is sorted.
return;
}
@@ -255,7 +259,7 @@
mNMService.untetherInterface(mIfaceName);
} catch (Exception e) {
mLastError = ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
- Log.e(TAG, "Failed to untether interface: " + e.toString());
+ mLog.e("Failed to untether interface: " + e);
}
configureIfaceIp(false);
@@ -316,7 +320,7 @@
maybeLogMessage(this, message.what);
switch (message.what) {
case CMD_TETHER_REQUESTED:
- Log.e(TAG, "CMD_TETHER_REQUESTED while in local hotspot mode.");
+ mLog.e("CMD_TETHER_REQUESTED while in local-only hotspot mode.");
break;
case CMD_TETHER_CONNECTION_CHANGED:
// Ignored in local hotspot state.
@@ -389,7 +393,7 @@
boolean retValue = true;
switch (message.what) {
case CMD_TETHER_REQUESTED:
- Log.e(TAG, "CMD_TETHER_REQUESTED while already tethering.");
+ mLog.e("CMD_TETHER_REQUESTED while already tethering.");
break;
case CMD_TETHER_CONNECTION_CHANGED:
String newUpstreamIfaceName = (String)(message.obj);
@@ -406,7 +410,7 @@
mNMService.startInterfaceForwarding(mIfaceName,
newUpstreamIfaceName);
} catch (Exception e) {
- Log.e(TAG, "Exception enabling Nat: " + e.toString());
+ mLog.e("Exception enabling NAT: " + e);
cleanupUpstreamInterface(newUpstreamIfaceName);
mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
transitionTo(mInitialState);
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 97a2d5e..be71490 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -29,6 +29,7 @@
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.NetworkState;
+import android.net.util.SharedLog;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -73,6 +74,7 @@
private static final int CALLBACK_MOBILE_REQUEST = 3;
private final Context mContext;
+ private final SharedLog mLog;
private final StateMachine mTarget;
private final Handler mHandler;
private final int mWhat;
@@ -84,16 +86,18 @@
private boolean mDunRequired;
private Network mCurrentDefault;
- public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, int what) {
+ public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, int what, SharedLog log) {
mContext = ctx;
mTarget = tgt;
mHandler = mTarget.getHandler();
mWhat = what;
+ mLog = log.forSubComponent(TAG);
}
@VisibleForTesting
- public UpstreamNetworkMonitor(StateMachine tgt, int what, ConnectivityManager cm) {
- this(null, tgt, what);
+ public UpstreamNetworkMonitor(
+ StateMachine tgt, int what, ConnectivityManager cm, SharedLog log) {
+ this(null, tgt, what, log);
mCM = cm;
}
@@ -136,7 +140,7 @@
public void registerMobileNetworkRequest() {
if (mMobileNetworkCallback != null) {
- Log.e(TAG, "registerMobileNetworkRequest() already registered");
+ mLog.e("registerMobileNetworkRequest() already registered");
return;
}
@@ -156,7 +160,7 @@
// TODO: Change the timeout from 0 (no onUnavailable callback) to some
// moderate callback timeout. This might be useful for updating some UI.
// Additionally, we log a message to aid in any subsequent debugging.
- Log.d(TAG, "requesting mobile upstream network: " + mobileUpstreamRequest);
+ mLog.i("requesting mobile upstream network: " + mobileUpstreamRequest);
cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, legacyType, mHandler);
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 8b8e3c4..908e517 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -7691,7 +7691,10 @@
default:
return null;
}
- if (pattern.length == 1) {
+ if (pattern.length == 0) {
+ // No vibration
+ return null;
+ } else if (pattern.length == 1) {
// One-shot vibration
return VibrationEffect.createOneShot(pattern[0], VibrationEffect.DEFAULT_AMPLITUDE);
} else {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index a8d19e9..c8ade2d 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3122,10 +3122,6 @@
if (reason == null) {
reason = "";
}
- if (reason.equals(PowerManager.REBOOT_RECOVERY)
- || reason.equals(PowerManager.REBOOT_RECOVERY_UPDATE)) {
- reason = "recovery";
- }
// If the reason is "quiescent", it means that the boot process should proceed
// without turning on the screen/lights.
@@ -3134,6 +3130,15 @@
if (reason.equals(PowerManager.REBOOT_QUIESCENT)) {
sQuiescent = true;
reason = "";
+ } else if (reason.endsWith("," + PowerManager.REBOOT_QUIESCENT)) {
+ sQuiescent = true;
+ reason = reason.substring(0,
+ reason.length() - PowerManager.REBOOT_QUIESCENT.length() - 1);
+ }
+
+ if (reason.equals(PowerManager.REBOOT_RECOVERY)
+ || reason.equals(PowerManager.REBOOT_RECOVERY_UPDATE)) {
+ reason = "recovery";
}
if (sQuiescent) {
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 65e3ec0..f769261 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -78,6 +78,9 @@
// requires that the duration of the two animations are the same.
SurfaceControl thumbnail;
int thumbnailTransactionSeq;
+ // TODO(b/62029108): combine both members into a private one. Create a member function to set
+ // the thumbnail layer to +1 to the highest layer position and replace all setter instances
+ // with this function. Remove all unnecessary calls to both variables in other classes.
int thumbnailLayer;
int thumbnailForceAboveLayer;
Animation thumbnailAnimation;
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
index 172ec48..01a3143 100644
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -257,9 +257,20 @@
w.mLayer = layer;
w.mWinAnimator.mAnimLayer = w.getAnimLayerAdjustment()
+ w.getSpecialWindowAnimLayerAdjustment();
- if (w.mAppToken != null && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer > 0
- && w.mWinAnimator.mAnimLayer > w.mAppToken.mAppAnimator.thumbnailForceAboveLayer) {
- w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = w.mWinAnimator.mAnimLayer;
+ if (w.mAppToken != null && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer > 0) {
+ if (w.mWinAnimator.mAnimLayer > w.mAppToken.mAppAnimator.thumbnailForceAboveLayer) {
+ w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = w.mWinAnimator.mAnimLayer;
+ }
+ // TODO(b/62029108): the entire contents of the if statement should call the refactored
+ // function to set the thumbnail layer for w.AppToken
+ int highestLayer = w.mAppToken.getHighestAnimLayer();
+ if (highestLayer > 0) {
+ if (w.mAppToken.mAppAnimator.thumbnail != null
+ && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer != highestLayer) {
+ w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = highestLayer;
+ w.mAppToken.mAppAnimator.thumbnail.setLayer(highestLayer + 1);
+ }
+ }
}
}
}
diff --git a/services/net/java/android/net/util/SharedLog.java b/services/net/java/android/net/util/SharedLog.java
new file mode 100644
index 0000000..343d237
--- /dev/null
+++ b/services/net/java/android/net/util/SharedLog.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2017 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.net.util;
+
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.StringJoiner;
+
+
+/**
+ * Class to centralize logging functionality for tethering.
+ *
+ * All access to class methods other than dump() must be on the same thread.
+ *
+ * @hide
+ */
+public class SharedLog {
+ private final static int DEFAULT_MAX_RECORDS = 500;
+ private final static String COMPONENT_DELIMITER = ".";
+
+ private enum Category {
+ NONE,
+ ERROR,
+ MARK,
+ WARN,
+ };
+
+ private final LocalLog mLocalLog;
+ // The tag to use for output to the system log. This is not output to the
+ // LocalLog because that would be redundant.
+ private final String mTag;
+ // The component (or subcomponent) of a system that is sharing this log.
+ // This can grow in depth if components call forSubComponent() to obtain
+ // their SharedLog instance. The tag is not included in the component for
+ // brevity.
+ private final String mComponent;
+
+ public SharedLog(String tag) {
+ this(DEFAULT_MAX_RECORDS, tag);
+ }
+
+ public SharedLog(int maxRecords, String tag) {
+ this(new LocalLog(maxRecords), tag, tag);
+ }
+
+ private SharedLog(LocalLog localLog, String tag, String component) {
+ mLocalLog = localLog;
+ mTag = tag;
+ mComponent = component;
+ }
+
+ public SharedLog forSubComponent(String component) {
+ if (!isRootLogInstance()) {
+ component = mComponent + COMPONENT_DELIMITER + component;
+ }
+ return new SharedLog(mLocalLog, mTag, component);
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ mLocalLog.readOnlyLocalLog().dump(fd, writer, args);
+ }
+
+ //////
+ // Methods that both log an entry and emit it to the system log.
+ //////
+
+ public void e(Exception e) {
+ Log.e(mTag, record(Category.ERROR, e.toString()));
+ }
+
+ public void e(String msg) {
+ Log.e(mTag, record(Category.ERROR, msg));
+ }
+
+ public void i(String msg) {
+ Log.i(mTag, record(Category.NONE, msg));
+ }
+
+ public void w(String msg) {
+ Log.w(mTag, record(Category.WARN, msg));
+ }
+
+ //////
+ // Methods that only log an entry (and do NOT emit to the system log).
+ //////
+
+ public void log(String msg) {
+ record(Category.NONE, msg);
+ }
+
+ public void mark(String msg) {
+ record(Category.MARK, msg);
+ }
+
+ private String record(Category category, String msg) {
+ final String entry = logLine(category, msg);
+ mLocalLog.log(entry);
+ return entry;
+ }
+
+ private String logLine(Category category, String msg) {
+ final StringJoiner sj = new StringJoiner(" ");
+ if (!isRootLogInstance()) sj.add("[" + mComponent + "]");
+ if (category != Category.NONE) sj.add(category.toString());
+ return sj.add(msg).toString();
+ }
+
+ // Check whether this SharedLog instance is nominally the top level in
+ // a potential hierarchy of shared logs (the root of a tree),
+ // or is a subcomponent within the hierarchy.
+ private boolean isRootLogInstance() {
+ return TextUtils.isEmpty(mComponent) || mComponent.equals(mTag);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 32eee84..6618a69 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -115,6 +115,10 @@
"mChildAppWindowAbove");
mChildAppWindowBelow = createCommonWindow(mAppWindow, TYPE_APPLICATION_MEDIA_OVERLAY,
"mChildAppWindowBelow");
+
+ // Adding a display will cause freezing the display. Make sure to wait until it's unfrozen
+ // to not run into race conditions with the tests.
+ waitUntilHandlersIdle();
}
@After
@@ -135,6 +139,9 @@
mDisplayContent.removeImmediately();
sWm.mInputMethodTarget = null;
}
+
+ // Wait until everything is really cleaned up.
+ waitUntilHandlersIdle();
}
private WindowState createCommonWindow(WindowState parent, int type, String name) {
diff --git a/tests/net/java/android/net/util/SharedLogTest.java b/tests/net/java/android/net/util/SharedLogTest.java
new file mode 100644
index 0000000..7fd7a63
--- /dev/null
+++ b/tests/net/java/android/net/util/SharedLogTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2017 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.net.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Vector;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SharedLogTest {
+ private static final String TIMESTAMP_PATTERN =
+ "^[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9][0-9]";
+ private static final String TIMESTAMP = "mm-dd HH:MM:SS.xxx";
+
+ @Test
+ public void testBasicOperation() {
+ final SharedLog logTop = new SharedLog("top");
+ logTop.mark("first post!");
+
+ final SharedLog logLevel2a = logTop.forSubComponent("twoA");
+ final SharedLog logLevel2b = logTop.forSubComponent("twoB");
+ logLevel2b.e("2b or not 2b");
+ logLevel2a.w("second post?");
+
+ final SharedLog logLevel3 = logLevel2a.forSubComponent("three");
+ logTop.log("still logging");
+ logLevel3.log("3 >> 2");
+ logLevel2a.mark("ok: last post");
+
+ final String[] expected = {
+ TIMESTAMP + " - MARK first post!",
+ TIMESTAMP + " - [twoB] ERROR 2b or not 2b",
+ TIMESTAMP + " - [twoA] WARN second post?",
+ TIMESTAMP + " - still logging",
+ TIMESTAMP + " - [twoA.three] 3 >> 2",
+ TIMESTAMP + " - [twoA] MARK ok: last post",
+ };
+ // Verify the logs are all there and in the correct order.
+ verifyLogLines(expected, logTop);
+
+ // In fact, because they all share the same underlying LocalLog,
+ // every subcomponent SharedLog's dump() is identical.
+ verifyLogLines(expected, logLevel2a);
+ verifyLogLines(expected, logLevel2b);
+ verifyLogLines(expected, logLevel3);
+ }
+
+ private static void verifyLogLines(String[] expected, SharedLog log) {
+ final ByteArrayOutputStream ostream = new ByteArrayOutputStream();
+ final PrintWriter pw = new PrintWriter(ostream, true);
+ log.dump(null, pw, null);
+
+ final String dumpOutput = ostream.toString();
+ assertTrue(dumpOutput != null);
+ assertTrue(!"".equals(dumpOutput));
+
+ final String[] lines = dumpOutput.split("\n");
+ assertEquals(expected.length, lines.length);
+
+ for (int i = 0; i < lines.length; i++) {
+ // Fix up the timestamps.
+ lines[i] = lines[i].replaceAll(TIMESTAMP_PATTERN, TIMESTAMP);
+ }
+
+ for (int i = 0; i < expected.length; i++) {
+ assertEquals(expected[i], lines[i]);
+ }
+ }
+}
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
index a3f33dc..27e683c 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
@@ -38,6 +38,7 @@
import android.net.ConnectivityManager;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
+import android.net.util.SharedLog;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.test.TestLooper;
@@ -63,12 +64,14 @@
@Mock private IControlsTethering mTetherHelper;
@Mock private InterfaceConfiguration mInterfaceConfiguration;
@Mock private IPv6TetheringInterfaceServices mIPv6TetheringInterfaceServices;
+ @Mock private SharedLog mSharedLog;
private final TestLooper mLooper = new TestLooper();
private TetherInterfaceStateMachine mTestedSm;
private void initStateMachine(int interfaceType) throws Exception {
- mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(), interfaceType,
+ mTestedSm = new TetherInterfaceStateMachine(
+ IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog,
mNMService, mStatsService, mTetherHelper, mIPv6TetheringInterfaceServices);
mTestedSm.start();
// Starting the state machine always puts us in a consistent state and notifies
@@ -90,12 +93,13 @@
@Before public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ when(mSharedLog.forSubComponent(anyString())).thenReturn(mSharedLog);
}
@Test
public void startsOutAvailable() {
mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(),
- TETHERING_BLUETOOTH, mNMService, mStatsService, mTetherHelper,
+ TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper,
mIPv6TetheringInterfaceServices);
mTestedSm.start();
mLooper.dispatchAll();
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index c72efb0..9bb392a 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -25,11 +25,13 @@
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.Handler;
@@ -40,6 +42,7 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.net.util.SharedLog;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -69,6 +72,7 @@
@Mock private Context mContext;
@Mock private IConnectivityManager mCS;
+ @Mock private SharedLog mLog;
private TestStateMachine mSM;
private TestConnectivityManager mCM;
@@ -78,10 +82,12 @@
MockitoAnnotations.initMocks(this);
reset(mContext);
reset(mCS);
+ reset(mLog);
+ when(mLog.forSubComponent(anyString())).thenReturn(mLog);
mCM = spy(new TestConnectivityManager(mContext, mCS));
mSM = new TestStateMachine();
- mUNM = new UpstreamNetworkMonitor(mSM, EVENT_UNM_UPDATE, (ConnectivityManager) mCM);
+ mUNM = new UpstreamNetworkMonitor(mSM, EVENT_UNM_UPDATE, (ConnectivityManager) mCM, mLog);
}
@After public void tearDown() throws Exception {