Merge "Fix import lines in SystemServer.java"
diff --git a/Android.bp b/Android.bp
index 65f232d..8163ff8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -287,6 +287,8 @@
"core/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl",
"core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl",
"core/java/android/service/gatekeeper/IGateKeeperService.aidl",
+ "core/java/android/service/intelligence/IIntelligenceService.aidl",
+
"core/java/android/service/notification/INotificationListener.aidl",
"core/java/android/service/notification/IStatusBarNotificationHolder.aidl",
"core/java/android/service/notification/IConditionListener.aidl",
@@ -343,6 +345,7 @@
"core/java/android/view/autofill/IAutoFillManager.aidl",
"core/java/android/view/autofill/IAutoFillManagerClient.aidl",
"core/java/android/view/autofill/IAutofillWindowPresenter.aidl",
+ "core/java/android/view/intelligence/IIntelligenceManager.aidl",
"core/java/android/view/IApplicationToken.aidl",
"core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl",
"core/java/android/view/IDockedStackListener.aidl",
diff --git a/api/system-current.txt b/api/system-current.txt
index d8da475..24bdd9b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6041,6 +6041,7 @@
method public void setCallExtra(java.lang.String, java.lang.String);
method public void setCallExtraBoolean(java.lang.String, boolean);
method public void setCallExtraInt(java.lang.String, int);
+ method public void setCallRestrictCause(int);
method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
method public void updateCallType(android.telephony.ims.ImsCallProfile);
method public void updateMediaProfile(android.telephony.ims.ImsCallProfile);
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 6547b3a..a3cd8a3 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -205,10 +205,6 @@
],
srcs: [
- // atom_field_options.proto needs field_options.proto, but that is
- // not included in libprotobuf-cpp-lite, so compile it here.
- ":libprotobuf-internal-protos",
-
"src/atom_field_options.proto",
"src/atoms.proto",
"src/stats_log.proto",
@@ -263,11 +259,11 @@
],
proto: {
- type: "lite",
+ type: "full",
include_dirs: ["external/protobuf/src"],
},
- shared_libs: ["libprotobuf-cpp-lite"],
+ shared_libs: ["libprotobuf-cpp-full"],
}
@@ -280,10 +276,6 @@
defaults: ["statsd_defaults"],
srcs: [
- // atom_field_options.proto needs field_options.proto, but that is
- // not included in libprotobuf-cpp-lite, so compile it here.
- ":libprotobuf-internal-protos",
-
"src/atom_field_options.proto",
"src/atoms.proto",
"src/stats_log.proto",
@@ -298,7 +290,7 @@
],
proto: {
- type: "lite",
+ type: "full",
include_dirs: ["external/protobuf/src"],
},
@@ -320,7 +312,7 @@
shared_libs: [
"libgtest_prod",
"libstatslog",
- "libprotobuf-cpp-lite",
+ "libprotobuf-cpp-full",
],
}
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 065f49e..f0f5993 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -272,7 +272,7 @@
}
/*
- * onDumpReport dumps serialized ConfigMetricsReportList into outData.
+ * onDumpReport dumps serialized ConfigMetricsReportList into proto.
*/
void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTimeStampNs,
const bool include_current_partial_bucket,
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index 13ab844..ee111cd 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -24,7 +24,6 @@
#include "subscriber/IncidentdReporter.h"
#include "subscriber/SubscriberReporter.h"
-#include <inttypes.h>
#include <statslog.h>
#include <time.h>
@@ -224,7 +223,7 @@
}
if (!mSubscriptions.empty()) {
- ALOGI("An anomaly (%" PRId64 ") %s has occurred! Informing subscribers.",
+ ALOGI("An anomaly (%lld) %s has occurred! Informing subscribers.",
mAlert.id(), key.toString().c_str());
informSubscribers(key);
} else {
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 4a9b521..136ba07 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -35,8 +35,6 @@
#include "stats_util.h"
#include "statslog.h"
-#include <inttypes.h>
-
using std::set;
using std::string;
using std::unordered_map;
@@ -575,7 +573,7 @@
for (int i = 0; i < config.no_report_metric_size(); ++i) {
const auto no_report_metric = config.no_report_metric(i);
if (metricMap.find(no_report_metric) == metricMap.end()) {
- ALOGW("no_report_metric %" PRId64 " not exist", no_report_metric);
+ ALOGW("no_report_metric %lld not exist", no_report_metric);
return false;
}
noReportMetricIds.insert(no_report_metric);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a666819..86ed267 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -120,6 +120,8 @@
import android.view.autofill.AutofillManager.AutofillClient;
import android.view.autofill.AutofillPopupWindow;
import android.view.autofill.IAutofillWindowPresenter;
+import android.view.intelligence.ContentCaptureEvent;
+import android.view.intelligence.IntelligenceManager;
import android.widget.AdapterView;
import android.widget.Toast;
import android.widget.Toolbar;
@@ -821,6 +823,10 @@
/** The autofill manager. Always access via {@link #getAutofillManager()}. */
@Nullable private AutofillManager mAutofillManager;
+ /** The screen observation manager. Always access via {@link #getIntelligenceManager()}. */
+ @Nullable private IntelligenceManager mIntelligenceManager;
+
+
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
@@ -994,7 +1000,7 @@
}
/**
- * (Create and) return the autofill manager
+ * (Creates, sets and) returns the autofill manager
*
* @return The autofill manager
*/
@@ -1006,6 +1012,43 @@
return mAutofillManager;
}
+ /**
+ * (Creates, sets, and ) returns the intelligence manager
+ *
+ * @return The intelligence manager
+ */
+ @NonNull private IntelligenceManager getIntelligenceManager() {
+ if (mIntelligenceManager == null) {
+ mIntelligenceManager = getSystemService(IntelligenceManager.class);
+ }
+ return mIntelligenceManager;
+ }
+
+ private void notifyIntelligenceManagerIfNeeded(@ContentCaptureEvent.EventType int event) {
+ final IntelligenceManager im = getIntelligenceManager();
+ if (im == null || !im.isContentCaptureEnabled()) {
+ return;
+ }
+ switch (event) {
+ case ContentCaptureEvent.TYPE_ACTIVITY_CREATED:
+ //TODO(b/111276913): decide whether the InteractionSessionId should be
+ // saved / restored in the activity bundle.
+ im.onActivityCreated(mToken, getComponentName());
+ break;
+ case ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED:
+ im.onActivityDestroyed();
+ break;
+ case ContentCaptureEvent.TYPE_ACTIVITY_STARTED:
+ case ContentCaptureEvent.TYPE_ACTIVITY_RESUMED:
+ case ContentCaptureEvent.TYPE_ACTIVITY_PAUSED:
+ case ContentCaptureEvent.TYPE_ACTIVITY_STOPPED:
+ im.onActivityLifecycleEvent(event);
+ break;
+ default:
+ Log.w(TAG, "notifyIntelligenceManagerIfNeeded(): invalid type " + event);
+ }
+ }
+
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
@@ -1081,6 +1124,8 @@
}
mRestoredFromBundle = savedInstanceState != null;
mCalled = true;
+
+ notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_CREATED);
}
/**
@@ -1314,6 +1359,7 @@
if (mAutoFillResetNeeded) {
getAutofillManager().onVisibleForAutofill();
}
+ notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STARTED);
}
/**
@@ -1396,6 +1442,7 @@
}
}
}
+ notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_RESUMED);
mCalled = true;
}
@@ -1789,6 +1836,7 @@
mAutoFillIgnoreFirstResumePause = false;
}
}
+ notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_PAUSED);
mCalled = true;
}
@@ -1977,6 +2025,7 @@
getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_CANCEL,
mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN));
}
+ notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STOPPED);
}
}
@@ -2047,6 +2096,9 @@
}
getApplication().dispatchActivityDestroyed(this);
+
+ notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED);
+
}
/**
@@ -6403,9 +6455,16 @@
void dumpInner(@NonNull String prefix, @Nullable FileDescriptor fd,
@NonNull PrintWriter writer, @Nullable String[] args) {
- if (args != null && args.length > 0 && args[0].equals("--autofill")) {
- dumpAutofillManager(prefix, writer);
- return;
+ if (args != null && args.length > 0) {
+ // Handle special cases
+ switch (args[0]) {
+ case "--autofill":
+ dumpAutofillManager(prefix, writer);
+ return;
+ case "--intelligence":
+ dumpIntelligenceManager(prefix, writer);
+ return;
+ }
}
writer.print(prefix); writer.print("Local Activity ");
writer.print(Integer.toHexString(System.identityHashCode(this)));
@@ -6435,6 +6494,7 @@
mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix);
dumpAutofillManager(prefix, writer);
+ dumpIntelligenceManager(prefix, writer);
ResourcesManager.getInstance().dump(prefix, writer);
}
@@ -6450,6 +6510,15 @@
}
}
+ void dumpIntelligenceManager(String prefix, PrintWriter writer) {
+ final IntelligenceManager im = getIntelligenceManager();
+ if (im != null) {
+ im.dump(prefix, writer);
+ } else {
+ writer.print(prefix); writer.println("No IntelligenceManager");
+ }
+ }
+
/**
* Bit indicating that this activity is "immersive" and should not be
* interrupted by notifications if possible.
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index f267169..e95f9ab 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -66,8 +66,8 @@
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.IHdmiControlService;
import android.hardware.input.InputManager;
-import android.hardware.iris.IrisManager;
import android.hardware.iris.IIrisService;
+import android.hardware.iris.IrisManager;
import android.hardware.location.ContextHubManager;
import android.hardware.radio.RadioManager;
import android.hardware.usb.IUsbManager;
@@ -163,6 +163,8 @@
import android.view.autofill.AutofillManager;
import android.view.autofill.IAutoFillManager;
import android.view.inputmethod.InputMethodManager;
+import android.view.intelligence.IIntelligenceManager;
+import android.view.intelligence.IntelligenceManager;
import android.view.textclassifier.TextClassificationManager;
import android.view.textservice.TextServicesManager;
@@ -1032,6 +1034,17 @@
return new AutofillManager(ctx.getOuterContext(), service);
}});
+ registerService(Context.INTELLIGENCE_MANAGER_SERVICE, IntelligenceManager.class,
+ new CachedServiceFetcher<IntelligenceManager>() {
+ @Override
+ public IntelligenceManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ // Get the services without throwing as this is an optional feature
+ IBinder b = ServiceManager.getService(Context.INTELLIGENCE_MANAGER_SERVICE);
+ IIntelligenceManager service = IIntelligenceManager.Stub.asInterface(b);
+ return new IntelligenceManager(ctx.getOuterContext(), service);
+ }});
+
registerService(Context.VR_SERVICE, VrManager.class, new CachedServiceFetcher<VrManager>() {
@Override
public VrManager createService(ContextImpl ctx) throws ServiceNotFoundException {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 6a7829b..ccf8417 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3865,6 +3865,14 @@
public static final String AUTOFILL_MANAGER_SERVICE = "autofill";
/**
+ * Official published name of the intelligence service.
+ *
+ * @hide
+ * @see #getSystemService(String)
+ */
+ public static final String INTELLIGENCE_MANAGER_SERVICE = "intelligence";
+
+ /**
* Use with {@link #getSystemService(String)} to access the
* {@link com.android.server.voiceinteraction.SoundTriggerService}.
*
diff --git a/core/java/android/service/intelligence/IIntelligenceService.aidl b/core/java/android/service/intelligence/IIntelligenceService.aidl
new file mode 100644
index 0000000..bacad8b
--- /dev/null
+++ b/core/java/android/service/intelligence/IIntelligenceService.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 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.service.intelligence;
+
+import android.service.intelligence.InteractionSessionId;
+import android.service.intelligence.InteractionContext;
+
+import android.view.intelligence.ContentCaptureEvent;
+
+import java.util.List;
+
+
+/**
+ * Interface from the system to an intelligence service.
+ *
+ * @hide
+ */
+oneway interface IIntelligenceService {
+
+ // Called when session is created (context not null) or destroyed (context null)
+ void onSessionLifecycle(in InteractionContext context, in InteractionSessionId sessionId);
+
+ void onContentCaptureEvents(in InteractionSessionId sessionId,
+ in List<ContentCaptureEvent> events);
+}
diff --git a/core/java/android/service/intelligence/IntelligenceService.java b/core/java/android/service/intelligence/IntelligenceService.java
index 4b8825d..a2b60f0 100644
--- a/core/java/android/service/intelligence/IntelligenceService.java
+++ b/core/java/android/service/intelligence/IntelligenceService.java
@@ -15,16 +15,24 @@
*/
package android.service.intelligence;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
import android.view.intelligence.ContentCaptureEvent;
import java.util.List;
/**
- * A service used to captures the content of the screen.
+ * A service used to capture the content of the screen.
*
* <p>The data collected by this service can be analyzed and combined with other sources to provide
* contextual data in other areas of the system such as Autofill.
@@ -34,6 +42,8 @@
@SystemApi
public abstract class IntelligenceService extends Service {
+ private static final String TAG = "IntelligenceService";
+
/**
* The {@link Intent} that must be declared as handled by the service.
* To be supported, the service must also require the
@@ -43,6 +53,50 @@
public static final String SERVICE_INTERFACE =
"android.service.intelligence.IntelligenceService";
+ private Handler mHandler;
+
+ private final IIntelligenceService mInterface = new IIntelligenceService.Stub() {
+
+ @Override
+ public void onSessionLifecycle(InteractionContext context, InteractionSessionId sessionId)
+ throws RemoteException {
+ if (context != null) {
+ mHandler.sendMessage(
+ obtainMessage(IntelligenceService::onCreateInteractionSession,
+ IntelligenceService.this, context, sessionId));
+ } else {
+ mHandler.sendMessage(
+ obtainMessage(IntelligenceService::onDestroyInteractionSession,
+ IntelligenceService.this, sessionId));
+ }
+ }
+ @Override
+ public void onContentCaptureEvents(InteractionSessionId sessionId,
+ List<ContentCaptureEvent> events) {
+ mHandler.sendMessage(
+ obtainMessage(IntelligenceService::onContentCaptureEvent,
+ IntelligenceService.this, sessionId, events));
+
+ }
+ };
+
+ @CallSuper
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mHandler = new Handler(Looper.getMainLooper(), null, true);
+ }
+
+ /** @hide */
+ @Override
+ public final IBinder onBind(Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return mInterface.asBinder();
+ }
+ Log.w(TAG, "Tried to bind to wrong intent: " + intent);
+ return null;
+ }
+
/**
* Creates a new interaction session.
*
@@ -59,11 +113,12 @@
* @param sessionId the session's Id
* @param events the events
*/
+ // TODO(b/111276913): rename to onContentCaptureEvents
public abstract void onContentCaptureEvent(@NonNull InteractionSessionId sessionId,
@NonNull List<ContentCaptureEvent> events);
/**
- * Destroys the content capture session identified by the specified {@code sessionId}.
+ * Destroys the interaction session.
*
* @param sessionId the id of the session to destroy
*/
diff --git a/core/java/android/service/intelligence/InteractionContext.aidl b/core/java/android/service/intelligence/InteractionContext.aidl
new file mode 100644
index 0000000..4ce6aa4
--- /dev/null
+++ b/core/java/android/service/intelligence/InteractionContext.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2018, 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.service.intelligence;
+
+parcelable InteractionContext;
diff --git a/core/java/android/service/intelligence/InteractionContext.java b/core/java/android/service/intelligence/InteractionContext.java
index 4d83820..c1803ad 100644
--- a/core/java/android/service/intelligence/InteractionContext.java
+++ b/core/java/android/service/intelligence/InteractionContext.java
@@ -23,6 +23,9 @@
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -32,7 +35,7 @@
public final class InteractionContext implements Parcelable {
/**
- * Flag used to indicate that the app explicitly disabled contents capture for the activity
+ * Flag used to indicate that the app explicitly disabled content capture for the activity
* (using
* {@link android.view.intelligence.IntelligenceManager#disableContentCapture()}),
* in which case the service will just receive activity-level events.
@@ -54,24 +57,34 @@
@Retention(RetentionPolicy.SOURCE)
@interface ContextCreationFlags{}
+ // TODO(b/111276913): create new object for taskId + componentName / reuse on other places
+ private final @NonNull ComponentName mComponentName;
+ private final int mTaskId;
+ private final int mDisplayId;
+ private final int mFlags;
+
+
/** @hide */
- InteractionContext() {
+ public InteractionContext(@NonNull ComponentName componentName, int taskId, int displayId,
+ int flags) {
+ mComponentName = Preconditions.checkNotNull(componentName);
+ mTaskId = taskId;
+ mDisplayId = displayId;
+ mFlags = flags;
}
/**
* Gets the id of the {@link TaskInfo task} associated with this context.
*/
public int getTaskId() {
- //TODO(b/111276913): implement
- return 108;
+ return mTaskId;
}
/**
* Gets the activity associated with this context.
*/
public @NonNull ComponentName getActivityComponent() {
- //TODO(b/111276913): implement
- return null;
+ return mComponentName;
}
/**
@@ -79,8 +92,7 @@
* {G android.hardware.display.DisplayManager#getDisplay(int) DisplayManager.getDisplay()}.
*/
public int getDisplayId() {
- //TODO(b/111276913): implement
- return 42;
+ return mDisplayId;
}
/**
@@ -90,8 +102,26 @@
* {@link #FLAG_DISABLED_BY_APP}.
*/
public @ContextCreationFlags int getFlags() {
- //TODO(b/111276913): implement
- return 42;
+ return mFlags;
+ }
+
+ /**
+ * @hide
+ */
+ // TODO(b/111276913): dump to proto as well
+ public void dump(PrintWriter pw) {
+ pw.print("comp="); pw.print(mComponentName.flattenToShortString());
+ pw.print(", taskId="); pw.print(mTaskId);
+ pw.print(", displayId="); pw.print(mDisplayId);
+ if (mFlags > 0) {
+ pw.print(", flags="); pw.print(mFlags);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Context[act=" + mComponentName.flattenToShortString() + ", taskId=" + mTaskId
+ + ", displayId=" + mDisplayId + ", flags=" + mFlags + "]";
}
@Override
@@ -101,6 +131,10 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeParcelable(mComponentName, flags);
+ parcel.writeInt(mTaskId);
+ parcel.writeInt(mDisplayId);
+ parcel.writeInt(mFlags);
}
public static final Parcelable.Creator<InteractionContext> CREATOR =
@@ -108,8 +142,11 @@
@Override
public InteractionContext createFromParcel(Parcel parcel) {
- // TODO(b/111276913): implement
- return null;
+ final ComponentName componentName = parcel.readParcelable(null);
+ final int taskId = parcel.readInt();
+ final int displayId = parcel.readInt();
+ final int flags = parcel.readInt();
+ return new InteractionContext(componentName, taskId, displayId, flags);
}
@Override
diff --git a/core/java/android/service/intelligence/InteractionSessionId.aidl b/core/java/android/service/intelligence/InteractionSessionId.aidl
new file mode 100644
index 0000000..a5392b6
--- /dev/null
+++ b/core/java/android/service/intelligence/InteractionSessionId.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2018, 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.service.intelligence;
+
+parcelable InteractionSessionId;
diff --git a/core/java/android/service/intelligence/InteractionSessionId.java b/core/java/android/service/intelligence/InteractionSessionId.java
index 4c9d706..a2971ae 100644
--- a/core/java/android/service/intelligence/InteractionSessionId.java
+++ b/core/java/android/service/intelligence/InteractionSessionId.java
@@ -16,17 +16,85 @@
package android.service.intelligence;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import java.io.PrintWriter;
+import java.util.UUID;
+
// TODO(b/111276913): add javadocs / implement equals/hashcode/string
/** @hide */
@SystemApi
public final class InteractionSessionId implements Parcelable {
- /** @hide */
+ private final @NonNull String mValue;
+
+ /**
+ * Creates a new instance.
+ *
+ * @hide
+ */
public InteractionSessionId() {
+ this(UUID.randomUUID().toString());
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param value The internal value.
+ *
+ * @hide
+ */
+ public InteractionSessionId(@NonNull String value) {
+ mValue = value;
+ }
+
+ /**
+ * @hide
+ */
+ public String getValue() {
+ return mValue;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((mValue == null) ? 0 : mValue.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ final InteractionSessionId other = (InteractionSessionId) obj;
+ if (mValue == null) {
+ if (other.mValue != null) return false;
+ } else if (!mValue.equals(other.mValue)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @inheritdoc
+ *
+ * <p><b>NOTE: </b>this method is only useful for debugging purposes and is not guaranteed to
+ * be stable, hence it should not be used to identify the session.
+ */
+ @Override
+ public String toString() {
+ return mValue;
+ }
+
+ /** @hide */
+ // TODO(b/111276913): dump to proto as well
+ public void dump(PrintWriter pw) {
+ pw.print(mValue);
}
@Override
@@ -36,6 +104,7 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeString(mValue);
}
public static final Parcelable.Creator<InteractionSessionId> CREATOR =
@@ -43,8 +112,7 @@
@Override
public InteractionSessionId createFromParcel(Parcel parcel) {
- // TODO(b/111276913): implement
- return null;
+ return new InteractionSessionId(parcel.readString());
}
@Override
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.aidl b/core/java/android/view/intelligence/ContentCaptureEvent.aidl
new file mode 100644
index 0000000..c66a6cb
--- /dev/null
+++ b/core/java/android/view/intelligence/ContentCaptureEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 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.view.intelligence;
+
+parcelable ContentCaptureEvent;
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.java b/core/java/android/view/intelligence/ContentCaptureEvent.java
index b8330e5..2530ae3 100644
--- a/core/java/android/view/intelligence/ContentCaptureEvent.java
+++ b/core/java/android/view/intelligence/ContentCaptureEvent.java
@@ -30,6 +30,11 @@
@SystemApi
public final class ContentCaptureEvent implements Parcelable {
+ /** @hide */
+ public static final int TYPE_ACTIVITY_DESTROYED = -2;
+ /** @hide */
+ public static final int TYPE_ACTIVITY_CREATED = -1;
+
/**
* Called when the activity is started.
*/
@@ -85,10 +90,18 @@
TYPE_VIEW_TEXT_CHANGED
})
@Retention(RetentionPolicy.SOURCE)
- @interface EventType{}
+ public @interface EventType{}
+
+ private final int mType;
+ private final long mEventTime;
+ private final int mFlags;
+
/** @hide */
- ContentCaptureEvent() {
+ public ContentCaptureEvent(int type, long eventTime, int flags) {
+ mType = type;
+ mEventTime = eventTime;
+ mFlags = flags;
}
/**
@@ -99,14 +112,14 @@
* {@link #TYPE_VIEW_ADDED}, {@link #TYPE_VIEW_REMOVED}, or {@link #TYPE_VIEW_TEXT_CHANGED}.
*/
public @EventType int getType() {
- return 42;
+ return mType;
}
/**
* Gets when the event was generated, in ms.
*/
public long getEventTime() {
- return 48151623;
+ return mEventTime;
}
/**
@@ -116,7 +129,7 @@
* {@link android.view.intelligence.IntelligenceManager#FLAG_USER_INPUT}.
*/
public int getFlags() {
- return 0;
+ return mFlags;
}
/**
@@ -149,6 +162,15 @@
return null;
}
+ @Override
+ public String toString() {
+ final StringBuilder string = new StringBuilder("ContentCaptureEvent[type=")
+ .append(getTypeAsString(mType)).append(", time=").append(mEventTime);
+ if (mFlags > 0) {
+ string.append(", flags=").append(mFlags);
+ }
+ return string.append(']').toString();
+ }
@Override
public int describeContents() {
@@ -157,6 +179,9 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mType);
+ parcel.writeLong(mEventTime);
+ parcel.writeInt(mFlags);
}
public static final Parcelable.Creator<ContentCaptureEvent> CREATOR =
@@ -164,8 +189,10 @@
@Override
public ContentCaptureEvent createFromParcel(Parcel parcel) {
- // TODO(b/111276913): implement
- return null;
+ final int type = parcel.readInt();
+ final long eventTime = parcel.readLong();
+ final int flags = parcel.readInt();
+ return new ContentCaptureEvent(type, eventTime, flags);
}
@Override
@@ -173,4 +200,27 @@
return new ContentCaptureEvent[size];
}
};
+
+
+ /** @hide */
+ public static String getTypeAsString(@EventType int type) {
+ switch (type) {
+ case TYPE_ACTIVITY_STARTED:
+ return "ACTIVITY_STARTED";
+ case TYPE_ACTIVITY_RESUMED:
+ return "ACTIVITY_RESUMED";
+ case TYPE_ACTIVITY_PAUSED:
+ return "ACTIVITY_PAUSED";
+ case TYPE_ACTIVITY_STOPPED:
+ return "ACTIVITY_STOPPED";
+ case TYPE_VIEW_ADDED:
+ return "VIEW_ADDED";
+ case TYPE_VIEW_REMOVED:
+ return "VIEW_REMOVED";
+ case TYPE_VIEW_TEXT_CHANGED:
+ return "VIEW_TEXT_CHANGED";
+ default:
+ return "UKNOWN_TYPE: " + type;
+ }
+ }
}
diff --git a/core/java/android/view/intelligence/IIntelligenceManager.aidl b/core/java/android/view/intelligence/IIntelligenceManager.aidl
new file mode 100644
index 0000000..2f128de
--- /dev/null
+++ b/core/java/android/view/intelligence/IIntelligenceManager.aidl
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 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.view.intelligence;
+
+import android.content.ComponentName;
+import android.os.IBinder;
+import android.service.intelligence.InteractionSessionId;
+import android.view.intelligence.ContentCaptureEvent;
+
+import com.android.internal.os.IResultReceiver;
+
+import java.util.List;
+
+/**
+ * {@hide}
+ */
+oneway interface IIntelligenceManager {
+ /**
+ * Starts a session, sending the "remote" sessionId to the receiver.
+ */
+ void startSession(int userId, IBinder activityToken, in ComponentName componentName,
+ in InteractionSessionId sessionId, int flags, in IResultReceiver result);
+
+ /**
+ * Finishes a session.
+ */
+ void finishSession(int userId, in InteractionSessionId sessionId);
+
+ /**
+ * Sends a batch of events
+ */
+ void sendEvents(int userId, in InteractionSessionId sessionId,
+ in List<ContentCaptureEvent> events);
+}
diff --git a/core/java/android/view/intelligence/IntelligenceManager.java b/core/java/android/view/intelligence/IntelligenceManager.java
index 5513ce2f..9bf6c2c 100644
--- a/core/java/android/view/intelligence/IntelligenceManager.java
+++ b/core/java/android/view/intelligence/IntelligenceManager.java
@@ -18,29 +18,196 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.SystemService;
import android.content.ComponentName;
import android.content.Context;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.service.intelligence.InteractionSessionId;
+import android.util.Log;
+import android.view.intelligence.ContentCaptureEvent.EventType;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.IResultReceiver;
import com.android.internal.util.Preconditions;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.List;
import java.util.Set;
/**
- * TODO(b/111276913): add javadocs / implement / add SystemService / PackageFeature
+ * TODO(b/111276913): add javadocs / implement
*/
+@SystemService(Context.INTELLIGENCE_MANAGER_SERVICE)
public final class IntelligenceManager {
+ private static final String TAG = "IntelligenceManager";
+
+ // TODO(b/111276913): define a way to dynamically set it (for example, using settings?)
+ private static final boolean VERBOSE = false;
+
/**
* Used to indicate that a text change was caused by user input (for example, through IME).
*/
//TODO(b/111276913): link to notifyTextChanged() method once available
public static final int FLAG_USER_INPUT = 0x1;
+
+ /**
+ * Initial state, when there is no session.
+ *
+ * @hide
+ */
+ public static final int STATE_UNKNOWN = 0;
+
+ /**
+ * Service's startSession() was called, but server didn't confirm it was created yet.
+ *
+ * @hide
+ */
+ public static final int STATE_WAITING_FOR_SERVER = 1;
+
+ /**
+ * Session is active.
+ *
+ * @hide
+ */
+ public static final int STATE_ACTIVE = 2;
+
private final Context mContext;
+ @Nullable
+ private final IIntelligenceManager mService;
+
+ private final Object mLock = new Object();
+
+ @Nullable
+ @GuardedBy("mLock")
+ private InteractionSessionId mId;
+
+ @GuardedBy("mLock")
+ private int mState = STATE_UNKNOWN;
+
+ @GuardedBy("mLock")
+ private IBinder mApplicationToken;
+
+ // TODO(b/111276913): replace by an interface name implemented by Activity, similar to
+ // AutofillClient
+ @GuardedBy("mLock")
+ private ComponentName mComponentName;
+
/** @hide */
- public IntelligenceManager(@NonNull Context context) {
+ public IntelligenceManager(@NonNull Context context, @Nullable IIntelligenceManager service) {
mContext = Preconditions.checkNotNull(context, "context cannot be null");
+ mService = service;
+ }
+
+ /** @hide */
+ public void onActivityCreated(@NonNull IBinder token, @NonNull ComponentName componentName) {
+ if (!isContentCaptureEnabled()) return;
+
+ synchronized (mLock) {
+ if (mState != STATE_UNKNOWN) {
+ Log.w(TAG, "ignoring onActivityStarted(" + token + ") while on state "
+ + getStateAsStringLocked());
+ return;
+ }
+ mState = STATE_WAITING_FOR_SERVER;
+ mId = new InteractionSessionId();
+ mApplicationToken = token;
+ mComponentName = componentName;
+
+ if (VERBOSE) {
+ Log.v(TAG, "onActivityStarted(): token=" + token + ", act=" + componentName
+ + ", id=" + mId);
+ }
+ final int flags = 0; // TODO(b/111276913): get proper flags
+
+ try {
+ mService.startSession(mContext.getUserId(), mApplicationToken, componentName,
+ mId, flags, new IResultReceiver.Stub() {
+ @Override
+ public void send(int resultCode, Bundle resultData)
+ throws RemoteException {
+ synchronized (mLock) {
+ if (resultCode > 0) {
+ mState = STATE_ACTIVE;
+ } else {
+ // TODO(b/111276913): handle other cases like disabled by
+ // service
+ mState = STATE_UNKNOWN;
+ }
+ if (VERBOSE) {
+ Log.v(TAG, "onActivityStarted() result: code=" + resultCode
+ + ", id=" + mId
+ + ", state=" + getStateAsStringLocked());
+ }
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Used for intermediate events (i.e, other than created and destroyed).
+ *
+ * @hide
+ */
+ public void onActivityLifecycleEvent(@EventType int type) {
+ if (!isContentCaptureEnabled()) return;
+
+ //TODO(b/111276913): should buffer event (and call service on handler thread), instead of
+ // calling right away
+ final ContentCaptureEvent event = new ContentCaptureEvent(type, SystemClock.uptimeMillis(),
+ 0);
+ final List<ContentCaptureEvent> events = Arrays.asList(event);
+
+ synchronized (mLock) {
+ //TODO(b/111276913): check session state; for example, how to handle if it's waiting for
+ // remote id
+
+ if (VERBOSE) {
+ Log.v(TAG, "onActivityLifecycleEvent() for " + mComponentName.flattenToShortString()
+ + ": " + ContentCaptureEvent.getTypeAsString(type));
+ }
+
+ try {
+ mService.sendEvents(mContext.getUserId(), mId, events);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /** @hide */
+ public void onActivityDestroyed() {
+ if (!isContentCaptureEnabled()) return;
+
+ synchronized (mLock) {
+ //TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
+ // id) and send it to the cache of batched commands
+
+ if (VERBOSE) {
+ Log.v(TAG, "onActivityDestroyed(): state=" + getStateAsStringLocked()
+ + ", mId=" + mId);
+ }
+
+ try {
+ mService.finishSession(mContext.getUserId(), mId);
+ mState = STATE_UNKNOWN;
+ mId = null;
+ mApplicationToken = null;
+ mComponentName = null;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
/**
@@ -54,11 +221,13 @@
}
/**
- * Checks whether contents capture is enabled for this activity.
+ * Checks whether content capture is enabled for this activity.
*/
public boolean isContentCaptureEnabled() {
- //TODO(b/111276913): implement
- return false;
+ //TODO(b/111276913): properly implement by checking if it was explicitly disabled by
+ // service, or if service is not set
+ // (and probably renamign to isEnabledLocked()
+ return mService != null;
}
/**
@@ -68,6 +237,7 @@
* it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}.
*/
public void disableContentCapture() {
+ //TODO(b/111276913): implement
}
/**
@@ -140,4 +310,40 @@
//TODO(b/111276913): implement
return null;
}
+
+ /** @hide */
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.println("IntelligenceManager");
+ final String prefix2 = prefix + " ";
+ synchronized (mLock) {
+ pw.print(prefix2); pw.print("mContext: "); pw.println(mContext);
+ pw.print(prefix2); pw.print("mService: "); pw.println(mService);
+ pw.print(prefix2); pw.print("user: "); pw.println(mContext.getUserId());
+ pw.print(prefix2); pw.print("enabled: "); pw.println(isContentCaptureEnabled());
+ pw.print(prefix2); pw.print("id: "); pw.println(mId);
+ pw.print(prefix2); pw.print("state: "); pw.print(mState); pw.print(" (");
+ pw.print(getStateAsStringLocked()); pw.println(")");
+ pw.print(prefix2); pw.print("appToken: "); pw.println(mApplicationToken);
+ pw.print(prefix2); pw.print("componentName: "); pw.println(mComponentName);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private String getStateAsStringLocked() {
+ return getStateAsString(mState);
+ }
+
+ @NonNull
+ private static String getStateAsString(int state) {
+ switch (state) {
+ case STATE_UNKNOWN:
+ return "UNKNOWN";
+ case STATE_WAITING_FOR_SERVER:
+ return "WAITING_FOR_SERVER";
+ case STATE_ACTIVE:
+ return "ACTIVE";
+ default:
+ return "INVALID:" + state;
+ }
+ }
}
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index fb1fde2..3d50d2d 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -232,9 +232,9 @@
return;
}
dprintf(fd, "\nPackage: %s", proto->package_name().c_str());
- dprintf(fd, "\nVersion: %" PRId64, proto->version_code());
- dprintf(fd, "\nStats since: %" PRId64 "ns", proto->stats_start());
- dprintf(fd, "\nStats end: %" PRId64 "ns", proto->stats_end());
+ dprintf(fd, "\nVersion: %lld", proto->version_code());
+ dprintf(fd, "\nStats since: %lldns", proto->stats_start());
+ dprintf(fd, "\nStats end: %lldns", proto->stats_end());
auto summary = proto->summary();
dprintf(fd, "\nTotal frames rendered: %d", summary.total_frames());
dprintf(fd, "\nJanky frames: %d (%.2f%%)", summary.janky_frames(),
diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h
index 0377426..ad76559 100644
--- a/libs/protoutil/include/android/util/ProtoOutputStream.h
+++ b/libs/protoutil/include/android/util/ProtoOutputStream.h
@@ -97,7 +97,6 @@
bool write(uint64_t fieldId, double val);
bool write(uint64_t fieldId, float val);
bool write(uint64_t fieldId, int val);
- bool write(uint64_t fieldId, long val);
bool write(uint64_t fieldId, long long val);
bool write(uint64_t fieldId, bool val);
bool write(uint64_t fieldId, std::string val);
diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp
index 0c62d52..ff3fad6 100644
--- a/libs/protoutil/src/ProtoOutputStream.cpp
+++ b/libs/protoutil/src/ProtoOutputStream.cpp
@@ -116,34 +116,6 @@
}
bool
-ProtoOutputStream::write(uint64_t fieldId, long val)
-{
- if (mCompact) return false;
- const uint32_t id = (uint32_t)fieldId;
- switch (fieldId & FIELD_TYPE_MASK) {
- case FIELD_TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
- case FIELD_TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
- case FIELD_TYPE_INT64: writeInt64Impl(id, (long long)val); break;
- case FIELD_TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
- case FIELD_TYPE_INT32: writeInt32Impl(id, (int)val); break;
- case FIELD_TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
- case FIELD_TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
- case FIELD_TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
- case FIELD_TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
- case FIELD_TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
- case FIELD_TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
- case FIELD_TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
- case FIELD_TYPE_ENUM: writeEnumImpl(id, (int)val); break;
- case FIELD_TYPE_BOOL: writeBoolImpl(id, val != 0); break;
- default:
- ALOGW("Field type %d is not supported when writing long val.",
- (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
- return false;
- }
- return true;
-}
-
-bool
ProtoOutputStream::write(uint64_t fieldId, long long val)
{
return internalWrite(fieldId, val, "long long");
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index b51caa5..7470192 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -23,9 +23,7 @@
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.graphics.SurfaceTexture;
-import android.net.Uri;
import android.os.Handler;
-import android.os.Parcel;
import android.os.PersistableBundle;
import android.view.Surface;
import android.view.SurfaceHolder;
@@ -34,7 +32,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -241,47 +238,6 @@
return new MediaPlayer2Impl(context);
}
- private static final String[] decodeMediaPlayer2Uri(String location) {
- Uri uri = Uri.parse(location);
- if (!"mediaplayer2".equals(uri.getScheme())) {
- return new String[] {location};
- }
-
- List<String> uris = uri.getQueryParameters("uri");
- if (uris.isEmpty()) {
- return new String[] {location};
- }
-
- List<String> keys = uri.getQueryParameters("key");
- List<String> values = uri.getQueryParameters("value");
- if (keys.size() != values.size()) {
- return new String[] {uris.get(0)};
- }
-
- List<String> ls = new ArrayList();
- ls.add(uris.get(0));
- for (int i = 0; i < keys.size() ; i++) {
- ls.add(keys.get(i));
- ls.add(values.get(i));
- }
-
- return ls.toArray(new String[ls.size()]);
- }
-
- private static final String encodeMediaPlayer2Uri(String uri, String[] keys, String[] values) {
- Uri.Builder builder = new Uri.Builder();
- builder.scheme("mediaplayer2").path("/").appendQueryParameter("uri", uri);
- if (keys == null || values == null || keys.length != values.length) {
- return builder.build().toString();
- }
- for (int i = 0; i < keys.length ; i++) {
- builder
- .appendQueryParameter("key", keys[i])
- .appendQueryParameter("value", values[i]);
- }
- return builder.build().toString();
- }
-
/**
* @hide
*/
@@ -291,12 +247,6 @@
}
/**
- * Returns a {@link MediaPlayerBase} implementation which runs based on
- * this MediaPlayer2 instance.
- */
- public abstract MediaPlayerBase getMediaPlayerBase();
-
- /**
* Releases the resources held by this {@code MediaPlayer2} object.
*
* It is considered good practice to call this method when you're
@@ -342,8 +292,7 @@
* Starts or resumes playback. If playback had previously been paused,
* playback will continue from where it was paused. If playback had
* reached end of stream and been paused, or never started before,
- * playback will start at the beginning. If the source had not been
- * prepared, the player will prepare the source and play.
+ * playback will start at the beginning.
*
* @return a token which can be used to cancel the operation later with {@link #cancel}.
*/
@@ -404,9 +353,8 @@
/**
* Gets the current buffered media source position received through progressive downloading.
- * The received buffering percentage indicates how much of the content has been buffered
- * or played. For example a buffering update of 80 percent when half the content
- * has already been played indicates that the next 30 percent of the
+ * For example a buffering update of 8000 milliseconds when 5000 milliseconds of the content
+ * has already been played indicates that the next 3000 milliseconds of the
* content to play has been buffered.
*
* @return the current buffered media source position in milliseconds
@@ -416,7 +364,6 @@
/**
* MediaPlayer2 has not been prepared or just has been reset.
* In this state, MediaPlayer2 doesn't fetch data.
- * @hide
*/
public static final int PLAYER_STATE_IDLE = 1001;
@@ -424,26 +371,23 @@
* MediaPlayer2 has been just prepared.
* In this state, MediaPlayer2 just fetches data from media source,
* but doesn't actively render data.
- * @hide
*/
public static final int PLAYER_STATE_PREPARED = 1002;
/**
* MediaPlayer2 is paused.
- * In this state, MediaPlayer2 doesn't actively render data.
- * @hide
+ * In this state, MediaPlayer2 has allocated resources to construct playback
+ * pipeline, but it doesn't actively render data.
*/
public static final int PLAYER_STATE_PAUSED = 1003;
/**
* MediaPlayer2 is actively playing back data.
- * @hide
*/
public static final int PLAYER_STATE_PLAYING = 1004;
/**
* MediaPlayer2 has hit some fatal error and cannot continue playback.
- * @hide
*/
public static final int PLAYER_STATE_ERROR = 1005;
@@ -469,7 +413,7 @@
/**
* Sets the audio attributes for this MediaPlayer2.
* See {@link AudioAttributes} for how to build and configure an instance of this class.
- * You must call this method before {@link #prepare()} in order
+ * You must call this method before {@link #play()} and {@link #pause()} in order
* for the audio attributes to become effective thereafter.
* @param attributes a non-null set of audio attributes
* @return a token which can be used to cancel the operation later with {@link #cancel}.
@@ -547,7 +491,7 @@
public abstract Object setPlayerVolume(float volume);
/**
- * Returns the current volume of this player to this player.
+ * Returns the current volume of this player.
* Note that it does not take into account the associated stream volume.
* @return the player volume.
*/
@@ -561,24 +505,9 @@
}
/**
- * Create a request parcel which can be routed to the native media
- * player using {@link #invoke(Parcel, Parcel)}. The Parcel
- * returned has the proper InterfaceToken set. The caller should
- * not overwrite that token, i.e it can only append data to the
- * Parcel.
- *
- * @return A parcel suitable to hold a request for the native
- * player.
- * {@hide}
- */
- public Parcel newRequest() {
- return null;
- }
-
- /**
* Insert a task in the command queue to help the client to identify whether a batch
* of commands has been finished. When this command is processed, a notification
- * {@code EventCallback.onCommandLabelReached} will be fired with the
+ * {@link EventCallback#onCommandLabelReached onCommandLabelReached} will be fired with the
* given {@code label}.
*
* @see EventCallback#onCommandLabelReached
@@ -595,16 +524,13 @@
* portion of the media.
*
* Either a surface holder or surface must be set if a display or video sink
- * is needed. Not calling this method or {@link #setSurface(Surface)}
+ * is needed. Not calling this method or {@link #setSurface(Surface)}
* when playing back a video will result in only the audio track being played.
* A null surface holder or surface will result in only the audio track being
* played.
*
* @param sh the SurfaceHolder to use for video display
- * @throws IllegalStateException if the internal player engine has not been
- * initialized or has been released.
* @return a token which can be used to cancel the operation later with {@link #cancel}.
- * @hide
*/
public abstract Object setDisplay(SurfaceHolder sh);
@@ -624,56 +550,44 @@
*
* @param surface The {@link Surface} to be used for the video portion of
* the media.
- * @throws IllegalStateException if the internal player engine has not been
- * initialized or has been released.
* @return a token which can be used to cancel the operation later with {@link #cancel}.
*/
// This is an asynchronous call.
public abstract Object setSurface(Surface surface);
- /* Do not change these video scaling mode values below without updating
- * their counterparts in system/window.h! Please do not forget to update
- * {@link #isVideoScalingModeSupported} when new video scaling modes
- * are added.
- */
/**
- * Specifies a video scaling mode. The content is stretched to the
- * surface rendering area. When the surface has the same aspect ratio
- * as the content, the aspect ratio of the content is maintained;
- * otherwise, the aspect ratio of the content is not maintained when video
- * is being rendered.
- * There is no content cropping with this video scaling mode.
- */
- public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1;
-
- /**
- * Specifies a video scaling mode. The content is scaled, maintaining
- * its aspect ratio. The whole surface area is always used. When the
- * aspect ratio of the content is the same as the surface, no content
- * is cropped; otherwise, content is cropped to fit the surface.
- * @hide
- */
- public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2;
-
- /**
- * Sets video scaling mode. To make the target video scaling mode
- * effective during playback, this method must be called after
- * data source is set. If not called, the default video
- * scaling mode is {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT}.
+ * Set the low-level power management behavior for this MediaPlayer2. This
+ * can be used when the MediaPlayer2 is not playing through a SurfaceHolder
+ * set with {@link #setDisplay(SurfaceHolder)} and thus can use the
+ * high-level {@link #setScreenOnWhilePlaying(boolean)} feature.
*
- * <p> The supported video scaling modes are:
- * <ul>
- * <li> {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT}
- * </ul>
+ * <p>This function has the MediaPlayer2 access the low-level power manager
+ * service to control the device's power usage while playing is occurring.
+ * The parameter is a combination of {@link android.os.PowerManager} wake flags.
+ * Use of this method requires {@link android.Manifest.permission#WAKE_LOCK}
+ * permission.
+ * By default, no attempt is made to keep the device awake during playback.
*
- * @param mode target video scaling mode. Must be one of the supported
- * video scaling modes; otherwise, IllegalArgumentException will be thrown.
+ * @param context the Context to use
+ * @param mode the power/wake mode to set
* @return a token which can be used to cancel the operation later with {@link #cancel}.
- *
- * @see MediaPlayer2#VIDEO_SCALING_MODE_SCALE_TO_FIT
- * @hide
+ * @see android.os.PowerManager
*/
- public abstract Object setVideoScalingMode(int mode);
+ // This is an asynchronous call.
+ public abstract Object setWakeMode(Context context, int mode);
+
+ /**
+ * Control whether we should use the attached SurfaceHolder to keep the
+ * screen on while video playback is occurring. This is the preferred
+ * method over {@link #setWakeMode} where possible, since it doesn't
+ * require that the application have permission for low-level wake lock
+ * access.
+ *
+ * @param screenOn Supply true to keep the screen on, false to allow it to turn off.
+ * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ */
+ // This is an asynchronous call.
+ public abstract Object setScreenOnWhilePlaying(boolean screenOn);
/**
* Cancels a pending command.
@@ -702,7 +616,7 @@
* @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and
* does not correspond to a valid audio device.
*/
- // This is an asynchronous call.
+ // This is a synchronous call.
@Override
public abstract boolean setPreferredDevice(AudioDeviceInfo deviceInfo);
@@ -747,36 +661,6 @@
AudioRouting.OnRoutingChangedListener listener);
/**
- * Set the low-level power management behavior for this MediaPlayer2.
- *
- * <p>This function has the MediaPlayer2 access the low-level power manager
- * service to control the device's power usage while playing is occurring.
- * The parameter is a combination of {@link android.os.PowerManager} wake flags.
- * Use of this method requires {@link android.Manifest.permission#WAKE_LOCK}
- * permission.
- * By default, no attempt is made to keep the device awake during playback.
- *
- * @param context the Context to use
- * @param mode the power/wake mode to set
- * @see android.os.PowerManager
- * @hide
- */
- public abstract void setWakeMode(Context context, int mode);
-
- /**
- * Control whether we should use the attached SurfaceHolder to keep the
- * screen on while video playback is occurring. This is the preferred
- * method over {@link #setWakeMode} where possible, since it doesn't
- * require that the application have permission for low-level wake lock
- * access.
- *
- * @param screenOn Supply true to keep the screen on, false to allow it
- * to turn off.
- * @hide
- */
- public abstract void setScreenOnWhilePlaying(boolean screenOn);
-
- /**
* Returns the width of the video.
*
* @return the width of the video, or 0 if there is no video,
@@ -1719,18 +1603,21 @@
*/
public static final int CALL_COMPLETED_SET_BUFFERING_PARAMS = 31;
- /** The player just completed a call {@link #setVideoScalingMode}.
- * @see EventCallback#onCallCompleted
- * @hide
- */
- public static final int CALL_COMPLETED_SET_VIDEO_SCALING_MODE = 32;
-
/** The player just completed a call {@link #setDisplay}.
* @see EventCallback#onCallCompleted
- * @hide
*/
public static final int CALL_COMPLETED_SET_DISPLAY = 33;
+ /** The player just completed a call {@link #setWakeMode}.
+ * @see EventCallback#onCallCompleted
+ */
+ public static final int CALL_COMPLETED_SET_WAKE_MODE = 34;
+
+ /** The player just completed a call {@link #setScreenOnWhilePlaying}.
+ * @see EventCallback#onCallCompleted
+ */
+ public static final int CALL_COMPLETED_SET_SCREEN_ON_WHILE_PLAYING = 35;
+
/**
* The start of the methods which have separate call complete callback.
* @hide
@@ -1776,8 +1663,9 @@
CALL_COMPLETED_SKIP_TO_NEXT,
CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES,
CALL_COMPLETED_SET_BUFFERING_PARAMS,
- CALL_COMPLETED_SET_VIDEO_SCALING_MODE,
CALL_COMPLETED_SET_DISPLAY,
+ CALL_COMPLETED_SET_WAKE_MODE,
+ CALL_COMPLETED_SET_SCREEN_ON_WHILE_PLAYING,
CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED,
CALL_COMPLETED_PREPARE_DRM,
})
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index ef8db1d..6697a4f 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -19,11 +19,11 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.ContentProvider;
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
-import android.graphics.SurfaceTexture;
import android.graphics.Rect;
import android.media.MediaPlayer2Proto.PlayerMessage;
import android.media.MediaPlayer2Proto.Value;
@@ -34,8 +34,6 @@
import android.os.Message;
import android.os.PersistableBundle;
import android.os.PowerManager;
-import android.os.SystemProperties;
-import android.provider.Settings;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
@@ -78,18 +76,22 @@
native_init();
}
+ private static final int NEXT_SOURCE_STATE_ERROR = -1;
+ private static final int NEXT_SOURCE_STATE_INIT = 0;
+ private static final int NEXT_SOURCE_STATE_PREPARING = 1;
+ private static final int NEXT_SOURCE_STATE_PREPARED = 2;
+
private final static String TAG = "MediaPlayer2Impl";
private Context mContext;
- private long mNativeContext; // accessed by native methods
+ private long mNativeContext; // accessed by native methods
private long mNativeSurfaceTexture; // accessed by native methods
- private int mListenerContext; // accessed by native methods
+ private int mListenerContext; // accessed by native methods
private SurfaceHolder mSurfaceHolder;
private PowerManager.WakeLock mWakeLock = null;
private boolean mScreenOnWhilePlaying;
private boolean mStayAwake;
- private int mStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
private final Object mSrcLock = new Object();
//--- guarded by |mSrcLock| start
@@ -134,7 +136,7 @@
/**
* Default constructor.
- * <p>When done with the MediaPlayer2Impl, you should call {@link #close()},
+ * <p>When done with the MediaPlayer2Impl, you should call {@link #close()},
* to free the resources. If not released, too many MediaPlayer2Impl instances may
* result in an exception.</p>
*/
@@ -152,45 +154,11 @@
}
@Override
- public MediaPlayerBase getMediaPlayerBase() {
- return null;
- }
-
- /**
- * Releases the resources held by this {@code MediaPlayer2} object.
- *
- * It is considered good practice to call this method when you're
- * done using the MediaPlayer2. In particular, whenever an Activity
- * of an application is paused (its onPause() method is called),
- * or stopped (its onStop() method is called), this method should be
- * invoked to release the MediaPlayer2 object, unless the application
- * has a special need to keep the object around. In addition to
- * unnecessary resources (such as memory and instances of codecs)
- * being held, failure to call this method immediately if a
- * MediaPlayer2 object is no longer needed may also lead to
- * continuous battery consumption for mobile devices, and playback
- * failure for other applications if no multiple instances of the
- * same codec are supported on a device. Even if multiple instances
- * of the same codec are supported, some performance degradation
- * may be expected when unnecessary multiple instances are used
- * at the same time.
- *
- * {@code close()} may be safely called after a prior {@code close()}.
- * This class implements the Java {@code AutoCloseable} interface and
- * may be used with try-with-resources.
- */
- @Override
public void close() {
super.close();
release();
}
- /**
- * Starts or resumes playback. If playback had previously been paused,
- * playback will continue from where it was paused. If playback had
- * been stopped, or never started before, playback will start at the
- * beginning.
- */
@Override
public Object play() {
return addTask(new Task(CALL_COMPLETED_PLAY, false) {
@@ -204,14 +172,6 @@
private native void _start() throws IllegalStateException;
- /**
- * Prepares the player for playback, asynchronously.
- *
- * After setting the datasource and the display surface, you need to either
- * call prepare(). For streams, you should call prepare(),
- * which returns immediately, rather than blocking until enough data has been
- * buffered.
- */
@Override
public Object prepare() {
return addTask(new Task(CALL_COMPLETED_PREPARE, true) {
@@ -224,9 +184,6 @@
public native void _prepare();
- /**
- * Pauses playback. Call play() to resume.
- */
@Override
public Object pause() {
return addTask(new Task(CALL_COMPLETED_PAUSE, false) {
@@ -241,11 +198,6 @@
private native void _pause() throws IllegalStateException;
- /**
- * Tries to play next data source if applicable.
- *
- * @throws IllegalStateException if it is called in an invalid state
- */
@Override
public Object skipToNext() {
return addTask(new Task(CALL_COMPLETED_SKIP_TO_NEXT, false) {
@@ -259,32 +211,12 @@
});
}
- /**
- * Gets the current playback position.
- *
- * @return the current position in milliseconds
- */
@Override
public native long getCurrentPosition();
- /**
- * Gets the duration of the file.
- *
- * @return the duration in milliseconds, if no duration is available
- * (for example, if streaming live content), -1 is returned.
- */
@Override
public native long getDuration();
- /**
- * Gets the current buffered media source position received through progressive downloading.
- * The received buffering percentage indicates how much of the content has been buffered
- * or played. For example a buffering update of 80 percent when half the content
- * has already been played indicates that the next 30 percent of the
- * content to play has been buffered.
- *
- * @return the current buffered media source position in milliseconds
- */
@Override
public long getBufferedPosition() {
// Use cached buffered percent for now.
@@ -298,14 +230,6 @@
private native int native_getState();
- /**
- * Sets the audio attributes for this MediaPlayer2.
- * See {@link AudioAttributes} for how to build and configure an instance of this class.
- * You must call this method before {@link #prepare()} in order
- * for the audio attributes to become effective thereafter.
- * @param attributes a non-null set of audio attributes
- * @throws IllegalArgumentException if the attributes are null or invalid.
- */
@Override
public Object setAudioAttributes(@NonNull AudioAttributes attributes) {
return addTask(new Task(CALL_COMPLETED_SET_AUDIO_ATTRIBUTES, false) {
@@ -326,11 +250,6 @@
return attributes;
}
- /**
- * Sets the data source as described by a DataSourceDesc.
- *
- * @param dsd the descriptor of data source you want to play
- */
@Override
public Object setDataSource(@NonNull DataSourceDesc dsd) {
return addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
@@ -351,12 +270,6 @@
});
}
- /**
- * Sets a single data source as described by a DataSourceDesc which will be played
- * after current data source is finished.
- *
- * @param dsd the descriptor of data source you want to play after current one
- */
@Override
public Object setNextDataSource(@NonNull DataSourceDesc dsd) {
return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
@@ -374,11 +287,6 @@
});
}
- /**
- * Sets a list of data sources to be played sequentially after current data source is done.
- *
- * @param dsds the list of data sources you want to play after current one
- */
@Override
public Object setNextDataSources(@NonNull List<DataSourceDesc> dsds) {
return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCES, false) {
@@ -428,16 +336,11 @@
}
}
- /**
- * Configures the player to loop on the current data source.
- * @param loop true if the current data source is meant to loop.
- */
@Override
public Object loopCurrent(boolean loop) {
return addTask(new Task(CALL_COMPLETED_LOOP_CURRENT, false) {
@Override
void process() {
- // TODO: set the looping mode, send notification
setLooping(loop);
}
});
@@ -445,57 +348,29 @@
private native void setLooping(boolean looping);
- /**
- * Sets the volume of the audio of the media to play, expressed as a linear multiplier
- * on the audio samples.
- * Note that this volume is specific to the player, and is separate from stream volume
- * used across the platform.<br>
- * A value of 0.0f indicates muting, a value of 1.0f is the nominal unattenuated and unamplified
- * gain. See {@link #getMaxPlayerVolume()} for the volume range supported by this player.
- * @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
- */
@Override
public Object setPlayerVolume(float volume) {
return addTask(new Task(CALL_COMPLETED_SET_PLAYER_VOLUME, false) {
@Override
void process() {
mVolume = volume;
- _setVolume(volume);
+ native_setVolume(volume);
}
});
}
- private native void _setVolume(float volume);
+ private native void native_setVolume(float volume);
- /**
- * Returns the current volume of this player to this player.
- * Note that it does not take into account the associated stream volume.
- * @return the player volume.
- */
@Override
public float getPlayerVolume() {
return mVolume;
}
- /**
- * @return the maximum volume that can be used in {@link #setPlayerVolume(float)}.
- */
@Override
public float getMaxPlayerVolume() {
return 1.0f;
}
- private static final int NEXT_SOURCE_STATE_ERROR = -1;
- private static final int NEXT_SOURCE_STATE_INIT = 0;
- private static final int NEXT_SOURCE_STATE_PREPARING = 1;
- private static final int NEXT_SOURCE_STATE_PREPARED = 2;
-
- /*
- * Update the MediaPlayer2Impl SurfaceTexture.
- * Call after setting a new display surface.
- */
- private native void _setVideoSurface(Surface surface);
-
/* Do not change these values (starting with INVOKE_ID) without updating
* their counterparts in include/media/mediaplayer2.h!
*/
@@ -504,7 +379,6 @@
private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3;
private static final int INVOKE_ID_SELECT_TRACK = 4;
private static final int INVOKE_ID_DESELECT_TRACK = 5;
- private static final int INVOKE_ID_SET_VIDEO_SCALE_MODE = 6;
private static final int INVOKE_ID_GET_SELECTED_TRACK = 7;
/**
@@ -518,7 +392,7 @@
* native player.
*/
private PlayerMessage invoke(PlayerMessage msg) {
- byte[] ret = _invoke(msg.toByteArray());
+ byte[] ret = native_invoke(msg.toByteArray());
if (ret == null) {
return null;
}
@@ -529,7 +403,7 @@
}
}
- private native byte[] _invoke(byte[] request);
+ private native byte[] native_invoke(byte[] request);
@Override
public Object notifyWhenCommandLabelReached(Object label) {
@@ -559,7 +433,7 @@
} else {
surface = null;
}
- _setVideoSurface(surface);
+ native_setVideoSurface(surface);
updateSurfaceScreenOn();
}
});
@@ -574,49 +448,13 @@
Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for Surface");
}
mSurfaceHolder = null;
- _setVideoSurface(surface);
+ native_setVideoSurface(surface);
updateSurfaceScreenOn();
}
});
}
- /**
- * Sets video scaling mode. To make the target video scaling mode
- * effective during playback, this method must be called after
- * data source is set. If not called, the default video
- * scaling mode is {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT}.
- *
- * <p> The supported video scaling modes are:
- * <ul>
- * <li> {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT}
- * <li> {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING}
- * </ul>
- *
- * @param mode target video scaling mode. Must be one of the supported
- * video scaling modes; otherwise, IllegalArgumentException will be thrown.
- *
- * @see MediaPlayer2#VIDEO_SCALING_MODE_SCALE_TO_FIT
- * @see MediaPlayer2#VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
- * @hide
- */
- @Override
- public Object setVideoScalingMode(int mode) {
- return addTask(new Task(CALL_COMPLETED_SET_VIDEO_SCALING_MODE, false) {
- @Override
- void process() {
- if (!isVideoScalingModeSupported(mode)) {
- final String msg = "Scaling mode " + mode + " is not supported";
- throw new IllegalArgumentException(msg);
- }
- PlayerMessage request = PlayerMessage.newBuilder()
- .addValues(Value.newBuilder()
- .setInt32Value(INVOKE_ID_SET_VIDEO_SCALE_MODE))
- .addValues(Value.newBuilder().setInt32Value(mode))
- .build();
- invoke(request);
- }
- });
- }
+ private native void native_setVideoSurface(Surface surface);
@Override
public boolean cancelCommand(Object token) {
@@ -632,26 +470,6 @@
}
}
- private Object addTask(Task task) {
- synchronized (mTaskLock) {
- mPendingTasks.add(task);
- processPendingTask_l();
- }
- return task;
- }
-
- @GuardedBy("mTaskLock")
- private void processPendingTask_l() {
- if (mCurrentTask != null) {
- return;
- }
- if (!mPendingTasks.isEmpty()) {
- Task task = mPendingTasks.remove(0);
- mCurrentTask = task;
- mTaskHandler.post(task);
- }
- }
-
private void handleDataSource(boolean isCurrent, @NonNull DataSourceDesc dsd, long srcId)
throws IOException {
checkArgument(dsd != null, "the DataSourceDesc cannot be null");
@@ -875,9 +693,7 @@
boolean isCurrent, long srcId, Media2DataSource dataSource,
long startPos, long endPos);
- /**
- * @return true if there is a next data source, false otherwise.
- */
+ // return true if there is a next data source, false otherwise.
// This function should be always called on |mHandlerThread|.
private boolean prepareNextDataSource() {
if (Looper.myLooper() != mHandlerThread.getLooper()) {
@@ -975,30 +791,11 @@
private native void nativePlayNextDataSource(long srcId);
-
- private int getAudioStreamType() {
- if (mStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
- mStreamType = _getAudioStreamType();
- }
- return mStreamType;
- }
-
- private native int _getAudioStreamType() throws IllegalStateException;
-
-
//--------------------------------------------------------------------------
// Explicit Routing
//--------------------
private AudioDeviceInfo mPreferredDevice = null;
- /**
- * Specifies an audio device (via an {@link AudioDeviceInfo} object) to route
- * the output from this MediaPlayer2.
- * @param deviceInfo The {@link AudioDeviceInfo} specifying the audio sink or source.
- * If deviceInfo is null, default routing is restored.
- * @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and
- * does not correspond to a valid audio device.
- */
@Override
public boolean setPreferredDevice(AudioDeviceInfo deviceInfo) {
if (deviceInfo != null && !deviceInfo.isSink()) {
@@ -1014,10 +811,6 @@
return status;
}
- /**
- * Returns the selected output specified by {@link #setPreferredDevice}. Note that this
- * is not guaranteed to correspond to the actual device being used for playback.
- */
@Override
public AudioDeviceInfo getPreferredDevice() {
synchronized (this) {
@@ -1025,12 +818,6 @@
}
}
- /**
- * Returns an {@link AudioDeviceInfo} identifying the current routing of this MediaPlayer2
- * Note: The query is only valid if the MediaPlayer2 is currently playing.
- * If the player is not playing, the returned device can be null or correspond to previously
- * selected device when the player was last active.
- */
@Override
public AudioDeviceInfo getRoutedDevice() {
int deviceId = native_getRoutedDeviceId();
@@ -1047,9 +834,7 @@
return null;
}
- /*
- * Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
- */
+ // Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
@GuardedBy("mRoutingChangeListeners")
private void enableNativeRoutingCallbacksLocked(boolean enabled) {
if (mRoutingChangeListeners.size() == 0) {
@@ -1057,23 +842,12 @@
}
}
- /**
- * The list of AudioRouting.OnRoutingChangedListener interfaces added (with
- * {@link #addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, Handler)}
- * by an app to receive (re)routing notifications.
- */
+ // The list of AudioRouting.OnRoutingChangedListener interfaces added with
+ // addOnRoutingChangedListener by an app to receive (re)routing notifications.
@GuardedBy("mRoutingChangeListeners")
private ArrayMap<AudioRouting.OnRoutingChangedListener,
NativeRoutingEventHandlerDelegate> mRoutingChangeListeners = new ArrayMap<>();
- /**
- * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing
- * changes on this MediaPlayer2.
- * @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive
- * notifications of rerouting events.
- * @param handler Specifies the {@link Handler} object for the thread on which to execute
- * the callback. If <code>null</code>, the handler on the main looper will be used.
- */
@Override
public void addOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener,
Handler handler) {
@@ -1087,12 +861,6 @@
}
}
- /**
- * Removes an {@link AudioRouting.OnRoutingChangedListener} which has been previously added
- * to receive rerouting notifications.
- * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface
- * to remove.
- */
@Override
public void removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener) {
synchronized (mRoutingChangeListeners) {
@@ -1107,70 +875,59 @@
private native final int native_getRoutedDeviceId();
private native final void native_enableDeviceCallback(boolean enabled);
- /**
- * Set the low-level power management behavior for this MediaPlayer2. This
- * can be used when the MediaPlayer2 is not playing through a SurfaceHolder
- * set with {@link #setDisplay(SurfaceHolder)} and thus can use the
- * high-level {@link #setScreenOnWhilePlaying(boolean)} feature.
- *
- * <p>This function has the MediaPlayer2 access the low-level power manager
- * service to control the device's power usage while playing is occurring.
- * The parameter is a combination of {@link android.os.PowerManager} wake flags.
- * Use of this method requires {@link android.Manifest.permission#WAKE_LOCK}
- * permission.
- * By default, no attempt is made to keep the device awake during playback.
- *
- * @param context the Context to use
- * @param mode the power/wake mode to set
- * @see android.os.PowerManager
- * @hide
- */
@Override
- public void setWakeMode(Context context, int mode) {
- boolean washeld = false;
+ public Object setWakeMode(Context context, int mode) {
+ return addTask(new Task(CALL_COMPLETED_SET_WAKE_MODE, false) {
+ @Override
+ void process() {
+ boolean washeld = false;
- /* Disable persistant wakelocks in media player based on property */
- if (SystemProperties.getBoolean("audio.offload.ignore_setawake", false) == true) {
- Log.w(TAG, "IGNORING setWakeMode " + mode);
- return;
- }
+ if (mWakeLock != null) {
+ if (mWakeLock.isHeld()) {
+ washeld = true;
+ mWakeLock.release();
+ }
+ mWakeLock = null;
+ }
- if (mWakeLock != null) {
- if (mWakeLock.isHeld()) {
- washeld = true;
- mWakeLock.release();
+ PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ ActivityManager am =
+ (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ List<RunningAppProcessInfo> runningAppsProcInfo = am.getRunningAppProcesses();
+ int pid = android.os.Process.myPid();
+ String name = "pid " + String.valueOf(pid);
+ if (runningAppsProcInfo != null) {
+ for (RunningAppProcessInfo procInfo : runningAppsProcInfo) {
+ if (procInfo.pid == pid) {
+ name = procInfo.processName;
+ break;
+ }
+ }
+ }
+ mWakeLock = pm.newWakeLock(mode | PowerManager.ON_AFTER_RELEASE, name);
+ mWakeLock.setReferenceCounted(false);
+ if (washeld) {
+ mWakeLock.acquire();
+ }
}
- mWakeLock = null;
- }
-
- PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(mode|PowerManager.ON_AFTER_RELEASE, MediaPlayer2Impl.class.getName());
- mWakeLock.setReferenceCounted(false);
- if (washeld) {
- mWakeLock.acquire();
- }
+ });
}
- /**
- * Control whether we should use the attached SurfaceHolder to keep the
- * screen on while video playback is occurring. This is the preferred
- * method over {@link #setWakeMode} where possible, since it doesn't
- * require that the application have permission for low-level wake lock
- * access.
- *
- * @param screenOn Supply true to keep the screen on, false to allow it
- * to turn off.
- * @hide
- */
@Override
- public void setScreenOnWhilePlaying(boolean screenOn) {
- if (mScreenOnWhilePlaying != screenOn) {
- if (screenOn && mSurfaceHolder == null) {
- Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective without a SurfaceHolder");
+ public Object setScreenOnWhilePlaying(boolean screenOn) {
+ return addTask(new Task(CALL_COMPLETED_SET_SCREEN_ON_WHILE_PLAYING, false) {
+ @Override
+ void process() {
+ if (mScreenOnWhilePlaying != screenOn) {
+ if (screenOn && mSurfaceHolder == null) {
+ Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective"
+ + " without a SurfaceHolder");
+ }
+ mScreenOnWhilePlaying = screenOn;
+ updateSurfaceScreenOn();
+ }
}
- mScreenOnWhilePlaying = screenOn;
- updateSurfaceScreenOn();
- }
+ });
}
private void stayAwake(boolean awake) {
@@ -1191,42 +948,12 @@
}
}
- /**
- * Returns the width of the video.
- *
- * @return the width of the video, or 0 if there is no video,
- * no display surface was set, or the width has not been determined
- * yet. The {@code EventCallback} can be registered via
- * {@link #setEventCallback(Executor, EventCallback)} to provide a
- * notification {@code EventCallback.onVideoSizeChanged} when the width
- * is available.
- */
@Override
public native int getVideoWidth();
- /**
- * Returns the height of the video.
- *
- * @return the height of the video, or 0 if there is no video,
- * no display surface was set, or the height has not been determined
- * yet. The {@code EventCallback} can be registered via
- * {@link #setEventCallback(Executor, EventCallback)} to provide a
- * notification {@code EventCallback.onVideoSizeChanged} when the height
- * is available.
- */
@Override
public native int getVideoHeight();
- /**
- * Return Metrics data about the current player.
- *
- * @return a {@link PersistableBundle} containing the set of attributes and values
- * available for the media being handled by this instance of MediaPlayer2
- * The attributes are descibed in {@link MetricsConstants}.
- *
- * Additional vendor-specific fields may also be present in
- * the return value.
- */
@Override
public PersistableBundle getMetrics() {
PersistableBundle bundle = native_getMetrics();
@@ -1235,27 +962,9 @@
private native PersistableBundle native_getMetrics();
- /**
- * Checks whether the MediaPlayer2 is playing.
- *
- * @return true if currently playing, false otherwise
- * @throws IllegalStateException if the internal player engine has not been
- * initialized or has been released.
- * @hide
- */
@Override
public native boolean isPlaying();
- /**
- * Gets the current buffering management params used by the source component.
- * Calling it only after {@code setDataSource} has been called.
- * Each type of data source might have different set of default params.
- *
- * @return the current buffering management params used by the source component.
- * @throws IllegalStateException if the internal player engine has not been
- * initialized, or {@code setDataSource} has not been called.
- * @hide
- */
@Override
@NonNull
public native BufferingParams getBufferingParams();
@@ -3388,13 +3097,6 @@
// Modular DRM end
- /*
- * Test whether a given video scaling mode is supported.
- */
- private boolean isVideoScalingModeSupported(int mode) {
- return (mode == VIDEO_SCALING_MODE_SCALE_TO_FIT ||
- mode == VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
- }
private static class TimedTextUtil {
// These keys must be in sync with the keys in TextDescription2.h
@@ -3450,6 +3152,26 @@
}
}
+ private Object addTask(Task task) {
+ synchronized (mTaskLock) {
+ mPendingTasks.add(task);
+ processPendingTask_l();
+ }
+ return task;
+ }
+
+ @GuardedBy("mTaskLock")
+ private void processPendingTask_l() {
+ if (mCurrentTask != null) {
+ return;
+ }
+ if (!mPendingTasks.isEmpty()) {
+ Task task = mPendingTasks.remove(0);
+ mCurrentTask = task;
+ mTaskHandler.post(task);
+ }
+ }
+
private abstract class Task implements Runnable {
private final int mMediaCallType;
private final boolean mNeedToWaitForEventToComplete;
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 7cf8828..8637ada 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -543,7 +543,7 @@
import jav.util.Iterator;
HashMap<k, v> hm;
- Set<Entry<k, v> > s = hm.entrySet();
+ Set<Entry<k, v>> s = hm.entrySet();
Iterator i = s.iterator();
Entry e = s.next();
*/
@@ -613,10 +613,10 @@
}
static jobject ListOfVectorsToArrayListOfByteArray(JNIEnv *env,
- List<Vector<uint8_t> > list) {
+ List<Vector<uint8_t>> list) {
jclass clazz = gFields.arraylistClassId;
jobject arrayList = env->NewObject(clazz, gFields.arraylist.init);
- List<Vector<uint8_t> >::iterator iter = list.begin();
+ List<Vector<uint8_t>>::iterator iter = list.begin();
while (iter != list.end()) {
jbyteArray byteArray = VectorToJByteArray(env, *iter);
env->CallBooleanMethod(arrayList, gFields.arraylist.add, byteArray);
@@ -1216,7 +1216,7 @@
return NULL;
}
- List<Vector<uint8_t> > secureStops;
+ List<Vector<uint8_t>> secureStops;
status_t err = drm->getSecureStops(secureStops);
@@ -1237,7 +1237,7 @@
return NULL;
}
- List<Vector<uint8_t> > secureStopIds;
+ List<Vector<uint8_t>> secureStopIds;
status_t err = drm->getSecureStopIds(secureStopIds);
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 61c28ed..f60e7da 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -893,20 +893,6 @@
process_media_player_call( env, thiz, mp->reset(), NULL, NULL );
}
-static jint
-android_media_MediaPlayer2_getAudioStreamType(JNIEnv *env, jobject thiz)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return 0;
- }
- audio_stream_type_t streamtype;
- process_media_player_call( env, thiz, mp->getAudioStreamType(&streamtype), NULL, NULL );
- ALOGV("getAudioStreamType: %d (streamtype)", streamtype);
- return (jint) streamtype;
-}
-
static jboolean
android_media_MediaPlayer2_setParameter(JNIEnv *env, jobject thiz, jint key, jobject)
{
@@ -1467,7 +1453,7 @@
(void *)android_media_MediaPlayer2_handleDataSourceCallback
},
{"nativePlayNextDataSource", "(J)V", (void *)android_media_MediaPlayer2_playNextDataSource},
- {"_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer2_setVideoSurface},
+ {"native_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer2_setVideoSurface},
{"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer2_getBufferingParams},
{"_setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer2_setBufferingParams},
{"_prepare", "()V", (void *)android_media_MediaPlayer2_prepare},
@@ -1487,13 +1473,12 @@
{"getDuration", "()J", (void *)android_media_MediaPlayer2_getDuration},
{"_release", "()V", (void *)android_media_MediaPlayer2_release},
{"_reset", "()V", (void *)android_media_MediaPlayer2_reset},
- {"_getAudioStreamType", "()I", (void *)android_media_MediaPlayer2_getAudioStreamType},
{"setParameter", "(ILjava/lang/Object;)Z", (void *)android_media_MediaPlayer2_setParameter},
{"getParameter", "(I)Ljava/lang/Object;", (void *)android_media_MediaPlayer2_getParameter},
{"setLooping", "(Z)V", (void *)android_media_MediaPlayer2_setLooping},
{"isLooping", "()Z", (void *)android_media_MediaPlayer2_isLooping},
- {"_setVolume", "(F)V", (void *)android_media_MediaPlayer2_setVolume},
- {"_invoke", "([B)[B", (void *)android_media_MediaPlayer2_invoke},
+ {"native_setVolume", "(F)V", (void *)android_media_MediaPlayer2_setVolume},
+ {"native_invoke", "([B)[B", (void *)android_media_MediaPlayer2_invoke},
{"native_init", "()V", (void *)android_media_MediaPlayer2_native_init},
{"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer2_native_setup},
{"native_finalize", "()V", (void *)android_media_MediaPlayer2_native_finalize},
diff --git a/packages/SimAppDialog/res/drawable/illo_sim_app_dialog.xml b/packages/SimAppDialog/res/drawable/illo_sim_app_dialog.xml
new file mode 100644
index 0000000..8dd88b4
--- /dev/null
+++ b/packages/SimAppDialog/res/drawable/illo_sim_app_dialog.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 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.
+-->
+<!-- Empty drawable as this is not displayed by default. Must be provided by resource overlay. -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" />
diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
index 5bcce4d..12f9bb6 100644
--- a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
+++ b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
@@ -37,6 +37,22 @@
android:text="@string/install_carrier_app_description_default"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
- </LinearLayout>
+
+ <com.android.setupwizardlib.view.FillContentLayout
+ android:id="@+id/illo_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:visibility="gone">
+
+ <ImageView
+ android:src="@drawable/illo_sim_app_dialog"
+ style="@style/SuwContentIllustration"
+ android:contentDescription="@string/install_carrier_app_image_content_description"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ </com.android.setupwizardlib.view.FillContentLayout>
+</LinearLayout>
</com.android.setupwizardlib.GlifLayout>
diff --git a/packages/SimAppDialog/res/values/bools.xml b/packages/SimAppDialog/res/values/bools.xml
new file mode 100644
index 0000000..4953d5e
--- /dev/null
+++ b/packages/SimAppDialog/res/values/bools.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!--
+ Whether to show an illustration on the screen asking the user to download the carrier app.
+ May be set to true in a resource overlay as long as a drawable asset with ID
+ illo_sim_app_dialog is provided, along with a content description for accessibility with
+ string ID install_carrier_app_image_content_description.
+ -->
+ <bool name="show_sim_app_dialog_illo">false</bool>
+</resources>
diff --git a/packages/SimAppDialog/res/values/strings.xml b/packages/SimAppDialog/res/values/strings.xml
index 87941cb..9e8359c 100644
--- a/packages/SimAppDialog/res/values/strings.xml
+++ b/packages/SimAppDialog/res/values/strings.xml
@@ -32,4 +32,6 @@
<string name="install_carrier_app_defer_action">Not now</string>
<!-- Name of the button for downloading the carrier app [CHAR LIMIT=25] -->
<string name="install_carrier_app_download_action">Download app</string>
-</resources>
\ No newline at end of file
+ <!-- Empty placeholder string for an illustration content description that is supplied via resource overlay. [DO NOT TRANSLATE] -->
+ <string name="install_carrier_app_image_content_description" translatable="false" />
+</resources>
diff --git a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
index 9e9b80d..8e8d9f7 100644
--- a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
+++ b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
@@ -65,6 +65,11 @@
Button downloadButton = findViewById(R.id.download_button);
downloadButton.setOnClickListener(this);
+ // Show/hide illo depending on whether one was provided in a resource overlay
+ boolean showIllo = getResources().getBoolean(R.bool.show_sim_app_dialog_illo);
+ View illoContainer = findViewById(R.id.illo_container);
+ illoContainer.setVisibility(showIllo ? View.VISIBLE : View.GONE);
+
// Include carrier name in description text if its present in the intent
Intent intent = getIntent();
if (intent != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 003f158d..c47d408 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -1107,7 +1107,9 @@
mIsClipped = clipped;
}
- if (mAmbientState.isDarkAtAll()) {
+ if (mPulsing) {
+ setClipBounds(null);
+ } else if (mAmbientState.isDarkAtAll()) {
setClipBounds(mBackgroundAnimationRect);
} else if (clipped) {
setClipBounds(mRequestedClipBounds);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index b5f67c0..098fa62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -15,22 +15,13 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.mockitoSession;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -38,49 +29,34 @@
import android.animation.Animator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
-import android.graphics.Rect;
import android.os.Handler;
-import android.os.IPowerManager;
-import android.os.Looper;
-import android.os.PowerManager;
import android.service.notification.StatusBarNotification;
-import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import android.testing.TestableLooper.RunWithLooper;
+import android.testing.UiThreadTest;
import android.view.MotionEvent;
-import android.view.VelocityTracker;
import android.view.View;
-import android.view.MotionEvent;
import com.android.systemui.SwipeHelper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationMenuRow;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoSession;
-import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.mockito.stubbing.Answer;
-import java.util.ArrayList;
-
/**
* Tests for {@link NotificationSwipeHelper}.
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
+@UiThreadTest
public class NotificationSwipeHelperTest extends SysuiTestCase {
private NotificationSwipeHelper mSwipeHelper;
@@ -96,7 +72,6 @@
@Rule public MockitoRule mockito = MockitoJUnit.rule();
@Before
- @UiThreadTest
public void setUp() throws Exception {
mCallback = mock(NotificationSwipeHelper.NotificationCallback.class);
mListener = mock(NotificationMenuRowPlugin.OnMenuEventListener.class);
diff --git a/services/autofill/java/com/android/server/intelligence/ContentCaptureSession.java b/services/autofill/java/com/android/server/intelligence/ContentCaptureSession.java
new file mode 100644
index 0000000..9cab1ed
--- /dev/null
+++ b/services/autofill/java/com/android/server/intelligence/ContentCaptureSession.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.intelligence;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.IBinder;
+import android.service.intelligence.IntelligenceService;
+import android.service.intelligence.InteractionContext;
+import android.service.intelligence.InteractionSessionId;
+import android.util.Slog;
+import android.view.intelligence.ContentCaptureEvent;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+import com.android.server.AbstractRemoteService;
+import com.android.server.intelligence.RemoteIntelligenceService.RemoteIntelligenceServiceCallbacks;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+final class ContentCaptureSession implements RemoteIntelligenceServiceCallbacks {
+
+ private static final String TAG = "ContentCaptureSession";
+
+ private final Object mLock;
+ private final IBinder mActivityToken;
+
+ private final IntelligencePerUserService mService;
+ private final RemoteIntelligenceService mRemoteService;
+ private final InteractionContext mInterationContext;
+ private final InteractionSessionId mId;
+
+ ContentCaptureSession(@NonNull Context context, int userId, @NonNull Object lock,
+ @NonNull IBinder activityToken, @NonNull IntelligencePerUserService service,
+ @NonNull ComponentName serviceComponentName, @NonNull ComponentName appComponentName,
+ int taskId, int displayId, @NonNull InteractionSessionId sessionId, int flags,
+ boolean bindInstantServiceAllowed, boolean verbose) {
+ mLock = lock;
+ mActivityToken = activityToken;
+ mService = service;
+ mId = Preconditions.checkNotNull(sessionId);
+ mRemoteService = new RemoteIntelligenceService(context,
+ IntelligenceService.SERVICE_INTERFACE, serviceComponentName, userId, this,
+ bindInstantServiceAllowed, verbose);
+ mInterationContext = new InteractionContext(appComponentName, taskId, displayId, flags);
+ }
+
+ /**
+ * Notifies the {@link IntelligenceService} that the service started.
+ */
+ @GuardedBy("mLock")
+ public void notifySessionStartedLocked() {
+ mRemoteService.onSessionLifecycleRequest(mInterationContext, mId);
+ }
+
+ /**
+ * Notifies the {@link IntelligenceService} of a batch of events.
+ */
+ public void sendEventsLocked(List<ContentCaptureEvent> events) {
+ mRemoteService.onContentCaptureEventsRequest(mId, events);
+ }
+
+ /**
+ * Cleans up the session and remove itself from the service.
+ *
+ * @param notifyRemoteService whether it should trigger a {@link
+ * IntelligenceService#onDestroyInteractionSession(InteractionSessionId)}
+ * request.
+ */
+ @GuardedBy("mLock")
+ public void removeSelfLocked(boolean notifyRemoteService) {
+ try {
+ if (notifyRemoteService) {
+ mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId);
+ }
+ } finally {
+ mService.removeSessionLocked(mId);
+ }
+ }
+
+ @Override // from RemoteScreenObservationServiceCallbacks
+ public void onServiceDied(AbstractRemoteService service) {
+ // TODO(b/111276913): implement (remove session from PerUserSession?)
+ if (mService.isDebug()) {
+ Slog.d(TAG, "onServiceDied() for " + mId);
+ }
+ synchronized (mLock) {
+ removeSelfLocked(/* notifyRemoteService= */ false);
+ }
+ }
+
+ @Override // from RemoteScreenObservationServiceCallbacks
+ public void onFailureOrTimeout(boolean timedOut) {
+ // TODO(b/111276913): log metrics on whether timed out or not
+ if (mService.isDebug()) {
+ Slog.d(TAG, "onFailureOrTimeout(" + mId + "): timed out=" + timedOut);
+ }
+ synchronized (mLock) {
+ removeSelfLocked(/* notifyRemoteService= */ false);
+ }
+ }
+
+ @GuardedBy("mLock")
+ public void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
+ pw.print(prefix); pw.print("id: "); mId.dump(pw); pw.println();
+ pw.print(prefix); pw.print("context: "); mInterationContext.dump(pw); pw.println();
+ pw.print(prefix); pw.print("activity token: "); pw.println(mActivityToken);
+ }
+
+ @Override
+ public String toString() {
+ return "ContentCaptureSession[id=" + mId.getValue() + ", act=" + mActivityToken + "]";
+ }
+}
diff --git a/services/autofill/java/com/android/server/intelligence/IntelligenceManagerService.java b/services/autofill/java/com/android/server/intelligence/IntelligenceManagerService.java
new file mode 100644
index 0000000..43d4a44
--- /dev/null
+++ b/services/autofill/java/com/android/server/intelligence/IntelligenceManagerService.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.intelligence;
+
+import static android.content.Context.INTELLIGENCE_MANAGER_SERVICE;
+
+import android.annotation.NonNull;
+import android.app.ActivityManagerInternal;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.UserManager;
+import android.service.intelligence.InteractionSessionId;
+import android.view.intelligence.ContentCaptureEvent;
+import android.view.intelligence.IIntelligenceManager;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
+import com.android.server.AbstractMasterSystemService;
+import com.android.server.LocalServices;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * A service used to observe the contents of the screen.
+ *
+ * <p>The data collected by this service can be analyzed and combined with other sources to provide
+ * contextual data in other areas of the system such as Autofill.
+ */
+public final class IntelligenceManagerService
+ extends AbstractMasterSystemService<IntelligencePerUserService> {
+
+ private static final String TAG = "IntelligenceManagerService";
+
+ @GuardedBy("mLock")
+ private ActivityManagerInternal mAm;
+
+ public IntelligenceManagerService(Context context) {
+ super(context, UserManager.DISALLOW_INTELLIGENCE_CAPTURE);
+ }
+
+ @Override // from MasterSystemService
+ protected String getServiceSettingsProperty() {
+ // TODO(b/111276913): STOPSHIP temporary settings, until it's set by resourcs + cmd
+ return "intel_service";
+ }
+
+ @Override // from MasterSystemService
+ protected IntelligencePerUserService newServiceLocked(int resolvedUserId,
+ boolean disabled) {
+ return new IntelligencePerUserService(this, mLock, resolvedUserId);
+ }
+
+ @Override // from SystemService
+ public void onStart() {
+ publishBinderService(INTELLIGENCE_MANAGER_SERVICE,
+ new IntelligenceManagerServiceStub());
+ }
+
+ private ActivityManagerInternal getAmInternal() {
+ synchronized (mLock) {
+ if (mAm == null) {
+ mAm = LocalServices.getService(ActivityManagerInternal.class);
+ }
+ }
+ return mAm;
+ }
+
+ final class IntelligenceManagerServiceStub extends IIntelligenceManager.Stub {
+
+ @Override
+ public void startSession(int userId, @NonNull IBinder activityToken,
+ @NonNull ComponentName componentName, @NonNull InteractionSessionId sessionId,
+ int flags, @NonNull IResultReceiver result) {
+ Preconditions.checkNotNull(activityToken);
+ Preconditions.checkNotNull(componentName);
+ Preconditions.checkNotNull(sessionId);
+
+ // TODO(b/111276913): refactor getTaskIdForActivity() to also return ComponentName,
+ // so we don't pass it on startSession (same for Autofill)
+ final int taskId = getAmInternal().getTaskIdForActivity(activityToken, false);
+
+ // TODO(b/111276913): get from AM as well
+ final int displayId = 0;
+
+ synchronized (mLock) {
+ final IntelligencePerUserService service = getServiceForUserLocked(userId);
+ service.startSessionLocked(activityToken, componentName, taskId, displayId,
+ sessionId, flags, result);
+ }
+ }
+
+ @Override
+ public void sendEvents(int userId, @NonNull InteractionSessionId sessionId,
+ @NonNull List<ContentCaptureEvent> events) {
+ Preconditions.checkNotNull(sessionId);
+ Preconditions.checkNotNull(events);
+
+ synchronized (mLock) {
+ final IntelligencePerUserService service = getServiceForUserLocked(userId);
+ service.sendEventsLocked(sessionId, events);
+ }
+ }
+
+ @Override
+ public void finishSession(int userId, @NonNull InteractionSessionId sessionId) {
+ Preconditions.checkNotNull(sessionId);
+
+ synchronized (mLock) {
+ final IntelligencePerUserService service = getServiceForUserLocked(userId);
+ service.finishSessionLocked(sessionId);
+ }
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
+
+ synchronized (mLock) {
+ dumpLocked("", pw);
+ }
+ }
+ }
+}
diff --git a/services/autofill/java/com/android/server/intelligence/IntelligencePerUserService.java b/services/autofill/java/com/android/server/intelligence/IntelligencePerUserService.java
new file mode 100644
index 0000000..584b872
--- /dev/null
+++ b/services/autofill/java/com/android/server/intelligence/IntelligencePerUserService.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.intelligence;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.service.intelligence.InteractionSessionId;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.view.intelligence.ContentCaptureEvent;
+import android.view.intelligence.IntelligenceManager;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.IResultReceiver;
+import com.android.server.AbstractPerUserSystemService;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Per-user instance of {@link IntelligenceManagerService}.
+ */
+final class IntelligencePerUserService
+ extends AbstractPerUserSystemService<IntelligencePerUserService> {
+
+ private static final String TAG = "IntelligencePerUserService";
+
+ @GuardedBy("mLock")
+ private final ArrayMap<InteractionSessionId, ContentCaptureSession> mSessions =
+ new ArrayMap<>();
+
+ // TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's
+
+ protected IntelligencePerUserService(
+ IntelligenceManagerService master, Object lock, int userId) {
+ super(master, lock, userId);
+ }
+
+ @Override // from PerUserSystemService
+ protected ServiceInfo newServiceInfo(@NonNull ComponentName serviceComponent)
+ throws NameNotFoundException {
+
+ ServiceInfo si;
+ try {
+ // TODO(b/111276913): must check that either the service is from a system component,
+ // or it matches a service set by shell cmd (so it can be used on CTS tests and when
+ // OEMs are implementing the real service
+ si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+ PackageManager.GET_META_DATA, mUserId);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could not get service for " + serviceComponent + ": " + e);
+ return null;
+ }
+ if (!Manifest.permission.BIND_INTELLIGENCE_SERVICE.equals(si.permission)) {
+ Slog.w(TAG, "IntelligenceService from '" + si.packageName
+ + "' does not require permission "
+ + Manifest.permission.BIND_INTELLIGENCE_SERVICE);
+ throw new SecurityException("Service does not require permission "
+ + Manifest.permission.BIND_INTELLIGENCE_SERVICE);
+ }
+ return si;
+ }
+
+ // TODO(b/111276913): log metrics
+ @GuardedBy("mLock")
+ public void startSessionLocked(@NonNull IBinder activityToken,
+ @NonNull ComponentName componentName, int taskId, int displayId,
+ @NonNull InteractionSessionId sessionId, int flags,
+ @NonNull IResultReceiver resultReceiver) {
+ final ComponentName serviceComponentName = getServiceComponentName();
+ if (serviceComponentName == null) {
+ // TODO(b/111276913): this happens when the system service is starting, we should
+ // probably handle it in a more elegant way (like waiting for boot_complete or
+ // something like that
+ Slog.w(TAG, "startSession(" + activityToken + "): hold your horses");
+ return;
+ }
+
+ ContentCaptureSession session = mSessions.get(sessionId);
+ if (session != null) {
+ if (mMaster.debug) {
+ Slog.d(TAG, "startSession(): reusing session " + sessionId + " for "
+ + componentName);
+ }
+ // TODO(b/111276913): check if local ids match and decide what to do if they don't
+ // TODO(b/111276913): should we call session.notifySessionStartedLocked() again??
+ // if not, move notifySessionStartedLocked() into session constructor
+ sendToClient(resultReceiver, IntelligenceManager.STATE_ACTIVE);
+ return;
+ }
+
+ // TODO(b/117779333): get from mMaster once it's moved to superclass
+ final boolean bindInstantServiceAllowed = false;
+
+ session = new ContentCaptureSession(getContext(), mUserId, mLock, activityToken,
+ this, serviceComponentName, componentName, taskId, displayId, sessionId, flags,
+ bindInstantServiceAllowed, mMaster.verbose);
+ if (mMaster.verbose) {
+ Slog.v(TAG, "startSession(): new session for " + componentName + " and id "
+ + sessionId);
+ }
+ mSessions.put(sessionId, session);
+ session.notifySessionStartedLocked();
+ sendToClient(resultReceiver, IntelligenceManager.STATE_ACTIVE);
+ }
+
+ // TODO(b/111276913): log metrics
+ @GuardedBy("mLock")
+ public void finishSessionLocked(@NonNull InteractionSessionId sessionId) {
+ final ContentCaptureSession session = mSessions.get(sessionId);
+ if (session == null) {
+ Slog.w(TAG, "finishSession(): no session with id" + sessionId);
+ return;
+ }
+ if (mMaster.verbose) {
+ Slog.v(TAG, "finishSession(): " + session);
+ }
+ session.removeSelfLocked(true);
+ }
+
+ @GuardedBy("mLock")
+ public void sendEventsLocked(@NonNull InteractionSessionId sessionId,
+ @NonNull List<ContentCaptureEvent> events) {
+ final ContentCaptureSession session = mSessions.get(sessionId);
+ if (session == null) {
+ Slog.w(TAG, "sendEvents(): no session for " + sessionId);
+ return;
+ }
+ if (mMaster.verbose) {
+ Slog.v(TAG, "sendEvents(): id=" + sessionId + "; events =" + events.size());
+ }
+ session.sendEventsLocked(events);
+ }
+
+ @GuardedBy("mLock")
+ public void removeSessionLocked(@NonNull InteractionSessionId sessionId) {
+ mSessions.remove(sessionId);
+ }
+
+ @Override
+ protected void dumpLocked(String prefix, PrintWriter pw) {
+ super.dumpLocked(prefix, pw);
+ if (mSessions.isEmpty()) {
+ pw.print(prefix); pw.println("no sessions");
+ } else {
+ final int size = mSessions.size();
+ pw.print(prefix); pw.print("number sessions: "); pw.println(size);
+ final String prefix2 = prefix + " ";
+ for (int i = 0; i < size; i++) {
+ pw.print(prefix); pw.print("session@"); pw.println(i);
+ final ContentCaptureSession session = mSessions.valueAt(i);
+ session.dumpLocked(prefix2, pw);
+ }
+ }
+ }
+
+ private static void sendToClient(@NonNull IResultReceiver resultReceiver, int value) {
+ try {
+ resultReceiver.send(value, null);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Error async reporting result to client: " + e);
+ }
+ }
+}
diff --git a/services/autofill/java/com/android/server/intelligence/RemoteIntelligenceService.java b/services/autofill/java/com/android/server/intelligence/RemoteIntelligenceService.java
new file mode 100644
index 0000000..9d241fb
--- /dev/null
+++ b/services/autofill/java/com/android/server/intelligence/RemoteIntelligenceService.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.intelligence;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.service.intelligence.IIntelligenceService;
+import android.service.intelligence.InteractionContext;
+import android.service.intelligence.InteractionSessionId;
+import android.text.format.DateUtils;
+import android.util.Slog;
+import android.view.intelligence.ContentCaptureEvent;
+
+import com.android.server.AbstractRemoteService;
+
+import java.util.List;
+
+final class RemoteIntelligenceService extends AbstractRemoteService {
+
+ private static final String TAG = "RemoteIntelligenceService";
+
+ private static final long TIMEOUT_IDLE_BIND_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
+ private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.MINUTE_IN_MILLIS;
+
+ private final RemoteIntelligenceServiceCallbacks mCallbacks;
+ private IIntelligenceService mService;
+
+ RemoteIntelligenceService(Context context, String serviceInterface,
+ ComponentName componentName, int userId,
+ RemoteIntelligenceServiceCallbacks callbacks, boolean bindInstantServiceAllowed,
+ boolean verbose) {
+ super(context, serviceInterface, componentName, userId, callbacks,
+ bindInstantServiceAllowed, verbose);
+ mCallbacks = callbacks;
+ }
+
+ @Override // from RemoteService
+ protected IInterface getServiceInterface(@NonNull IBinder service) {
+ mService = IIntelligenceService.Stub.asInterface(service);
+ return mService;
+ }
+
+ // TODO(b/111276913): modify super class to allow permanent binding when value is 0 or negative
+ @Override // from RemoteService
+ protected long getTimeoutIdleBindMillis() {
+ // TODO(b/111276913): read from Settings so it can be changed in the field
+ return TIMEOUT_IDLE_BIND_MILLIS;
+ }
+
+ @Override // from RemoteService
+ protected long getRemoteRequestMillis() {
+ // TODO(b/111276913): read from Settings so it can be changed in the field
+ return TIMEOUT_REMOTE_REQUEST_MILLIS;
+ }
+
+ /**
+ * Called by {@link ContentCaptureSession} to generate a call to the
+ * {@link RemoteIntelligenceService} to indicate the session was created (when {@code context}
+ * is not {@code null} or destroyed (when {@code context} is {@code null}).
+ */
+ public void onSessionLifecycleRequest(@Nullable InteractionContext context,
+ @NonNull InteractionSessionId sessionId) {
+ cancelScheduledUnbind();
+ scheduleRequest(new PendingSessionLifecycleRequest(this, context, sessionId));
+ }
+
+ /**
+ * Called by {@link ContentCaptureSession} to send a batch of events to the service.
+ */
+ public void onContentCaptureEventsRequest(@NonNull InteractionSessionId sessionId,
+ @NonNull List<ContentCaptureEvent> events) {
+ cancelScheduledUnbind();
+ scheduleRequest(new PendingOnContentCaptureEventsRequest(this, sessionId, events));
+ }
+
+
+ private abstract static class MyPendingRequest
+ extends PendingRequest<RemoteIntelligenceService> {
+ protected final InteractionSessionId mSessionId;
+
+ private MyPendingRequest(@NonNull RemoteIntelligenceService service,
+ @NonNull InteractionSessionId sessionId) {
+ super(service);
+ mSessionId = sessionId;
+ }
+
+ @Override // from PendingRequest
+ protected final void onTimeout(RemoteIntelligenceService remoteService) {
+ Slog.w(TAG, "timed out handling " + getClass().getSimpleName() + " for "
+ + mSessionId);
+ remoteService.mCallbacks.onFailureOrTimeout(/* timedOut= */ true);
+ }
+
+ @Override // from PendingRequest
+ public final void run() {
+ final RemoteIntelligenceService remoteService = getService();
+ if (remoteService != null) {
+ try {
+ myRun(remoteService);
+ // We don't expect the service to call us back, so we finish right away.
+ finish();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "exception handling " + getClass().getSimpleName() + " for "
+ + mSessionId + ": " + e);
+ remoteService.mCallbacks.onFailureOrTimeout(/* timedOut= */ false);
+ }
+ }
+ }
+
+ protected abstract void myRun(@NonNull RemoteIntelligenceService service)
+ throws RemoteException;
+
+ }
+
+ private static final class PendingSessionLifecycleRequest extends MyPendingRequest {
+
+ private final InteractionContext mContext;
+
+ protected PendingSessionLifecycleRequest(@NonNull RemoteIntelligenceService service,
+ @Nullable InteractionContext context, @NonNull InteractionSessionId sessionId) {
+ super(service, sessionId);
+ mContext = context;
+ }
+
+ @Override // from MyPendingRequest
+ public void myRun(@NonNull RemoteIntelligenceService remoteService) throws RemoteException {
+ remoteService.mService.onSessionLifecycle(mContext, mSessionId);
+ }
+ }
+
+ private static final class PendingOnContentCaptureEventsRequest extends MyPendingRequest {
+
+ private final List<ContentCaptureEvent> mEvents;
+
+ protected PendingOnContentCaptureEventsRequest(@NonNull RemoteIntelligenceService service,
+ @NonNull InteractionSessionId sessionId,
+ @NonNull List<ContentCaptureEvent> events) {
+ super(service, sessionId);
+ mEvents = events;
+ }
+
+ @Override // from MyPendingRequest
+ public void myRun(@NonNull RemoteIntelligenceService remoteService) throws RemoteException {
+ remoteService.mService.onContentCaptureEvents(mSessionId, mEvents);
+ }
+ }
+
+ public interface RemoteIntelligenceServiceCallbacks extends VultureCallback {
+ // To keep it simple, we use the same callback for all failures / timeouts.
+ void onFailureOrTimeout(boolean timedOut);
+ }
+}
diff --git a/services/core/java/com/android/server/AbstractRemoteService.java b/services/core/java/com/android/server/AbstractRemoteService.java
index 1d3a34c..181d7fd 100644
--- a/services/core/java/com/android/server/AbstractRemoteService.java
+++ b/services/core/java/com/android/server/AbstractRemoteService.java
@@ -366,11 +366,12 @@
mCompleted = true;
}
- Slog.w(mTag, "timed out");
final S remoteService = mWeakService.get();
if (remoteService != null) {
- Slog.w(mTag, " timed out after " + service.getRemoteRequestMillis() + " ms");
+ Slog.w(mTag, "timed out after " + service.getRemoteRequestMillis() + " ms");
onTimeout(remoteService);
+ } else {
+ Slog.w(mTag, "timed out (no service)");
}
};
mServiceHandler.postAtTime(mTimeoutTrigger,
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index aae7b95..9d1a301 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -40,7 +40,6 @@
import android.content.pm.ResolveInfo;
import android.media.RingtoneManager;
import android.net.Uri;
-import android.os.Binder;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
@@ -880,9 +879,8 @@
}
private String getDefaultSystemHandlerActivityPackage(Intent intent, int userId) {
- ResolveInfo handler = mServiceInternal.resolveIntent(intent,
- intent.resolveType(mContext.getContentResolver()), DEFAULT_INTENT_QUERY_FLAGS,
- userId, false, Binder.getCallingUid());
+ ResolveInfo handler = mContext.getPackageManager().resolveActivityAsUser(
+ intent, DEFAULT_INTENT_QUERY_FLAGS, userId);
if (handler == null || handler.activityInfo == null) {
return null;
}
@@ -899,8 +897,8 @@
private String getDefaultSystemHandlerServicePackage(
Intent intent, int userId) {
- List<ResolveInfo> handlers = mServiceInternal.queryIntentServices(
- intent, DEFAULT_INTENT_QUERY_FLAGS, Binder.getCallingUid(), userId);
+ List<ResolveInfo> handlers = mContext.getPackageManager().queryIntentServicesAsUser(
+ intent, DEFAULT_INTENT_QUERY_FLAGS, userId);
if (handlers == null) {
return null;
}
@@ -924,10 +922,8 @@
for (String syncAdapterPackageName : syncAdapterPackageNames) {
homeIntent.setPackage(syncAdapterPackageName);
- ResolveInfo homeActivity = mServiceInternal.resolveIntent(homeIntent,
- homeIntent.resolveType(mContext.getContentResolver()),
- DEFAULT_INTENT_QUERY_FLAGS,
- userId, false, Binder.getCallingUid());
+ ResolveInfo homeActivity = mContext.getPackageManager().resolveActivityAsUser(
+ homeIntent, DEFAULT_INTENT_QUERY_FLAGS, userId);
if (homeActivity != null) {
continue;
}
@@ -941,7 +937,7 @@
}
private String getDefaultProviderAuthorityPackage(String authority, int userId) {
- ProviderInfo provider = mServiceInternal.resolveContentProvider(
+ ProviderInfo provider = mContext.getPackageManager().resolveContentProviderAsUser(
authority, DEFAULT_INTENT_QUERY_FLAGS, userId);
if (provider != null) {
return provider.packageName;
@@ -980,8 +976,9 @@
continue;
}
- final int flags = mServiceInternal.getPermissionFlagsTEMP(
- permission, packageName, userId);
+ UserHandle user = UserHandle.of(userId);
+ final int flags = mContext.getPackageManager()
+ .getPermissionFlags(permission, packageName, user);
// We didn't get this through the default grant policy. Move along.
if ((flags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) == 0) {
@@ -997,7 +994,7 @@
if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0 && !systemFixed) {
continue;
}
- mServiceInternal.revokeRuntimePermission(packageName, permission, userId, false);
+ mContext.getPackageManager().revokeRuntimePermission(packageName, permission, user);
if (DEBUG) {
Log.i(TAG, "revoked " + (systemFixed ? "fixed " : "not fixed ")
@@ -1007,8 +1004,8 @@
// Remove the GRANTED_BY_DEFAULT flag without touching the others.
// Note that we do not revoke FLAG_PERMISSION_SYSTEM_FIXED. That bit remains
// sticky once set.
- mServiceInternal.updatePermissionFlagsTEMP(permission, packageName,
- PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, 0, userId);
+ mContext.getPackageManager().updatePermissionFlags(permission, packageName,
+ PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, 0, user);
}
}
@@ -1077,8 +1074,9 @@
}
if (permissions.contains(permission)) {
- final int flags = mServiceInternal.getPermissionFlagsTEMP(
- permission, pkg.packageName, userId);
+ UserHandle user = UserHandle.of(userId);
+ final int flags = mContext.getPackageManager().getPermissionFlags(
+ permission, pkg.packageName, user);
// If any flags are set to the permission, then it is either set in
// its current state by the system or device/profile owner or the user.
@@ -1094,8 +1092,8 @@
continue;
}
- mServiceInternal.grantRuntimePermission(
- pkg.packageName, permission, userId, false);
+ mContext.getPackageManager()
+ .grantRuntimePermission(pkg.packageName, permission, user);
if (DEBUG) {
Log.i(TAG, "Granted " + (systemFixed ? "fixed " : "not fixed ")
+ permission + " to default handler " + pkg);
@@ -1106,8 +1104,8 @@
newFlags |= PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
}
- mServiceInternal.updatePermissionFlagsTEMP(permission, pkg.packageName,
- newFlags, newFlags, userId);
+ mContext.getPackageManager().updatePermissionFlags(permission, pkg.packageName,
+ newFlags, newFlags, user);
}
// If a component gets a permission for being the default handler A
@@ -1119,8 +1117,8 @@
Log.i(TAG, "Granted not fixed " + permission + " to default handler "
+ pkg);
}
- mServiceInternal.updatePermissionFlagsTEMP(permission, pkg.packageName,
- PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, userId);
+ mContext.getPackageManager().updatePermissionFlags(permission, pkg.packageName,
+ PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, user);
}
}
}
@@ -1137,10 +1135,12 @@
private PackageInfo getPackageInfo(String pkg,
@PackageManager.PackageInfoFlags int extraFlags) {
- return mServiceInternal.getPackageInfo(pkg,
- DEFAULT_PACKAGE_INFO_QUERY_FLAGS | extraFlags,
- //TODO is this the right filterCallingUid?
- UserHandle.USER_SYSTEM, UserHandle.USER_SYSTEM);
+ try {
+ return mContext.getPackageManager().getPackageInfo(pkg,
+ DEFAULT_PACKAGE_INFO_QUERY_FLAGS | extraFlags);
+ } catch (NameNotFoundException e) {
+ return null;
+ }
}
private boolean isSysComponentOrPersistentPlatformSignedPrivApp(PackageInfo pkg) {
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
new file mode 100644
index 0000000..e3133ef
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Observe activity manager launch sequences.
+ *
+ * The activity manager can have at most 1 concurrent launch sequences. Calls to this interface
+ * are ordered by a happens-before relation for each defined state transition (see below).
+ *
+ * When a new launch sequence is made, that sequence is in the {@code INTENT_STARTED} state which
+ * is communicated by the {@link #onIntentStarted} callback. This is a transient state.
+ *
+ * The intent can fail to launch the activity, in which case the sequence's state transitions to
+ * {@code INTENT_FAILED} via {@link #onIntentFailed}. This is a terminal state.
+ *
+ * If an activity is successfully started, the launch sequence's state will transition into
+ * {@code STARTED} via {@link #onActivityLaunched}. This is a transient state.
+ *
+ * It must then transition to either {@code CANCELLED} with {@link #onActivityLaunchCancelled}
+ * or into {@code FINISHED} with {@link #onActivityLaunchFinished}. These are terminal states.
+ *
+ * Note that the {@link ActivityRecord} provided as a parameter to some state transitions isn't
+ * necessarily the same within a single launch sequence: it is only the top-most activity at the
+ * time (if any). Trampoline activities coalesce several activity starts into a single launch
+ * sequence.
+ *
+ * Upon reaching a terminal state, it is considered that there are no active launch sequences
+ * until a subsequent transition into {@code INTENT_STARTED} initiates a new launch sequence.
+ *
+ * <pre>
+ * ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐ ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐ ╔══════════════════════════╗
+ * ╴╴▶ ⋮ INTENT_STARTED ⋮ ──▶ ⋮ ACTIVITY_LAUNCHED ⋮ ──▶ ║ ACTIVITY_LAUNCH_FINISHED ║
+ * └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘ └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘ ╚══════════════════════════╝
+ * : :
+ * : :
+ * ▼ ▼
+ * ╔════════════════╗ ╔═══════════════════════════╗
+ * ║ INTENT_FAILED ║ ║ ACTIVITY_LAUNCH_CANCELLED ║
+ * ╚════════════════╝ ╚═══════════════════════════╝
+ * </pre>
+ */
+public interface ActivityMetricsLaunchObserver {
+ /**
+ * The 'temperature' at which a launch sequence had started.
+ *
+ * The lower the temperature the more work has to be done during start-up.
+ * A 'cold' temperature means that a new process has been started and likely
+ * nothing is cached.
+ *
+ * A hot temperature means the existing activity is brought to the foreground.
+ * It may need to regenerate some objects as a result of {@code onTrimMemory}.
+ *
+ * A warm temperature is in the middle; an existing process is used, but the activity
+ * has to be created from scratch with {@code #onCreate}.
+ *
+ * @see https://developer.android.com/topic/performance/vitals/launch-time
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ TEMPERATURE_COLD,
+ TEMPERATURE_WARM,
+ TEMPERATURE_HOT
+ })
+ @interface Temperature {}
+
+ /** Cold launch sequence: a new process has started. */
+ public static final int TEMPERATURE_COLD = 1;
+ /** Warm launch sequence: process reused, but activity has to be created. */
+ public static final int TEMPERATURE_WARM = 2;
+ /** Hot launch sequence: process reused, activity brought-to-top. */
+ public static final int TEMPERATURE_HOT = 3;
+
+ /**
+ * Notifies the observer that a new launch sequence has begun as a result of a new intent.
+ *
+ * Once a launch sequence begins, the resolved activity will either subsequently start with
+ * {@link #onActivityLaunched} or abort early (for example due to a resolution error or due to
+ * a security error) with {@link #onIntentFailed}.
+ *
+ * Multiple calls to this method cannot occur without first terminating the current
+ * launch sequence.
+ */
+ public void onIntentStarted(@NonNull Intent intent);
+
+ /**
+ * Notifies the observer that the current launch sequence has failed to launch an activity.
+ *
+ * This function call terminates the current launch sequence. The next method call, if any,
+ * must be {@link #onIntentStarted}.
+ *
+ * Examples of this happening:
+ * - Failure to resolve to an activity
+ * - Calling package did not have the security permissions to call the requested activity
+ * - Resolved activity was already running and only needed to be brought to the top
+ *
+ * Multiple calls to this method cannot occur without first terminating the current
+ * launch sequence.
+ */
+ public void onIntentFailed();
+
+ /**
+ * Notifies the observer that the current launch sequence had begun starting an activity.
+ *
+ * This is an intermediate state: once an activity begins starting, the entire launch sequence
+ * will later terminate by either finishing or cancelling.
+ *
+ * The initial activity is the first activity to be started as part of a launch sequence:
+ * it is represented by {@param activity} However, it isn't
+ * necessarily the activity which will be considered as displayed when the activity
+ * finishes launching (e.g. {@code activity} in {@link #onActivityLaunchFinished}).
+ *
+ * Multiple calls to this method cannot occur without first terminating the current
+ * launch sequence.
+ */
+ public void onActivityLaunched(@NonNull ActivityRecord activity,
+ @Temperature int temperature);
+
+ /**
+ * Notifies the observer that the current launch sequence has been aborted.
+ *
+ * This function call terminates the current launch sequence. The next method call, if any,
+ * must be {@link #onIntentStarted}.
+ *
+ * This can happen for many reasons, for example the user switches away to another app
+ * prior to the launch sequence completing, or the application being killed.
+ *
+ * Multiple calls to this method cannot occur without first terminating the current
+ * launch sequence.
+ *
+ * @param abortingActivity the last activity that had the top-most window during abort
+ * (this can be {@code null} in rare situations its unknown).
+ *
+ * @apiNote The aborting activity isn't necessarily the same as the starting activity;
+ * in the case of a trampoline, multiple activities could've been started
+ * and only the latest activity is reported here.
+ */
+ public void onActivityLaunchCancelled(@Nullable ActivityRecord abortingActivity);
+
+ /**
+ * Notifies the observer that the current launch sequence has been successfully finished.
+ *
+ * This function call terminates the current launch sequence. The next method call, if any,
+ * must be {@link #onIntentStarted}.
+ *
+ * A launch sequence is considered to be successfully finished when a frame is fully
+ * drawn for the first time: the top-most activity at the time is what's reported here.
+ *
+ * @param finalActivity the top-most activity whose windows were first to fully draw
+ *
+ * Multiple calls to this method cannot occur without first terminating the current
+ * launch sequence.
+ *
+ * @apiNote The finishing activity isn't necessarily the same as the starting activity;
+ * in the case of a trampoline, multiple activities could've been started
+ * and only the latest activity that was top-most during first-frame drawn
+ * is reported here.
+ */
+ public void onActivityLaunchFinished(@NonNull ActivityRecord finalActivity);
+}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 8bde7dd..9b01dfd 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -80,6 +80,7 @@
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT;
+import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -141,15 +142,21 @@
private final Context mContext;
private final MetricsLogger mMetricsLogger = new MetricsLogger();
+ // set to INVALID_START_TIME in reset.
+ // set to valid value in notifyActivityLaunching
private long mCurrentTransitionStartTime = INVALID_START_TIME;
private long mLastTransitionStartTime = INVALID_START_TIME;
private int mCurrentTransitionDeviceUptime;
private int mCurrentTransitionDelayMs;
+
+ /** If the any app transitions have been logged as starting, after the latest reset. */
private boolean mLoggedTransitionStarting;
+ /** Map : @WindowingMode int => WindowingModeTransitionInfo */
private final SparseArray<WindowingModeTransitionInfo> mWindowingModeTransitionInfo =
new SparseArray<>();
+ /** Map : @WindowingMode int => WindowingModeTransitionInfo */
private final SparseArray<WindowingModeTransitionInfo> mLastWindowingModeTransitionInfo =
new SparseArray<>();
private final H mHandler;
@@ -157,6 +164,12 @@
private ArtManagerInternal mArtManagerInternal;
private final StringBuilder mStringBuilder = new StringBuilder();
+ /**
+ * Due to the global single concurrent launch sequence, all calls to this observer must be made
+ * in-order on the same thread to fulfill the "happens-before" guarantee in LaunchObserver.
+ */
+ private final ActivityMetricsLaunchObserver mLaunchObserver = null;
+
private final class H extends Handler {
public H(Looper looper) {
@@ -175,6 +188,7 @@
}
private final class WindowingModeTransitionInfo {
+ /** The latest activity to have been launched. */
private ActivityRecord launchedActivity;
private int startResult;
private boolean currentTransitionProcessRunning;
@@ -273,7 +287,7 @@
return;
}
- int windowingMode = stack.getWindowingMode();
+ @WindowingMode int windowingMode = stack.getWindowingMode();
if (windowingMode == WINDOWING_MODE_PINNED) {
stack = mSupervisor.findStackBehind(stack);
windowingMode = stack.getWindowingMode();
@@ -301,11 +315,19 @@
* Notifies the tracker at the earliest possible point when we are starting to launch an
* activity.
*/
- void notifyActivityLaunching() {
+ void notifyActivityLaunching(Intent intent) {
+ if (DEBUG_METRICS) {
+ Slog.i(TAG, String.format("notifyActivityLaunching: active:%b, intent:%s",
+ isAnyTransitionActive(),
+ intent));
+ }
+
if (!isAnyTransitionActive()) {
- if (DEBUG_METRICS) Slog.i(TAG, "notifyActivityLaunching");
+
mCurrentTransitionStartTime = SystemClock.uptimeMillis();
mLastTransitionStartTime = mCurrentTransitionStartTime;
+
+ launchObserverNotifyIntentStarted(intent);
}
}
@@ -350,7 +372,9 @@
+ " processRunning=" + processRunning
+ " processSwitch=" + processSwitch);
- final int windowingMode = launchedActivity != null
+ // If we are already in an existing transition, only update the activity name, but not the
+ // other attributes.
+ final @WindowingMode int windowingMode = launchedActivity != null
? launchedActivity.getWindowingMode()
: WINDOWING_MODE_UNDEFINED;
final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
@@ -361,13 +385,15 @@
if (launchedActivity != null && launchedActivity.nowVisible) {
// Launched activity is already visible. We cannot measure windows drawn delay.
- reset(true /* abort */, info);
+ reset(true /* abort */, info, "launched activity already visible");
return;
}
if (launchedActivity != null && info != null) {
// If we are already in an existing transition, only update the activity name, but not
// the other attributes.
+
+ // Coalesce multiple (trampoline) activities from a single sequence together.
info.launchedActivity = launchedActivity;
return;
}
@@ -377,7 +403,7 @@
if ((!isLoggableResultCode(resultCode) || launchedActivity == null || !processSwitch
|| windowingMode == WINDOWING_MODE_UNDEFINED) && !otherWindowModesLaunching) {
// Failed to launch or it was not a process switch, so we don't care about the timing.
- reset(true /* abort */, info);
+ reset(true /* abort */, info, "failed to launch or not a process switch");
return;
} else if (otherWindowModesLaunching) {
// Don't log this windowing mode but continue with the other windowing modes.
@@ -386,6 +412,8 @@
if (DEBUG_METRICS) Slog.i(TAG, "notifyActivityLaunched successful");
+ // A new launch sequence [with the windowingMode] has begun.
+ // Start tracking it.
final WindowingModeTransitionInfo newInfo = new WindowingModeTransitionInfo();
newInfo.launchedActivity = launchedActivity;
newInfo.currentTransitionProcessRunning = processRunning;
@@ -394,6 +422,7 @@
mLastWindowingModeTransitionInfo.put(windowingMode, newInfo);
mCurrentTransitionDeviceUptime = (int) (SystemClock.uptimeMillis() / 1000);
startTraces(newInfo);
+ launchObserverNotifyActivityLaunched(newInfo);
}
/**
@@ -407,7 +436,8 @@
/**
* Notifies the tracker that all windows of the app have been drawn.
*/
- WindowingModeTransitionInfoSnapshot notifyWindowsDrawn(int windowingMode, long timestamp) {
+ WindowingModeTransitionInfoSnapshot notifyWindowsDrawn(@WindowingMode int windowingMode,
+ long timestamp) {
if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn windowingMode=" + windowingMode);
final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
@@ -419,7 +449,7 @@
final WindowingModeTransitionInfoSnapshot infoSnapshot =
new WindowingModeTransitionInfoSnapshot(info);
if (allWindowsDrawn() && mLoggedTransitionStarting) {
- reset(false /* abort */, info);
+ reset(false /* abort */, info, "notifyWindowsDrawn - all windows drawn");
}
return infoSnapshot;
}
@@ -427,7 +457,7 @@
/**
* Notifies the tracker that the starting window was drawn.
*/
- void notifyStartingWindowDrawn(int windowingMode, long timestamp) {
+ void notifyStartingWindowDrawn(@WindowingMode int windowingMode, long timestamp) {
final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
if (info == null || info.loggedStartingWindowDrawn) {
return;
@@ -444,22 +474,28 @@
*/
void notifyTransitionStarting(SparseIntArray windowingModeToReason, long timestamp) {
if (!isAnyTransitionActive() || mLoggedTransitionStarting) {
+ // Ignore calls to this made after a reset and prior to notifyActivityLaunching.
+
+ // Ignore any subsequent notifyTransitionStarting until the next reset.
return;
}
if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting");
mCurrentTransitionDelayMs = calculateDelay(timestamp);
mLoggedTransitionStarting = true;
+
+ WindowingModeTransitionInfo foundInfo = null;
for (int index = windowingModeToReason.size() - 1; index >= 0; index--) {
- final int windowingMode = windowingModeToReason.keyAt(index);
+ final @WindowingMode int windowingMode = windowingModeToReason.keyAt(index);
final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(
windowingMode);
if (info == null) {
continue;
}
info.reason = windowingModeToReason.valueAt(index);
+ foundInfo = info;
}
if (allWindowsDrawn()) {
- reset(false /* abort */, null /* WindowingModeTransitionInfo */);
+ reset(false /* abort */, foundInfo, "notifyTransitionStarting - all windows drawn");
}
}
@@ -498,7 +534,7 @@
logAppTransitionCancel(info);
mWindowingModeTransitionInfo.remove(r.getWindowingMode());
if (mWindowingModeTransitionInfo.size() == 0) {
- reset(true /* abort */, info);
+ reset(true /* abort */, info, "notifyVisibilityChanged to invisible");
}
}
}
@@ -534,12 +570,25 @@
&& mWindowingModeTransitionInfo.size() > 0;
}
- private void reset(boolean abort, WindowingModeTransitionInfo info) {
- if (DEBUG_METRICS) Slog.i(TAG, "reset abort=" + abort);
+ private void reset(boolean abort, WindowingModeTransitionInfo info, String cause) {
+ if (DEBUG_METRICS) Slog.i(TAG, "reset abort=" + abort + ",cause=" + cause);
if (!abort && isAnyTransitionActive()) {
logAppTransitionMultiEvents();
}
stopLaunchTrace(info);
+
+ // Ignore reset-after reset.
+ if (isAnyTransitionActive()) {
+ // LaunchObserver callbacks.
+ if (abort) {
+ launchObserverNotifyActivityLaunchCancelled(info);
+ } else {
+ launchObserverNotifyActivityLaunchFinished(info);
+ }
+ } else {
+ launchObserverNotifyIntentFailed();
+ }
+
mCurrentTransitionStartTime = INVALID_START_TIME;
mCurrentTransitionDelayMs = INVALID_DELAY;
mLoggedTransitionStarting = false;
@@ -572,6 +621,13 @@
info.launchedActivity.packageName,
convertAppStartTransitionType(type),
info.launchedActivity.info.name);
+ if (DEBUG_METRICS) {
+ Slog.i(TAG, String.format("APP_START_CANCELED(%s, %s, %s, %s)",
+ info.launchedActivity.appInfo.uid,
+ info.launchedActivity.packageName,
+ convertAppStartTransitionType(type),
+ info.launchedActivity.info.name));
+ }
}
private void logAppTransitionMultiEvents() {
@@ -656,6 +712,17 @@
launchToken,
packageOptimizationInfo.getCompilationReason(),
packageOptimizationInfo.getCompilationFilter());
+
+ if (DEBUG_METRICS) {
+ Slog.i(TAG, String.format("APP_START_OCCURRED(%s, %s, %s, %s, %s)",
+ info.applicationInfo.uid,
+ info.packageName,
+ convertAppStartTransitionType(info.type),
+ info.launchedActivityName,
+ info.launchedActivityLaunchedFromPackage));
+ }
+
+
logAppStartMemoryStateCapture(info);
}
@@ -923,4 +990,76 @@
info.launchTraceActive = false;
}
}
+
+ /** Notify the {@link ActivityMetricsLaunchObserver} that a new launch sequence has begun. */
+ private void launchObserverNotifyIntentStarted(Intent intent) {
+ if (mLaunchObserver != null) {
+ // Beginning a launch is timing sensitive and so should be observed as soon as possible.
+ mLaunchObserver.onIntentStarted(intent);
+ }
+ }
+
+ /**
+ * Notify the {@link ActivityMetricsLaunchObserver} that the previous launch sequence has
+ * aborted due to intent failure (e.g. intent resolve failed or security error, etc) or
+ * intent being delivered to the top running activity.
+ */
+ private void launchObserverNotifyIntentFailed() {
+ if (mLaunchObserver != null) {
+ mLaunchObserver.onIntentFailed();
+ }
+ }
+
+ /**
+ * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence's activity
+ * has started.
+ */
+ private void launchObserverNotifyActivityLaunched(WindowingModeTransitionInfo info) {
+ @ActivityMetricsLaunchObserver.Temperature int temperature =
+ convertTransitionTypeToLaunchObserverTemperature(getTransitionType(info));
+
+ if (mLaunchObserver != null) {
+ // Beginning a launch is timing sensitive and so should be observed as soon as possible.
+ mLaunchObserver.onActivityLaunched(info.launchedActivity,
+ temperature);
+ }
+ }
+
+ /**
+ * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence is
+ * cancelled.
+ */
+ private void launchObserverNotifyActivityLaunchCancelled(WindowingModeTransitionInfo info) {
+ final ActivityRecord launchedActivity = info != null ? info.launchedActivity : null;
+
+ if (mLaunchObserver != null) {
+ mLaunchObserver.onActivityLaunchCancelled(launchedActivity);
+ }
+ }
+
+ /**
+ * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence's activity
+ * has fully finished (successfully).
+ */
+ private void launchObserverNotifyActivityLaunchFinished(WindowingModeTransitionInfo info) {
+ final ActivityRecord launchedActivity = info.launchedActivity;
+
+ if (mLaunchObserver != null) {
+ mLaunchObserver.onActivityLaunchFinished(launchedActivity);
+ }
+ }
+
+ private static @ActivityMetricsLaunchObserver.Temperature int
+ convertTransitionTypeToLaunchObserverTemperature(int transitionType) {
+ switch (transitionType) {
+ case TYPE_TRANSITION_WARM_LAUNCH:
+ return ActivityMetricsLaunchObserver.TEMPERATURE_WARM;
+ case TYPE_TRANSITION_HOT_LAUNCH:
+ return ActivityMetricsLaunchObserver.TEMPERATURE_HOT;
+ case TYPE_TRANSITION_COLD_LAUNCH:
+ return ActivityMetricsLaunchObserver.TEMPERATURE_COLD;
+ default:
+ return -1;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 77b331e..de203c9 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -4745,7 +4745,7 @@
final ActivityRecord targetActivity = task.getTopActivity();
sendPowerHintForLaunchStartIfNeeded(true /* forceSend */, targetActivity);
- mActivityMetricsLogger.notifyActivityLaunching();
+ mActivityMetricsLogger.notifyActivityLaunching(task.intent);
try {
mService.moveTaskToFrontLocked(task.taskId, 0, options,
true /* fromRecents */);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index e43a79a..afc946b 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -996,7 +996,7 @@
if (intent != null && intent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
- mSupervisor.getActivityMetricsLogger().notifyActivityLaunching();
+ mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
boolean componentSpecified = intent.getComponent() != null;
final int realCallingPid = Binder.getCallingPid();
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 7a1ebf2..9aeb025 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -122,7 +122,7 @@
targetActivity);
}
- mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching();
+ mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
mService.mAmInternal.setRunningRemoteAnimation(mCallingPid, true);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index cc4f055..54a140d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -230,6 +230,8 @@
"com.android.server.wallpaper.WallpaperManagerService$Lifecycle";
private static final String AUTO_FILL_MANAGER_SERVICE_CLASS =
"com.android.server.autofill.AutofillManagerService";
+ private static final String INTELLIGENCE_MANAGER_SERVICE_CLASS =
+ "com.android.server.intelligence.IntelligenceManagerService";
private static final String TIME_ZONE_RULES_MANAGER_SERVICE_CLASS =
"com.android.server.timezone.RulesManagerService$Lifecycle";
private static final String IOT_SERVICE_CLASS =
@@ -795,6 +797,8 @@
boolean disableSystemTextClassifier = SystemProperties.getBoolean(
"config.disable_systemtextclassifier", false);
+ boolean disableIntelligence = SystemProperties.getBoolean(
+ "config.disable_intelligence", false);
boolean disableNetworkTime = SystemProperties.getBoolean("config.disable_networktime",
false);
boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
@@ -1737,6 +1741,12 @@
traceEnd();
}
+ if (!disableIntelligence) {
+ traceBeginAndSlog("StartIntelligenceService");
+ mSystemServiceManager.startService(INTELLIGENCE_MANAGER_SERVICE_CLASS);
+ traceEnd();
+ }
+
traceBeginAndSlog("AppServiceManager");
mSystemServiceManager.startService(AppBindingService.Lifecycle.class);
traceEnd();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
index 6c125d1..4018eef 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
@@ -31,11 +31,9 @@
import android.app.Notification;
import android.app.NotificationChannel;
-import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.media.AudioAttributes;
import android.service.notification.StatusBarNotification;
-import android.service.notification.ZenModeConfig;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -45,6 +43,7 @@
import com.android.server.UiServiceTestCase;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -71,10 +70,12 @@
private NotificationRecord getNotificationRecord(NotificationChannel c) {
StatusBarNotification sbn = mock(StatusBarNotification.class);
- when(sbn.getNotification()).thenReturn(mock(Notification.class));
+ Notification notification = mock(Notification.class);
+ when(sbn.getNotification()).thenReturn(notification);
return new NotificationRecord(mContext, sbn, c);
}
+ @Ignore
@Test
public void testIsMessage() {
NotificationRecord r = getNotificationRecord();
@@ -86,6 +87,7 @@
assertFalse(mZenModeFiltering.isMessage(r));
}
+ @Ignore
@Test
public void testIsAlarm() {
NotificationChannel c = mock(NotificationChannel.class);
@@ -100,6 +102,7 @@
assertTrue(mZenModeFiltering.isAlarm(r));
}
+ @Ignore
@Test
public void testIsAlarm_wrongCategory() {
NotificationRecord r = getNotificationRecord();
@@ -107,6 +110,7 @@
assertFalse(mZenModeFiltering.isAlarm(r));
}
+ @Ignore
@Test
public void testIsAlarm_wrongUsage() {
NotificationChannel c = mock(NotificationChannel.class);
@@ -117,6 +121,7 @@
assertFalse(mZenModeFiltering.isAlarm(r));
}
+ @Ignore
@Test
public void testSuppressDNDInfo_yes_VisEffectsAllowed() {
NotificationRecord r = getNotificationRecord();
@@ -128,6 +133,7 @@
assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
}
+ @Ignore
@Test
public void testSuppressDNDInfo_yes_WrongId() {
NotificationRecord r = getNotificationRecord();
@@ -138,6 +144,7 @@
assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
}
+ @Ignore
@Test
public void testSuppressDNDInfo_yes_WrongPackage() {
NotificationRecord r = getNotificationRecord();
@@ -148,6 +155,7 @@
assertTrue(mZenModeFiltering.shouldIntercept(ZEN_MODE_IMPORTANT_INTERRUPTIONS, policy, r));
}
+ @Ignore
@Test
public void testSuppressDNDInfo_no() {
NotificationRecord r = getNotificationRecord();
@@ -160,6 +168,7 @@
assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_NO_INTERRUPTIONS, policy, r));
}
+ @Ignore
@Test
public void testSuppressAnything_yes_ZenModeOff() {
NotificationRecord r = getNotificationRecord();
@@ -169,6 +178,7 @@
assertFalse(mZenModeFiltering.shouldIntercept(ZEN_MODE_OFF, policy, r));
}
+ @Ignore
@Test
public void testSuppressAnything_bypass_ZenModeOn() {
NotificationRecord r = getNotificationRecord();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
new file mode 100644
index 0000000..215c51d
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.app.ActivityManager.START_SUCCESS;
+import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.content.Intent;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.util.SparseIntArray;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for the {@link ActivityMetricsLaunchObserver} class.
+ *
+ * Build/Install/Run:
+ * atest WmTests:ActivityMetricsLaunchObserverTests
+ */
+@SmallTest
+@Presubmit
+@FlakyTest(detail="promote once confirmed non-flaky")
+public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
+ private ActivityMetricsLogger mActivityMetricsLogger;
+ private ActivityMetricsLaunchObserver mLaunchObserver;
+
+ private TestActivityStack mStack;
+ private TaskRecord mTask;
+ private ActivityRecord mActivityRecord;
+ private ActivityRecord mActivityRecordTrampoline;
+
+ @Before
+ public void setUpAMLO() throws Exception {
+ setupActivityTaskManagerService();
+
+ mActivityMetricsLogger =
+ new ActivityMetricsLogger(mSupervisor, mService.mContext, mService.mH.getLooper());
+
+ mLaunchObserver = mock(ActivityMetricsLaunchObserver.class);
+
+ // TODO: Use ActivityMetricsLaunchObserverRegistry .
+ java.lang.reflect.Field f =
+ mActivityMetricsLogger.getClass().getDeclaredField("mLaunchObserver");
+ f.setAccessible(true);
+ f.set(mActivityMetricsLogger, mLaunchObserver);
+
+ // Sometimes we need an ActivityRecord for ActivityMetricsLogger to do anything useful.
+ // This seems to be the easiest way to create an ActivityRecord.
+ mStack = mSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
+ mActivityRecord = new ActivityBuilder(mService).setTask(mTask).build();
+ mActivityRecordTrampoline = new ActivityBuilder(mService).setTask(mTask).build();
+ }
+
+ @Test
+ public void testOnIntentStarted() throws Exception {
+ Intent intent = new Intent("action 1");
+
+ mActivityMetricsLogger.notifyActivityLaunching(intent);
+
+ verify(mLaunchObserver).onIntentStarted(eq(intent));
+ verifyNoMoreInteractions(mLaunchObserver);
+ }
+
+ @Test
+ public void testOnIntentFailed() throws Exception {
+ testOnIntentStarted();
+
+ ActivityRecord activityRecord = null;
+
+ // Bringing an intent that's already running 'to front' is not considered
+ // as an ACTIVITY_LAUNCHED state transition.
+ mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
+ activityRecord);
+
+ verify(mLaunchObserver).onIntentFailed();
+ verifyNoMoreInteractions(mLaunchObserver);
+ }
+
+ @Test
+ public void testOnActivityLaunched() throws Exception {
+ testOnIntentStarted();
+
+ mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
+ mActivityRecord);
+
+ verify(mLaunchObserver).onActivityLaunched(eq(mActivityRecord), anyInt());
+ verifyNoMoreInteractions(mLaunchObserver);
+ }
+
+ @Test
+ public void testOnActivityLaunchFinished() throws Exception {
+ testOnActivityLaunched();
+
+ mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
+ SystemClock.uptimeMillis());
+
+ mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecord.getWindowingMode(),
+ SystemClock.uptimeMillis());
+
+ verify(mLaunchObserver).onActivityLaunchFinished(eq(mActivityRecord));
+ verifyNoMoreInteractions(mLaunchObserver);
+ }
+
+ @Test
+ public void testOnActivityLaunchCancelled() throws Exception {
+ testOnActivityLaunched();
+
+ mActivityRecord.nowVisible = true;
+
+ // Cannot time already-visible activities.
+ mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mActivityRecord);
+
+ verify(mLaunchObserver).onActivityLaunchCancelled(eq(mActivityRecord));
+ verifyNoMoreInteractions(mLaunchObserver);
+ }
+
+ @Test
+ public void testOnActivityLaunchedTrampoline() throws Exception {
+ testOnIntentStarted();
+
+ mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
+ mActivityRecord);
+
+ verify(mLaunchObserver).onActivityLaunched(eq(mActivityRecord), anyInt());
+
+ // A second, distinct, activity launch is coalesced into the the current app launch sequence
+ mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
+ mActivityRecordTrampoline);
+
+ verifyNoMoreInteractions(mLaunchObserver);
+ }
+
+ @Test
+ public void testOnActivityLaunchFinishedTrampoline() throws Exception {
+ testOnActivityLaunchedTrampoline();
+
+ mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
+ SystemClock.uptimeMillis());
+
+ mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecordTrampoline.getWindowingMode(),
+ SystemClock.uptimeMillis());
+
+ verify(mLaunchObserver).onActivityLaunchFinished(eq(mActivityRecordTrampoline));
+ verifyNoMoreInteractions(mLaunchObserver);
+ }
+
+ @Test
+ public void testOnActivityLaunchCancelledTrampoline() throws Exception {
+ testOnActivityLaunchedTrampoline();
+
+ mActivityRecordTrampoline.nowVisible = true;
+
+ // Cannot time already-visible activities.
+ mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
+ mActivityRecordTrampoline);
+
+ verify(mLaunchObserver).onActivityLaunchCancelled(eq(mActivityRecordTrampoline));
+ verifyNoMoreInteractions(mLaunchObserver);
+ }
+}
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index cac9f2b..9c64cf6 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -32,7 +32,20 @@
public static final int IWLAN = 5;
/** @hide */
- private AccessNetworkType() {};
+ private AccessNetworkType() {}
+
+ /** @hide */
+ public static String toString(int type) {
+ switch (type) {
+ case UNKNOWN: return "UNKNOWN";
+ case GERAN: return "GERAN";
+ case UTRAN: return "UTRAN";
+ case EUTRAN: return "EUTRAN";
+ case CDMA2000: return "CDMA2000";
+ case IWLAN: return "IWLAN";
+ default: return Integer.toString(type);
+ }
+ }
}
/**
@@ -47,7 +60,16 @@
public static final int WLAN = 2;
/** @hide */
- private TransportType() {};
+ private TransportType() {}
+
+ /** @hide */
+ public static String toString(int type) {
+ switch (type) {
+ case WWAN: return "WWAN";
+ case WLAN: return "WLAN";
+ default: return Integer.toString(type);
+ }
+ }
}
/**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 983e766..99de4ca 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1231,6 +1231,47 @@
public static final String KEY_SHOW_PRECISE_FAILED_CAUSE_BOOL =
"show_precise_failed_cause_bool";
+ /**
+ * Boolean to decide whether lte is enabled.
+ * @hide
+ */
+ public static final String KEY_LTE_ENABLED_BOOL = "lte_enabled_bool";
+
+ /**
+ * Boolean to decide whether TD-SCDMA is supported.
+ * @hide
+ */
+ public static final String KEY_SUPPORT_TDSCDMA_BOOL = "support_tdscdma_bool";
+
+ /**
+ * A list of mcc/mnc that support TD-SCDMA for device when connect to the roaming network.
+ * @hide
+ */
+ public static final String KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY =
+ "support_tdscdma_roaming_networks_string_array";
+
+ /**
+ * Boolean to decide whether world mode is enabled.
+ * @hide
+ */
+ public static final String KEY_WORLD_MODE_ENABLED_BOOL = "world_mode_enabled_bool";
+
+ /**
+ * Package name of the carrier settings activity.
+ * @see {@link #KEY_CARRIER_SETTINGS_ACTIVITY_CLASS_NAME_STRING}.
+ * @hide
+ */
+ public static final String KEY_CARRIER_SETTINGS_ACTIVITY_PACKAGE_NAME_STRING =
+ "carrier_settings_activity_package_name_string";
+
+ /**
+ * Class name of the carrier settings activity.
+ * @see {@link #KEY_CARRIER_SETTINGS_ACTIVITY_PACKAGE_NAME_STRING}.
+ * @hide
+ */
+ public static final String KEY_CARRIER_SETTINGS_ACTIVITY_CLASS_NAME_STRING =
+ "carrier_settings_activity_class_name_string";
+
// These variables are used by the MMS service and exposed through another API,
// SmsManager. The variable names and string values are copied from there.
public static final String KEY_MMS_ALIAS_ENABLED_BOOL = "aliasEnabled";
@@ -2570,6 +2611,12 @@
sDefaults.putBoolean(KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL, false);
sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, true);
+ sDefaults.putBoolean(KEY_LTE_ENABLED_BOOL, true);
+ sDefaults.putBoolean(KEY_SUPPORT_TDSCDMA_BOOL, false);
+ sDefaults.putStringArray(KEY_SUPPORT_TDSCDMA_ROAMING_NETWORKS_STRING_ARRAY, null);
+ sDefaults.putBoolean(KEY_WORLD_MODE_ENABLED_BOOL, false);
+ sDefaults.putString(KEY_CARRIER_SETTINGS_ACTIVITY_PACKAGE_NAME_STRING, "");
+ sDefaults.putString(KEY_CARRIER_SETTINGS_ACTIVITY_CLASS_NAME_STRING, "");
sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
sDefaults.putBoolean(KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL, false);
sDefaults.putIntArray(KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY,
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index d7169b2..c6887ab 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -342,6 +342,12 @@
*/
public static final int TOO_MANY_ONGOING_CALLS = 75;
+ /**
+ * Indicates that a new outgoing call cannot be placed because OTASP provisioning is currently
+ * in process.
+ */
+ public static final int OTASP_PROVISIONING_IN_PROCESS = 76;
+
//*********************************************************************************************
// When adding a disconnect type:
// 1) Update toString() with the newly added disconnect type.
@@ -505,6 +511,8 @@
return "CALLING_DISABLED";
case TOO_MANY_ONGOING_CALLS:
return "TOO_MANY_ONGOING_CALLS";
+ case OTASP_PROVISIONING_IN_PROCESS:
+ return "OTASP_PROVISIONING_IN_PROCESS";
default:
return "INVALID: " + cause;
}
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index aabefe3..2e9bffe 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1184,6 +1184,16 @@
}
/**
+ * @param apnType APN type
+ * @return APN type in string format
+ * @hide
+ */
+ public static String getApnTypeString(int apnType) {
+ String apnTypeString = APN_TYPE_INT_MAP.get(apnType);
+ return apnTypeString == null ? "Unknown" : apnTypeString;
+ }
+
+ /**
* @param types comma delimited list of APN types.
* @return bitmask of APN types.
* @hide
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 89ef339..f73036e 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -16,17 +16,20 @@
package android.telephony.ims;
+import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.PersistableBundle;
import android.telecom.VideoProfile;
import android.util.Log;
import com.android.internal.telephony.PhoneConstants;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Parcelable object to handle IMS call profile.
* It is created from GSMA IR.92/IR.94, 3GPP TS 24.229/TS 26.114/TS26.111.
@@ -206,17 +209,36 @@
public static final int DIALSTRING_USSD = 2;
/**
- * Values for causes that restrict call types
+ * Call is not restricted on peer side and High Definition media is supported
*/
- // Default cause not restricted at peer and HD is supported
public static final int CALL_RESTRICT_CAUSE_NONE = 0;
- // Service not supported by RAT at peer
+
+ /**
+ * High Definition media is not supported on the peer side due to the Radio Access Technology
+ * (RAT) it is are connected to.
+ */
public static final int CALL_RESTRICT_CAUSE_RAT = 1;
- // Service Disabled at peer
+
+ /**
+ * The service has been disabled on the peer side.
+ */
public static final int CALL_RESTRICT_CAUSE_DISABLED = 2;
- // HD is not supported
+
+ /**
+ * High definition media is not currently supported.
+ */
public static final int CALL_RESTRICT_CAUSE_HD = 3;
+ /**@hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "CALL_RESTRICT_CAUSE_", value = {
+ CALL_RESTRICT_CAUSE_NONE,
+ CALL_RESTRICT_CAUSE_RAT,
+ CALL_RESTRICT_CAUSE_DISABLED,
+ CALL_RESTRICT_CAUSE_HD
+ })
+ public @interface CallRestrictCause {}
+
/**
* String extra properties
* oi : Originating identity (number), MT only
@@ -270,7 +292,7 @@
public int mCallType;
/** @hide */
@UnsupportedAppUsage
- public int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;
+ public @CallRestrictCause int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;
/**
* Extras associated with this {@link ImsCallProfile}.
@@ -285,7 +307,7 @@
* <li>{@code long[]}</li>
* <li>{@code double[]}</li>
* <li>{@code String[]}</li>
- * <li>{@link PersistableBundle}</li>
+ * <li>{@link android.os.PersistableBundle}</li>
* <li>{@link Boolean} (and boolean)</li>
* <li>{@code boolean[]}</li>
* <li>Other {@link Parcelable} classes in the {@code android.*} namespace.</li>
@@ -426,6 +448,14 @@
}
}
+ /**
+ * Set the call restrict cause, which provides the reason why a call has been restricted from
+ * using High Definition media.
+ */
+ public void setCallRestrictCause(@CallRestrictCause int cause) {
+ mRestrictCause = cause;
+ }
+
public void updateCallType(ImsCallProfile profile) {
mCallType = profile.mCallType;
}
@@ -494,7 +524,11 @@
return mCallType;
}
- public int getRestrictCause() {
+ /**
+ * @return The call restrict cause, which provides the reason why a call has been restricted
+ * from using High Definition media.
+ */
+ public @CallRestrictCause int getRestrictCause() {
return mRestrictCause;
}