diff options
86 files changed, 2319 insertions, 794 deletions
diff --git a/api/current.txt b/api/current.txt index d79aa02e2640..c72de2e94e0d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -28721,7 +28721,7 @@ package android.net { method public long getReportTimestamp(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.ConnectivityReport> CREATOR; - field public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttemped"; + field public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttempted"; field public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK = "networkProbesSucceeded"; field public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult"; field public static final int NETWORK_PROBE_DNS = 4; // 0x4 diff --git a/api/system-current.txt b/api/system-current.txt index ac287b226998..3a988314cc71 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -59,6 +59,7 @@ package android { field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"; field @Deprecated public static final String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL"; field public static final String CONNECTIVITY_USE_RESTRICTED_NETWORKS = "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"; + field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS"; field public static final String CONTROL_DISPLAY_COLOR_TRANSFORMS = "android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"; field public static final String CONTROL_DISPLAY_SATURATION = "android.permission.CONTROL_DISPLAY_SATURATION"; field public static final String CONTROL_INCALL_EXPERIENCE = "android.permission.CONTROL_INCALL_EXPERIENCE"; @@ -2562,6 +2563,48 @@ package android.hardware.hdmi { } +package android.hardware.lights { + + public final class Light implements android.os.Parcelable { + method public int describeContents(); + method public int getId(); + method public int getOrdinal(); + method public int getType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.Light> CREATOR; + } + + public final class LightState implements android.os.Parcelable { + ctor public LightState(@ColorInt int); + method public int describeContents(); + method @ColorInt public int getColor(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.LightState> CREATOR; + } + + public final class LightsManager { + method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public java.util.List<android.hardware.lights.Light> getLights(); + method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightsManager.LightsSession openSession(); + field public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8 + } + + public final class LightsManager.LightsSession implements java.lang.AutoCloseable { + method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void close(); + method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void setLights(@NonNull android.hardware.lights.LightsRequest); + } + + public final class LightsRequest { + } + + public static final class LightsRequest.Builder { + ctor public LightsRequest.Builder(); + method @NonNull public android.hardware.lights.LightsRequest build(); + method @NonNull public android.hardware.lights.LightsRequest.Builder clearLight(@NonNull android.hardware.lights.Light); + method @NonNull public android.hardware.lights.LightsRequest.Builder setLight(@NonNull android.hardware.lights.Light, @NonNull android.hardware.lights.LightState); + } + +} + package android.hardware.location { public class ContextHubClient implements java.io.Closeable { @@ -4613,12 +4656,20 @@ package android.net { public class NetworkPolicyManager { method @NonNull public android.telephony.SubscriptionPlan[] getSubscriptionPlans(int, @NonNull String); + method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void registerSubscriptionCallback(@NonNull android.net.NetworkPolicyManager.SubscriptionCallback); method public void setSubscriptionOverride(int, int, int, long, @NonNull String); method public void setSubscriptionPlans(int, @NonNull android.telephony.SubscriptionPlan[], @NonNull String); + method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public void unregisterSubscriptionCallback(@NonNull android.net.NetworkPolicyManager.SubscriptionCallback); field public static final int SUBSCRIPTION_OVERRIDE_CONGESTED = 2; // 0x2 field public static final int SUBSCRIPTION_OVERRIDE_UNMETERED = 1; // 0x1 } + public static class NetworkPolicyManager.SubscriptionCallback { + ctor public NetworkPolicyManager.SubscriptionCallback(); + method public void onSubscriptionOverride(int, int, int); + method public void onSubscriptionPlansChanged(int, @NonNull android.telephony.SubscriptionPlan[]); + } + public class NetworkProvider { ctor public NetworkProvider(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String); method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void declareNetworkRequestUnfulfillable(@NonNull android.net.NetworkRequest); diff --git a/api/test-current.txt b/api/test-current.txt index 1e5b7b82a916..afb49c2222b8 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -10,6 +10,7 @@ package android { field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE"; field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA"; field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"; + field public static final String CONTROL_DEVICE_LIGHTS = "android.permission.CONTROL_DEVICE_LIGHTS"; field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES"; field public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS"; field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS"; @@ -985,6 +986,49 @@ package android.hardware.display { } +package android.hardware.lights { + + public final class Light implements android.os.Parcelable { + method public int describeContents(); + method public int getId(); + method public int getOrdinal(); + method public int getType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.Light> CREATOR; + } + + public final class LightState implements android.os.Parcelable { + ctor public LightState(@ColorInt int); + method public int describeContents(); + method @ColorInt public int getColor(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.LightState> CREATOR; + } + + public final class LightsManager { + method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightState getLightState(@NonNull android.hardware.lights.Light); + method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public java.util.List<android.hardware.lights.Light> getLights(); + method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightsManager.LightsSession openSession(); + field public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8 + } + + public final class LightsManager.LightsSession implements java.lang.AutoCloseable { + method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void close(); + method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void setLights(@NonNull android.hardware.lights.LightsRequest); + } + + public final class LightsRequest { + } + + public static final class LightsRequest.Builder { + ctor public LightsRequest.Builder(); + method @NonNull public android.hardware.lights.LightsRequest build(); + method @NonNull public android.hardware.lights.LightsRequest.Builder clearLight(@NonNull android.hardware.lights.Light); + method @NonNull public android.hardware.lights.LightsRequest.Builder setLight(@NonNull android.hardware.lights.Light, @NonNull android.hardware.lights.LightState); + } + +} + package android.location { public final class GnssClock implements android.os.Parcelable { diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index da1a76f8169d..88db89dbd279 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -229,8 +229,6 @@ cc_binary { shared_libs: ["libgtest_prod"], - vintf_fragments: ["android.frameworks.stats@1.0-service.xml"], - init_rc: ["statsd.rc"], } diff --git a/cmds/statsd/android.frameworks.stats@1.0-service.xml b/cmds/statsd/android.frameworks.stats@1.0-service.xml deleted file mode 100644 index bb02f66a28b1..000000000000 --- a/cmds/statsd/android.frameworks.stats@1.0-service.xml +++ /dev/null @@ -1,11 +0,0 @@ -<manifest version="1.0" type="framework"> - <hal> - <name>android.frameworks.stats</name> - <transport>hwbinder</transport> - <version>1.0</version> - <interface> - <name>IStats</name> - <instance>default</instance> - </interface> - </hal> -</manifest> diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 1c7180ffbde1..d6c1380fa253 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -1440,7 +1440,6 @@ Status StatsService::sendWatchdogRollbackOccurredAtom(const int32_t rollbackType return Status::ok(); } - Status StatsService::getRegisteredExperimentIds(std::vector<int64_t>* experimentIdsOut) { uid_t uid = IPCThreadState::self()->getCallingUid(); @@ -1468,103 +1467,6 @@ Status StatsService::getRegisteredExperimentIds(std::vector<int64_t>* experiment return Status::ok(); } -hardware::Return<void> StatsService::reportSpeakerImpedance( - const SpeakerImpedance& speakerImpedance) { - android::util::stats_write(android::util::SPEAKER_IMPEDANCE_REPORTED, - speakerImpedance.speakerLocation, speakerImpedance.milliOhms); - - return hardware::Void(); -} - -hardware::Return<void> StatsService::reportHardwareFailed(const HardwareFailed& hardwareFailed) { - android::util::stats_write(android::util::HARDWARE_FAILED, int32_t(hardwareFailed.hardwareType), - hardwareFailed.hardwareLocation, int32_t(hardwareFailed.errorCode)); - - return hardware::Void(); -} - -hardware::Return<void> StatsService::reportPhysicalDropDetected( - const PhysicalDropDetected& physicalDropDetected) { - android::util::stats_write(android::util::PHYSICAL_DROP_DETECTED, - int32_t(physicalDropDetected.confidencePctg), physicalDropDetected.accelPeak, - physicalDropDetected.freefallDuration); - - return hardware::Void(); -} - -hardware::Return<void> StatsService::reportChargeCycles(const ChargeCycles& chargeCycles) { - std::vector<int32_t> buckets = chargeCycles.cycleBucket; - int initialSize = buckets.size(); - for (int i = 0; i < 10 - initialSize; i++) { - buckets.push_back(-1); // Push -1 for buckets that do not exist. - } - android::util::stats_write(android::util::CHARGE_CYCLES_REPORTED, buckets[0], buckets[1], - buckets[2], buckets[3], buckets[4], buckets[5], buckets[6], buckets[7], buckets[8], - buckets[9]); - - return hardware::Void(); -} - -hardware::Return<void> StatsService::reportBatteryHealthSnapshot( - const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) { - android::util::stats_write(android::util::BATTERY_HEALTH_SNAPSHOT, - int32_t(batteryHealthSnapshotArgs.type), batteryHealthSnapshotArgs.temperatureDeciC, - batteryHealthSnapshotArgs.voltageMicroV, batteryHealthSnapshotArgs.currentMicroA, - batteryHealthSnapshotArgs.openCircuitVoltageMicroV, - batteryHealthSnapshotArgs.resistanceMicroOhm, batteryHealthSnapshotArgs.levelPercent); - - return hardware::Void(); -} - -hardware::Return<void> StatsService::reportSlowIo(const SlowIo& slowIo) { - android::util::stats_write(android::util::SLOW_IO, int32_t(slowIo.operation), slowIo.count); - - return hardware::Void(); -} - -hardware::Return<void> StatsService::reportBatteryCausedShutdown( - const BatteryCausedShutdown& batteryCausedShutdown) { - android::util::stats_write(android::util::BATTERY_CAUSED_SHUTDOWN, - batteryCausedShutdown.voltageMicroV); - - return hardware::Void(); -} - -hardware::Return<void> StatsService::reportUsbPortOverheatEvent( - const UsbPortOverheatEvent& usbPortOverheatEvent) { - android::util::stats_write(android::util::USB_PORT_OVERHEAT_EVENT_REPORTED, - usbPortOverheatEvent.plugTemperatureDeciC, usbPortOverheatEvent.maxTemperatureDeciC, - usbPortOverheatEvent.timeToOverheat, usbPortOverheatEvent.timeToHysteresis, - usbPortOverheatEvent.timeToInactive); - - return hardware::Void(); -} - -hardware::Return<void> StatsService::reportSpeechDspStat( - const SpeechDspStat& speechDspStat) { - android::util::stats_write(android::util::SPEECH_DSP_STAT_REPORTED, - speechDspStat.totalUptimeMillis, speechDspStat.totalDowntimeMillis, - speechDspStat.totalCrashCount, speechDspStat.totalRecoverCount); - - return hardware::Void(); -} - -hardware::Return<void> StatsService::reportVendorAtom(const VendorAtom& vendorAtom) { - std::string reverseDomainName = (std::string) vendorAtom.reverseDomainName; - if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) { - ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId); - return hardware::Void(); - } - if (reverseDomainName.length() > 50) { - ALOGE("Vendor atom reverse domain name %s is too long.", reverseDomainName.c_str()); - return hardware::Void(); - } - LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), vendorAtom); - mProcessor->OnLogEvent(&event); - - return hardware::Void(); -} - void StatsService::binderDied(const wp <IBinder>& who) { ALOGW("statscompanion service died"); StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec()); diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 53b6ce989195..dba2502eeae7 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -53,7 +53,6 @@ namespace statsd { using android::hardware::Return; class StatsService : public BnStatsManager, - public IStats, public IBinder::DeathRecipient { public: StatsService(const sp<Looper>& handlerLooper, std::shared_ptr<LogEventQueue> queue); @@ -207,61 +206,6 @@ public: */ virtual Status getRegisteredExperimentIds(std::vector<int64_t>* expIdsOut); - /** - * Binder call to get SpeakerImpedance atom. - */ - virtual Return<void> reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) override; - - /** - * Binder call to get HardwareFailed atom. - */ - virtual Return<void> reportHardwareFailed(const HardwareFailed& hardwareFailed) override; - - /** - * Binder call to get PhysicalDropDetected atom. - */ - virtual Return<void> reportPhysicalDropDetected( - const PhysicalDropDetected& physicalDropDetected) override; - - /** - * Binder call to get ChargeCyclesReported atom. - */ - virtual Return<void> reportChargeCycles(const ChargeCycles& chargeCycles) override; - - /** - * Binder call to get BatteryHealthSnapshot atom. - */ - virtual Return<void> reportBatteryHealthSnapshot( - const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) override; - - /** - * Binder call to get SlowIo atom. - */ - virtual Return<void> reportSlowIo(const SlowIo& slowIo) override; - - /** - * Binder call to get BatteryCausedShutdown atom. - */ - virtual Return<void> reportBatteryCausedShutdown( - const BatteryCausedShutdown& batteryCausedShutdown) override; - - /** - * Binder call to get UsbPortOverheatEvent atom. - */ - virtual Return<void> reportUsbPortOverheatEvent( - const UsbPortOverheatEvent& usbPortOverheatEvent) override; - - /** - * Binder call to get Speech DSP state atom. - */ - virtual Return<void> reportSpeechDspStat( - const SpeechDspStat& speechDspStat) override; - - /** - * Binder call to get vendor atom. - */ - virtual Return<void> reportVendorAtom(const VendorAtom& vendorAtom) override; - /** IBinder::DeathRecipient */ virtual void binderDied(const wp<IBinder>& who) override; diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 838561e3ae10..1d51ab82efa8 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -207,37 +207,6 @@ LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requi } LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, - const VendorAtom& vendorAtom) { - mLogdTimestampNs = wallClockTimestampNs; - mElapsedTimestampNs = elapsedTimestampNs; - mTagId = vendorAtom.atomId; - mLogUid = AID_STATSD; - - mValues.push_back( - FieldValue(Field(mTagId, getSimpleField(1)), Value(vendorAtom.reverseDomainName))); - for (int i = 0; i < (int)vendorAtom.values.size(); i++) { - switch (vendorAtom.values[i].getDiscriminator()) { - case VendorAtom::Value::hidl_discriminator::intValue: - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 2)), - Value(vendorAtom.values[i].intValue()))); - break; - case VendorAtom::Value::hidl_discriminator::longValue: - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 2)), - Value(vendorAtom.values[i].longValue()))); - break; - case VendorAtom::Value::hidl_discriminator::floatValue: - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 2)), - Value(vendorAtom.values[i].floatValue()))); - break; - case VendorAtom::Value::hidl_discriminator::stringValue: - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 2)), - Value(vendorAtom.values[i].stringValue()))); - break; - } - } -} - -LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, const InstallTrainInfo& trainInfo) { mLogdTimestampNs = wallClockTimestampNs; mElapsedTimestampNs = elapsedTimestampNs; diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 531ce299beef..b46802af3dd8 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -18,7 +18,6 @@ #include "FieldValue.h" -#include <android/frameworks/stats/1.0/types.h> #include <android/os/StatsLogEventWrapper.h> #include <android/util/ProtoOutputStream.h> #include <log/log_event_list.h> @@ -29,8 +28,6 @@ #include <string> #include <vector> -using namespace android::frameworks::stats::V1_0; - namespace android { namespace os { namespace statsd { @@ -111,9 +108,6 @@ public: const std::vector<uint8_t>& experimentIds, int32_t userId); explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, - const VendorAtom& vendorAtom); - - explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, const InstallTrainInfo& installTrainInfo); ~LogEvent(); diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp index 7d446a9a1ed6..0e669287111d 100644 --- a/cmds/statsd/src/main.cpp +++ b/cmds/statsd/src/main.cpp @@ -80,8 +80,6 @@ int main(int /*argc*/, char** /*argv*/) { ps->giveThreadPoolName(); IPCThreadState::self()->disableBackgroundScheduling(true); - ::android::hardware::configureRpcThreadpool(4 /*threads*/, false /*willJoin*/); - std::shared_ptr<LogEventQueue> eventQueue = std::make_shared<LogEventQueue>(2000 /*buffer limit. Buffer is NOT pre-allocated*/); @@ -94,12 +92,6 @@ int main(int /*argc*/, char** /*argv*/) { return -1; } - auto ret = gStatsService->registerAsService(); - if (ret != ::android::OK) { - ALOGE("Failed to add service as HIDL service"); - return 1; // or handle error - } - registerSigHandler(); gStatsService->sayHiToStatsCompanion(); diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 86b2c06ccff2..d79cd9751c17 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -83,6 +83,7 @@ import android.hardware.hdmi.IHdmiControlService; import android.hardware.input.InputManager; import android.hardware.iris.IIrisService; import android.hardware.iris.IrisManager; +import android.hardware.lights.LightsManager; import android.hardware.location.ContextHubManager; import android.hardware.radio.RadioManager; import android.hardware.usb.IUsbManager; @@ -1348,6 +1349,13 @@ final class SystemServiceRegistry { return new DynamicSystemManager( IDynamicSystemService.Stub.asInterface(b)); }}); + registerService(Context.LIGHTS_SERVICE, LightsManager.class, + new CachedServiceFetcher<LightsManager>() { + @Override + public LightsManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return new LightsManager(ctx); + }}); //CHECKSTYLE:ON IndentationCheck } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index c3c7f972066b..4527d8ed83aa 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3380,6 +3380,7 @@ public abstract class Context { //@hide: TIME_DETECTOR_SERVICE, //@hide: TIME_ZONE_DETECTOR_SERVICE, PERMISSION_SERVICE, + LIGHTS_SERVICE, }) @Retention(RetentionPolicy.SOURCE) public @interface ServiceName {} @@ -4880,6 +4881,15 @@ public abstract class Context { public static final String TELEPHONY_REGISTRY_SERVICE = "telephony_registry"; /** + * Use with {@link #getSystemService(String)} to retrieve a + * {@link android.hardware.lights.LightsManager} for controlling device lights. + * + * @see #getSystemService(String) + * @hide + */ + public static final String LIGHTS_SERVICE = "lights"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/android/hardware/lights/ILightsManager.aidl b/core/java/android/hardware/lights/ILightsManager.aidl new file mode 100644 index 000000000000..6ea24b74a4a3 --- /dev/null +++ b/core/java/android/hardware/lights/ILightsManager.aidl @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2020 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.hardware.lights; + +import android.hardware.lights.Light; +import android.hardware.lights.LightState; + +/** + * API to lights manager service. + * + * {@hide} + */ +interface ILightsManager { + List<Light> getLights(); + LightState getLightState(int lightId); + void openSession(in IBinder sessionToken); + void closeSession(in IBinder sessionToken); + void setLightStates(in IBinder sessionToken, in int[] lightIds, in LightState[] states); +} diff --git a/core/java/android/hardware/lights/Light.aidl b/core/java/android/hardware/lights/Light.aidl new file mode 100644 index 000000000000..946e06d44cf3 --- /dev/null +++ b/core/java/android/hardware/lights/Light.aidl @@ -0,0 +1,20 @@ +/** + * Copyright (C) 2020 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.hardware.lights; + +/** @hide */ +parcelable Light; diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java new file mode 100644 index 000000000000..c5cb8037d4db --- /dev/null +++ b/core/java/android/hardware/lights/Light.java @@ -0,0 +1,105 @@ +/** + * Copyright (C) 2020 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.hardware.lights; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Represents a logical light on the device. + * + * @hide + */ +@SystemApi +@TestApi +public final class Light implements Parcelable { + private final int mId; + private final int mOrdinal; + private final int mType; + + /** + * Creates a new light with the given data. + * + * @hide */ + public Light(int id, int ordinal, int type) { + mId = id; + mOrdinal = ordinal; + mType = type; + } + + private Light(@NonNull Parcel in) { + mId = in.readInt(); + mOrdinal = in.readInt(); + mType = in.readInt(); + } + + /** Implement the Parcelable interface */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mId); + dest.writeInt(mOrdinal); + dest.writeInt(mType); + } + + /** Implement the Parcelable interface */ + @Override + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface */ + public static final @android.annotation.NonNull Parcelable.Creator<Light> CREATOR = + new Parcelable.Creator<Light>() { + public Light createFromParcel(Parcel in) { + return new Light(in); + } + + public Light[] newArray(int size) { + return new Light[size]; + } + }; + + /** + * Returns the id of the light. + */ + public int getId() { + return mId; + } + + /** + * Returns the ordinal of the light. + * + * <p>This represents the physical order of the lights on the device. The exact values are + * device-dependent, but for example, if there are lights in a row, sorting the Light objects + * by ordinal should match the order in which they appear on the device. If the device has + * 4 lights, the ordinals could be [1, 2, 3, 4] or [0, 10, 20, 30] or any other values that + * have the same sort order. + */ + public int getOrdinal() { + return mOrdinal; + } + + /** + * Returns the logical type of the light. + */ + public @LightsManager.LightType int getType() { + return mType; + } +} diff --git a/core/java/android/hardware/lights/LightState.aidl b/core/java/android/hardware/lights/LightState.aidl new file mode 100644 index 000000000000..d598336ae40c --- /dev/null +++ b/core/java/android/hardware/lights/LightState.aidl @@ -0,0 +1,20 @@ +/** + * Copyright (C) 2020 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.hardware.lights; + +/** @hide */ +parcelable LightState; diff --git a/core/java/android/hardware/lights/LightState.java b/core/java/android/hardware/lights/LightState.java new file mode 100644 index 000000000000..e55aa702f15c --- /dev/null +++ b/core/java/android/hardware/lights/LightState.java @@ -0,0 +1,84 @@ +/** + * Copyright (C) 2020 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.hardware.lights; + +import android.annotation.ColorInt; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Represents the state of a device light. + * + * <p>Controlling the color and brightness of a light is done on a best-effort basis. Each of the R, + * G and B channels represent the intensities of the respective part of an RGB LED, if that is + * supported. For devices that only support on or off lights, everything that's not off will turn + * the light on. If the light is monochrome and only the brightness can be controlled, the RGB color + * will be converted to only a brightness value and that will be used for the light's single + * channel. + * + * @hide + */ +@SystemApi +@TestApi +public final class LightState implements Parcelable { + private final int mColor; + + /** + * Creates a new LightState with the desired color and intensity. + * + * @param color the desired color and intensity in ARGB format. + */ + public LightState(@ColorInt int color) { + mColor = color; + } + + private LightState(@NonNull Parcel in) { + mColor = in.readInt(); + } + + /** + * Return the color and intensity associated with this LightState. + * @return the color and intensity in ARGB format. The A channel is ignored. + */ + public @ColorInt int getColor() { + return mColor; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mColor); + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Parcelable.Creator<LightState> CREATOR = + new Parcelable.Creator<LightState>() { + public LightState createFromParcel(Parcel in) { + return new LightState(in); + } + + public LightState[] newArray(int size) { + return new LightState[size]; + } + }; +} diff --git a/core/java/android/hardware/lights/LightsManager.java b/core/java/android/hardware/lights/LightsManager.java new file mode 100644 index 000000000000..1bc051b977a8 --- /dev/null +++ b/core/java/android/hardware/lights/LightsManager.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2020 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.hardware.lights; + +import android.Manifest; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.annotation.TestApi; +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.ServiceManager.ServiceNotFoundException; +import android.util.CloseGuard; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.ref.Reference; +import java.util.List; + +/** + * The LightsManager class allows control over device lights. + * + * @hide + */ +@SystemApi +@TestApi +@SystemService(Context.LIGHTS_SERVICE) +public final class LightsManager { + private static final String TAG = "LightsManager"; + + // These enum values copy the values from {@link com.android.server.lights.LightsManager} + // and the light HAL. Since 0-7 are lights reserved for system use, only the microphone light + // is available through this API. + /** Type for lights that indicate microphone usage */ + public static final int LIGHT_TYPE_MICROPHONE = 8; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"LIGHT_TYPE_"}, + value = { + LIGHT_TYPE_MICROPHONE, + }) + public @interface LightType {} + + @NonNull private final Context mContext; + @NonNull private final ILightsManager mService; + + /** + * Creates a LightsManager. + * + * @hide + */ + public LightsManager(@NonNull Context context) throws ServiceNotFoundException { + this(context, ILightsManager.Stub.asInterface( + ServiceManager.getServiceOrThrow(Context.LIGHTS_SERVICE))); + } + + /** + * Creates a LightsManager with a provided service implementation. + * + * @hide + */ + @VisibleForTesting + public LightsManager(@NonNull Context context, @NonNull ILightsManager service) { + mContext = Preconditions.checkNotNull(context); + mService = Preconditions.checkNotNull(service); + } + + /** + * Returns the lights available on the device. + * + * @return A list of available lights + */ + @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) + public @NonNull List<Light> getLights() { + try { + return mService.getLights(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns the state of a specified light. + * + * @hide + */ + @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) + @TestApi + public @NonNull LightState getLightState(@NonNull Light light) { + Preconditions.checkNotNull(light); + try { + return mService.getLightState(light.getId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Creates a new LightsSession that can be used to control the device lights. + */ + @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) + public @NonNull LightsSession openSession() { + try { + final LightsSession session = new LightsSession(); + mService.openSession(session.mToken); + return session; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Encapsulates a session that can be used to control device lights and represents the lifetime + * of the requests. + */ + public final class LightsSession implements AutoCloseable { + + private final IBinder mToken = new Binder(); + + private final CloseGuard mCloseGuard = new CloseGuard(); + private boolean mClosed = false; + + /** + * Instantiated by {@link LightsManager#openSession()}. + */ + @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) + private LightsSession() { + mCloseGuard.open("close"); + } + + /** + * Sends a request to modify the states of multiple lights. + * + * <p>This method only controls lights that aren't overridden by higher-priority sessions. + * Additionally, lights not controlled by this session can be controlled by lower-priority + * sessions. + * + * @param request the settings for lights that should change + */ + @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) + public void setLights(@NonNull LightsRequest request) { + Preconditions.checkNotNull(request); + if (!mClosed) { + try { + mService.setLightStates(mToken, request.mLightIds, request.mLightStates); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Closes the session, reverting all changes made through it. + */ + @RequiresPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS) + @Override + public void close() { + if (!mClosed) { + try { + mService.closeSession(mToken); + mClosed = true; + mCloseGuard.close(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + Reference.reachabilityFence(this); + } + + /** @hide */ + @Override + protected void finalize() throws Throwable { + try { + mCloseGuard.warnIfOpen(); + close(); + } finally { + super.finalize(); + } + } + } +} diff --git a/core/java/android/hardware/lights/LightsRequest.java b/core/java/android/hardware/lights/LightsRequest.java new file mode 100644 index 000000000000..a36da4c7d85d --- /dev/null +++ b/core/java/android/hardware/lights/LightsRequest.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2020 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.hardware.lights; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.util.SparseArray; + +import com.android.internal.util.Preconditions; + +/** + * Encapsulates a request to modify the state of multiple lights. + * + * @hide + */ +@SystemApi +@TestApi +public final class LightsRequest { + + /** Visible to {@link LightsManager.Session}. */ + final int[] mLightIds; + + /** Visible to {@link LightsManager.Session}. */ + final LightState[] mLightStates; + + /** + * Can only be constructed via {@link LightsRequest.Builder#build()}. + */ + private LightsRequest(SparseArray<LightState> changes) { + final int n = changes.size(); + mLightIds = new int[n]; + mLightStates = new LightState[n]; + for (int i = 0; i < n; i++) { + mLightIds[i] = changes.keyAt(i); + mLightStates[i] = changes.valueAt(i); + } + } + + /** + * Builder for creating device light change requests. + */ + public static final class Builder { + + private final SparseArray<LightState> mChanges = new SparseArray<>(); + + /** + * Overrides the color and intensity of a given light. + * + * @param light the light to modify + * @param state the desired color and intensity of the light + */ + public @NonNull Builder setLight(@NonNull Light light, @NonNull LightState state) { + Preconditions.checkNotNull(light); + Preconditions.checkNotNull(state); + mChanges.put(light.getId(), state); + return this; + } + + /** + * Removes the override for the color and intensity of a given light. + * + * @param light the light to modify + */ + public @NonNull Builder clearLight(@NonNull Light light) { + Preconditions.checkNotNull(light); + mChanges.put(light.getId(), null); + return this; + } + + /** + * Create a LightsRequest object used to override lights on the device. + * + * <p>The generated {@link LightsRequest} should be used in + * {@link LightsManager.Session#setLights(LightsLightsRequest). + */ + public @NonNull LightsRequest build() { + return new LightsRequest(mChanges); + } + } +} diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java index b128ea7f3e39..3c39d1558e87 100644 --- a/core/java/android/net/ConnectivityDiagnosticsManager.java +++ b/core/java/android/net/ConnectivityDiagnosticsManager.java @@ -202,7 +202,7 @@ public class ConnectivityDiagnosticsManager { */ @NetworkProbe public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = - "networkProbesAttemped"; + "networkProbesAttempted"; /** @hide */ @StringDef(prefix = {"KEY_"}, value = { diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 0f66c794ea50..14442a2088cd 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.GET_SIGNATURES; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.app.ActivityManager; @@ -46,11 +47,13 @@ import java.lang.annotation.RetentionPolicy; import java.time.ZonedDateTime; import java.util.HashSet; import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * Manager for creating and modifying network policy rules. * - * {@hide} + * @hide */ @SystemService(Context.NETWORK_POLICY_SERVICE) @SystemApi @@ -89,6 +92,7 @@ public class NetworkPolicyManager { * * See network-policy-restrictions.md for more info. */ + /** * No specific rule was set * @hide @@ -120,6 +124,7 @@ public class NetworkPolicyManager { * @hide */ public static final int RULE_REJECT_ALL = 1 << 6; + /** * Mask used to get the {@code RULE_xxx_METERED} rules * @hide @@ -133,7 +138,6 @@ public class NetworkPolicyManager { /** @hide */ public static final int FIREWALL_RULE_DEFAULT = 0; - /** @hide */ public static final String FIREWALL_CHAIN_NAME_NONE = "none"; /** @hide */ @@ -180,6 +184,9 @@ public class NetworkPolicyManager { @UnsupportedAppUsage private INetworkPolicyManager mService; + private final Map<SubscriptionCallback, SubscriptionCallbackProxy> + mCallbackMap = new ConcurrentHashMap<>(); + /** @hide */ public NetworkPolicyManager(Context context, INetworkPolicyManager service) { if (service == null) { @@ -286,6 +293,35 @@ public class NetworkPolicyManager { } /** @hide */ + @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) + @SystemApi + public void registerSubscriptionCallback(@NonNull SubscriptionCallback callback) { + if (callback == null) { + throw new NullPointerException("Callback cannot be null."); + } + + final SubscriptionCallbackProxy callbackProxy = new SubscriptionCallbackProxy(callback); + if (null != mCallbackMap.putIfAbsent(callback, callbackProxy)) { + throw new IllegalArgumentException("Callback is already registered."); + } + registerListener(callbackProxy); + } + + /** @hide */ + @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) + @SystemApi + public void unregisterSubscriptionCallback(@NonNull SubscriptionCallback callback) { + if (callback == null) { + throw new NullPointerException("Callback cannot be null."); + } + + final SubscriptionCallbackProxy callbackProxy = mCallbackMap.remove(callback); + if (callbackProxy == null) return; + + unregisterListener(callbackProxy); + } + + /** @hide */ public void setNetworkPolicies(NetworkPolicy[] policies) { try { mService.setNetworkPolicies(policies); @@ -512,6 +548,51 @@ public class NetworkPolicyManager { return WifiInfo.sanitizeSsid(ssid); } + /** @hide */ + @SystemApi + public static class SubscriptionCallback { + /** + * Notify clients of a new override about a given subscription. + * + * @param subId the subscriber this override applies to. + * @param overrideMask a bitmask that specifies which of the overrides is set. + * @param overrideValue a bitmask that specifies the override values. + */ + public void onSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask, + @SubscriptionOverrideMask int overrideValue) {} + + /** + * Notify of subscription plans change about a given subscription. + * + * @param subId the subscriber id that got subscription plans change. + * @param plans the list of subscription plans. + */ + public void onSubscriptionPlansChanged(int subId, @NonNull SubscriptionPlan[] plans) {} + } + + /** + * SubscriptionCallback proxy for SubscriptionCallback object. + * @hide + */ + public class SubscriptionCallbackProxy extends Listener { + private final SubscriptionCallback mCallback; + + SubscriptionCallbackProxy(SubscriptionCallback callback) { + mCallback = callback; + } + + @Override + public void onSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask, + @SubscriptionOverrideMask int overrideValue) { + mCallback.onSubscriptionOverride(subId, overrideMask, overrideValue); + } + + @Override + public void onSubscriptionPlansChanged(int subId, SubscriptionPlan[] plans) { + mCallback.onSubscriptionPlansChanged(subId, plans); + } + } + /** {@hide} */ public static class Listener extends INetworkPolicyListener.Stub { @Override public void onUidRulesChanged(int uid, int uidRules) { } diff --git a/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java b/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java index 6b90588f8d25..31645672f049 100644 --- a/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java +++ b/core/java/android/view/textclassifier/ActionsModelParamsSupplier.java @@ -60,7 +60,9 @@ public final class ActionsModelParamsSupplier implements private boolean mParsed = true; public ActionsModelParamsSupplier(Context context, @Nullable Runnable onChangedListener) { - mAppContext = Preconditions.checkNotNull(context).getApplicationContext(); + final Context appContext = Preconditions.checkNotNull(context).getApplicationContext(); + // Some contexts don't have an app context. + mAppContext = appContext != null ? appContext : context; mOnChangedListener = onChangedListener == null ? () -> {} : onChangedListener; mSettingsObserver = new SettingsObserver(mAppContext, () -> { synchronized (mLock) { diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java index aeb99b896b11..7c527bae475d 100644 --- a/core/java/android/view/textclassifier/ConversationActions.java +++ b/core/java/android/view/textclassifier/ConversationActions.java @@ -21,10 +21,12 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; +import android.annotation.UserIdInt; import android.app.Person; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import android.text.SpannedString; import com.android.internal.annotations.VisibleForTesting; @@ -316,6 +318,8 @@ public final class ConversationActions implements Parcelable { private final List<String> mHints; @Nullable private String mCallingPackageName; + @UserIdInt + private int mUserId = UserHandle.USER_NULL; @NonNull private Bundle mExtras; @@ -340,6 +344,7 @@ public final class ConversationActions implements Parcelable { List<String> hints = new ArrayList<>(); in.readStringList(hints); String callingPackageName = in.readString(); + int userId = in.readInt(); Bundle extras = in.readBundle(); Request request = new Request( conversation, @@ -348,6 +353,7 @@ public final class ConversationActions implements Parcelable { hints, extras); request.setCallingPackageName(callingPackageName); + request.setUserId(userId); return request; } @@ -358,6 +364,7 @@ public final class ConversationActions implements Parcelable { parcel.writeInt(mMaxSuggestions); parcel.writeStringList(mHints); parcel.writeString(mCallingPackageName); + parcel.writeInt(mUserId); parcel.writeBundle(mExtras); } @@ -428,6 +435,24 @@ public final class ConversationActions implements Parcelable { } /** + * Sets the id of the user that sent this request. + * <p> + * Package-private for SystemTextClassifier's use. + */ + void setUserId(@UserIdInt int userId) { + mUserId = userId; + } + + /** + * Returns the id of the user that sent this request. + * @hide + */ + @UserIdInt + public int getUserId() { + return mUserId; + } + + /** * Returns the extended data related to this request. * * <p><b>NOTE: </b>Do not modify this bundle. diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java index 9ae0c65e0cff..ae9d5c6520f7 100644 --- a/core/java/android/view/textclassifier/SelectionEvent.java +++ b/core/java/android/view/textclassifier/SelectionEvent.java @@ -19,8 +19,10 @@ package android.view.textclassifier; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import android.view.textclassifier.TextClassifier.EntityType; import android.view.textclassifier.TextClassifier.WidgetType; @@ -127,6 +129,7 @@ public final class SelectionEvent implements Parcelable { private String mWidgetType = TextClassifier.WIDGET_TYPE_UNKNOWN; private @InvocationMethod int mInvocationMethod; @Nullable private String mWidgetVersion; + private @UserIdInt int mUserId = UserHandle.USER_NULL; @Nullable private String mResultId; private long mEventTime; private long mDurationSinceSessionStart; @@ -171,6 +174,7 @@ public final class SelectionEvent implements Parcelable { mEnd = in.readInt(); mSmartStart = in.readInt(); mSmartEnd = in.readInt(); + mUserId = in.readInt(); } @Override @@ -199,6 +203,7 @@ public final class SelectionEvent implements Parcelable { dest.writeInt(mEnd); dest.writeInt(mSmartStart); dest.writeInt(mSmartEnd); + dest.writeInt(mUserId); } @Override @@ -401,6 +406,24 @@ public final class SelectionEvent implements Parcelable { } /** + * Sets the id of this event's user. + * <p> + * Package-private for SystemTextClassifier's use. + */ + void setUserId(@UserIdInt int userId) { + mUserId = userId; + } + + /** + * Returns the id of this event's user. + * @hide + */ + @UserIdInt + public int getUserId() { + return mUserId; + } + + /** * Returns the type of widget that was involved in triggering this event. */ @WidgetType @@ -426,6 +449,7 @@ public final class SelectionEvent implements Parcelable { mPackageName = context.getPackageName(); mWidgetType = context.getWidgetType(); mWidgetVersion = context.getWidgetVersion(); + mUserId = context.getUserId(); } /** @@ -612,7 +636,7 @@ public final class SelectionEvent implements Parcelable { @Override public int hashCode() { return Objects.hash(mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType, - mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod, mResultId, + mWidgetVersion, mPackageName, mUserId, mWidgetType, mInvocationMethod, mResultId, mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent, mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd); } @@ -633,6 +657,7 @@ public final class SelectionEvent implements Parcelable { && Objects.equals(mEntityType, other.mEntityType) && Objects.equals(mWidgetVersion, other.mWidgetVersion) && Objects.equals(mPackageName, other.mPackageName) + && mUserId == other.mUserId && Objects.equals(mWidgetType, other.mWidgetType) && mInvocationMethod == other.mInvocationMethod && Objects.equals(mResultId, other.mResultId) @@ -652,12 +677,12 @@ public final class SelectionEvent implements Parcelable { return String.format(Locale.US, "SelectionEvent {absoluteStart=%d, absoluteEnd=%d, eventType=%d, entityType=%s, " + "widgetVersion=%s, packageName=%s, widgetType=%s, invocationMethod=%s, " - + "resultId=%s, eventTime=%d, durationSinceSessionStart=%d, " + + "userId=%d, resultId=%s, eventTime=%d, durationSinceSessionStart=%d, " + "durationSincePreviousEvent=%d, eventIndex=%d," + "sessionId=%s, start=%d, end=%d, smartStart=%d, smartEnd=%d}", mAbsoluteStart, mAbsoluteEnd, mEventType, mEntityType, mWidgetVersion, mPackageName, mWidgetType, mInvocationMethod, - mResultId, mEventTime, mDurationSinceSessionStart, + mUserId, mResultId, mEventTime, mDurationSinceSessionStart, mDurationSincePreviousEvent, mEventIndex, mSessionId, mStart, mEnd, mSmartStart, mSmartEnd); } diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java index 8f8766e3f783..a97c3305208a 100644 --- a/core/java/android/view/textclassifier/SystemTextClassifier.java +++ b/core/java/android/view/textclassifier/SystemTextClassifier.java @@ -18,6 +18,7 @@ package android.view.textclassifier; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.annotation.WorkerThread; import android.content.Context; import android.os.Bundle; @@ -50,6 +51,10 @@ public final class SystemTextClassifier implements TextClassifier { private final TextClassificationConstants mSettings; private final TextClassifier mFallback; private final String mPackageName; + // NOTE: Always set this before sending a request to the manager service otherwise the manager + // service will throw a remote exception. + @UserIdInt + private final int mUserId; private TextClassificationSessionId mSessionId; public SystemTextClassifier(Context context, TextClassificationConstants settings) @@ -60,6 +65,7 @@ public final class SystemTextClassifier implements TextClassifier { mFallback = context.getSystemService(TextClassificationManager.class) .getTextClassifier(TextClassifier.LOCAL); mPackageName = Preconditions.checkNotNull(context.getOpPackageName()); + mUserId = context.getUserId(); } /** @@ -72,6 +78,7 @@ public final class SystemTextClassifier implements TextClassifier { Utils.checkMainThread(); try { request.setCallingPackageName(mPackageName); + request.setUserId(mUserId); final BlockingCallback<TextSelection> callback = new BlockingCallback<>("textselection"); mManagerService.onSuggestSelection(mSessionId, request, callback); @@ -95,6 +102,7 @@ public final class SystemTextClassifier implements TextClassifier { Utils.checkMainThread(); try { request.setCallingPackageName(mPackageName); + request.setUserId(mUserId); final BlockingCallback<TextClassification> callback = new BlockingCallback<>("textclassification"); mManagerService.onClassifyText(mSessionId, request, callback); @@ -123,6 +131,7 @@ public final class SystemTextClassifier implements TextClassifier { try { request.setCallingPackageName(mPackageName); + request.setUserId(mUserId); final BlockingCallback<TextLinks> callback = new BlockingCallback<>("textlinks"); mManagerService.onGenerateLinks(mSessionId, request, callback); @@ -142,6 +151,7 @@ public final class SystemTextClassifier implements TextClassifier { Utils.checkMainThread(); try { + event.setUserId(mUserId); mManagerService.onSelectionEvent(mSessionId, event); } catch (RemoteException e) { Log.e(LOG_TAG, "Error reporting selection event.", e); @@ -154,6 +164,12 @@ public final class SystemTextClassifier implements TextClassifier { Utils.checkMainThread(); try { + final TextClassificationContext tcContext = event.getEventContext() == null + ? new TextClassificationContext.Builder(mPackageName, WIDGET_TYPE_UNKNOWN) + .build() + : event.getEventContext(); + tcContext.setUserId(mUserId); + event.setEventContext(tcContext); mManagerService.onTextClassifierEvent(mSessionId, event); } catch (RemoteException e) { Log.e(LOG_TAG, "Error reporting textclassifier event.", e); @@ -167,6 +183,7 @@ public final class SystemTextClassifier implements TextClassifier { try { request.setCallingPackageName(mPackageName); + request.setUserId(mUserId); final BlockingCallback<TextLanguage> callback = new BlockingCallback<>("textlanguage"); mManagerService.onDetectLanguage(mSessionId, request, callback); @@ -187,6 +204,7 @@ public final class SystemTextClassifier implements TextClassifier { try { request.setCallingPackageName(mPackageName); + request.setUserId(mUserId); final BlockingCallback<ConversationActions> callback = new BlockingCallback<>("conversation-actions"); mManagerService.onSuggestConversationActions(mSessionId, request, callback); @@ -228,6 +246,7 @@ public final class SystemTextClassifier implements TextClassifier { printWriter.printPair("mFallback", mFallback); printWriter.printPair("mPackageName", mPackageName); printWriter.printPair("mSessionId", mSessionId); + printWriter.printPair("mUserId", mUserId); printWriter.decreaseIndent(); printWriter.println(); } @@ -243,6 +262,7 @@ public final class SystemTextClassifier implements TextClassifier { @NonNull TextClassificationSessionId sessionId) { mSessionId = Preconditions.checkNotNull(sessionId); try { + classificationContext.setUserId(mUserId); mManagerService.onCreateTextClassificationSession(classificationContext, mSessionId); } catch (RemoteException e) { Log.e(LOG_TAG, "Error starting a new classification session.", e); diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java index 63210516b3e1..975f3ba0763a 100644 --- a/core/java/android/view/textclassifier/TextClassification.java +++ b/core/java/android/view/textclassifier/TextClassification.java @@ -21,6 +21,7 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.PendingIntent; import android.app.RemoteAction; import android.content.Context; @@ -35,6 +36,7 @@ import android.os.Bundle; import android.os.LocaleList; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import android.text.SpannedString; import android.util.ArrayMap; import android.view.View.OnClickListener; @@ -551,6 +553,8 @@ public final class TextClassification implements Parcelable { @Nullable private final ZonedDateTime mReferenceTime; @NonNull private final Bundle mExtras; @Nullable private String mCallingPackageName; + @UserIdInt + private int mUserId = UserHandle.USER_NULL; private Request( CharSequence text, @@ -631,6 +635,24 @@ public final class TextClassification implements Parcelable { } /** + * Sets the id of the user that sent this request. + * <p> + * Package-private for SystemTextClassifier's use. + */ + void setUserId(@UserIdInt int userId) { + mUserId = userId; + } + + /** + * Returns the id of the user that sent this request. + * @hide + */ + @UserIdInt + public int getUserId() { + return mUserId; + } + + /** * Returns the extended data. * * <p><b>NOTE: </b>Do not modify this bundle. @@ -730,6 +752,7 @@ public final class TextClassification implements Parcelable { dest.writeParcelable(mDefaultLocales, flags); dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString()); dest.writeString(mCallingPackageName); + dest.writeInt(mUserId); dest.writeBundle(mExtras); } @@ -742,11 +765,13 @@ public final class TextClassification implements Parcelable { final ZonedDateTime referenceTime = referenceTimeString == null ? null : ZonedDateTime.parse(referenceTimeString); final String callingPackageName = in.readString(); + final int userId = in.readInt(); final Bundle extras = in.readBundle(); final Request request = new Request(text, startIndex, endIndex, defaultLocales, referenceTime, extras); request.setCallingPackageName(callingPackageName); + request.setUserId(userId); return request; } diff --git a/core/java/android/view/textclassifier/TextClassificationContext.java b/core/java/android/view/textclassifier/TextClassificationContext.java index 3bf8e9bd2bf0..db07685fe3ae 100644 --- a/core/java/android/view/textclassifier/TextClassificationContext.java +++ b/core/java/android/view/textclassifier/TextClassificationContext.java @@ -18,8 +18,10 @@ package android.view.textclassifier; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import android.view.textclassifier.TextClassifier.WidgetType; import com.android.internal.util.Preconditions; @@ -35,6 +37,8 @@ public final class TextClassificationContext implements Parcelable { private final String mPackageName; private final String mWidgetType; @Nullable private final String mWidgetVersion; + @UserIdInt + private int mUserId = UserHandle.USER_NULL; private TextClassificationContext( String packageName, @@ -54,6 +58,24 @@ public final class TextClassificationContext implements Parcelable { } /** + * Sets the id of this context's user. + * <p> + * Package-private for SystemTextClassifier's use. + */ + void setUserId(@UserIdInt int userId) { + mUserId = userId; + } + + /** + * Returns the id of this context's user. + * @hide + */ + @UserIdInt + public int getUserId() { + return mUserId; + } + + /** * Returns the widget type for this classification context. */ @NonNull @@ -75,8 +97,8 @@ public final class TextClassificationContext implements Parcelable { @Override public String toString() { return String.format(Locale.US, "TextClassificationContext{" - + "packageName=%s, widgetType=%s, widgetVersion=%s}", - mPackageName, mWidgetType, mWidgetVersion); + + "packageName=%s, widgetType=%s, widgetVersion=%s, userId=%d}", + mPackageName, mWidgetType, mWidgetVersion, mUserId); } /** @@ -133,12 +155,14 @@ public final class TextClassificationContext implements Parcelable { parcel.writeString(mPackageName); parcel.writeString(mWidgetType); parcel.writeString(mWidgetVersion); + parcel.writeInt(mUserId); } private TextClassificationContext(Parcel in) { mPackageName = in.readString(); mWidgetType = in.readString(); mWidgetVersion = in.readString(); + mUserId = in.readInt(); } public static final @android.annotation.NonNull Parcelable.Creator<TextClassificationContext> CREATOR = diff --git a/core/java/android/view/textclassifier/TextClassifierEvent.java b/core/java/android/view/textclassifier/TextClassifierEvent.java index 57da829b3f44..a041296f97db 100644 --- a/core/java/android/view/textclassifier/TextClassifierEvent.java +++ b/core/java/android/view/textclassifier/TextClassifierEvent.java @@ -139,7 +139,7 @@ public abstract class TextClassifierEvent implements Parcelable { @Nullable private final String[] mEntityTypes; @Nullable - private final TextClassificationContext mEventContext; + private TextClassificationContext mEventContext; @Nullable private final String mResultId; private final int mEventIndex; @@ -289,6 +289,15 @@ public abstract class TextClassifierEvent implements Parcelable { } /** + * Sets the event context. + * <p> + * Package-private for SystemTextClassifier's use. + */ + void setEventContext(@Nullable TextClassificationContext eventContext) { + mEventContext = eventContext; + } + + /** * Returns the id of the text classifier result related to this event. */ @Nullable diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java index 6c75ffbea0cd..3e3dc72bd677 100644 --- a/core/java/android/view/textclassifier/TextLanguage.java +++ b/core/java/android/view/textclassifier/TextLanguage.java @@ -20,10 +20,12 @@ import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.icu.util.ULocale; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import android.util.ArrayMap; import com.android.internal.annotations.VisibleForTesting; @@ -226,6 +228,8 @@ public final class TextLanguage implements Parcelable { private final CharSequence mText; private final Bundle mExtra; @Nullable private String mCallingPackageName; + @UserIdInt + private int mUserId = UserHandle.USER_NULL; private Request(CharSequence text, Bundle bundle) { mText = text; @@ -260,6 +264,24 @@ public final class TextLanguage implements Parcelable { } /** + * Sets the id of the user that sent this request. + * <p> + * Package-private for SystemTextClassifier's use. + */ + void setUserId(@UserIdInt int userId) { + mUserId = userId; + } + + /** + * Returns the id of the user that sent this request. + * @hide + */ + @UserIdInt + public int getUserId() { + return mUserId; + } + + /** * Returns a bundle containing non-structured extra information about this request. * * <p><b>NOTE: </b>Do not modify this bundle. @@ -278,16 +300,19 @@ public final class TextLanguage implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeCharSequence(mText); dest.writeString(mCallingPackageName); + dest.writeInt(mUserId); dest.writeBundle(mExtra); } private static Request readFromParcel(Parcel in) { final CharSequence text = in.readCharSequence(); final String callingPackageName = in.readString(); + final int userId = in.readInt(); final Bundle extra = in.readBundle(); final Request request = new Request(text, extra); request.setCallingPackageName(callingPackageName); + request.setUserId(userId); return request; } diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java index f3e0dc1ed843..d7ac52414923 100644 --- a/core/java/android/view/textclassifier/TextLinks.java +++ b/core/java/android/view/textclassifier/TextLinks.java @@ -20,11 +20,13 @@ import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.content.Context; import android.os.Bundle; import android.os.LocaleList; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import android.text.Spannable; import android.text.method.MovementMethod; import android.text.style.ClickableSpan; @@ -339,6 +341,8 @@ public final class TextLinks implements Parcelable { private final boolean mLegacyFallback; @Nullable private String mCallingPackageName; private final Bundle mExtras; + @UserIdInt + private int mUserId = UserHandle.USER_NULL; private Request( CharSequence text, @@ -410,6 +414,24 @@ public final class TextLinks implements Parcelable { } /** + * Sets the id of the user that sent this request. + * <p> + * Package-private for SystemTextClassifier's use. + */ + void setUserId(@UserIdInt int userId) { + mUserId = userId; + } + + /** + * Returns the id of the user that sent this request. + * @hide + */ + @UserIdInt + public int getUserId() { + return mUserId; + } + + /** * Returns the extended data. * * <p><b>NOTE: </b>Do not modify this bundle. @@ -509,6 +531,7 @@ public final class TextLinks implements Parcelable { dest.writeParcelable(mDefaultLocales, flags); dest.writeParcelable(mEntityConfig, flags); dest.writeString(mCallingPackageName); + dest.writeInt(mUserId); dest.writeBundle(mExtras); } @@ -517,11 +540,13 @@ public final class TextLinks implements Parcelable { final LocaleList defaultLocales = in.readParcelable(null); final EntityConfig entityConfig = in.readParcelable(null); final String callingPackageName = in.readString(); + final int userId = in.readInt(); final Bundle extras = in.readBundle(); final Request request = new Request(text, defaultLocales, entityConfig, /* legacyFallback= */ true, extras); request.setCallingPackageName(callingPackageName); + request.setUserId(userId); return request; } diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java index 75c27bdbc1d5..94e0bc3dcd67 100644 --- a/core/java/android/view/textclassifier/TextSelection.java +++ b/core/java/android/view/textclassifier/TextSelection.java @@ -20,10 +20,12 @@ import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.os.Bundle; import android.os.LocaleList; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import android.text.SpannedString; import android.util.ArrayMap; import android.view.textclassifier.TextClassifier.EntityType; @@ -211,6 +213,8 @@ public final class TextSelection implements Parcelable { private final boolean mDarkLaunchAllowed; private final Bundle mExtras; @Nullable private String mCallingPackageName; + @UserIdInt + private int mUserId = UserHandle.USER_NULL; private Request( CharSequence text, @@ -292,6 +296,24 @@ public final class TextSelection implements Parcelable { } /** + * Sets the id of the user that sent this request. + * <p> + * Package-private for SystemTextClassifier's use. + */ + void setUserId(@UserIdInt int userId) { + mUserId = userId; + } + + /** + * Returns the id of the user that sent this request. + * @hide + */ + @UserIdInt + public int getUserId() { + return mUserId; + } + + /** * Returns the extended data. * * <p><b>NOTE: </b>Do not modify this bundle. @@ -394,6 +416,7 @@ public final class TextSelection implements Parcelable { dest.writeInt(mEndIndex); dest.writeParcelable(mDefaultLocales, flags); dest.writeString(mCallingPackageName); + dest.writeInt(mUserId); dest.writeBundle(mExtras); } @@ -403,11 +426,13 @@ public final class TextSelection implements Parcelable { final int endIndex = in.readInt(); final LocaleList defaultLocales = in.readParcelable(null); final String callingPackageName = in.readString(); + final int userId = in.readInt(); final Bundle extras = in.readBundle(); final Request request = new Request(text, startIndex, endIndex, defaultLocales, /* darkLaunchAllowed= */ false, extras); request.setCallingPackageName(callingPackageName); + request.setUserId(userId); return request; } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 6f697cd65686..65711db01e36 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -11283,6 +11283,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Nullable + final TextClassificationManager getTextClassificationManagerForUser() { + return getServiceManagerForUser( + getContext().getPackageName(), TextClassificationManager.class); + } + + @Nullable final <T> T getServiceManagerForUser(String packageName, Class<T> managerClazz) { if (mTextOperationUser == null) { return getContext().getSystemService(managerClazz); @@ -12383,8 +12389,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @NonNull public TextClassifier getTextClassifier() { if (mTextClassifier == null) { - final TextClassificationManager tcm = - mContext.getSystemService(TextClassificationManager.class); + final TextClassificationManager tcm = getTextClassificationManagerForUser(); if (tcm != null) { return tcm.getTextClassifier(); } @@ -12400,8 +12405,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @NonNull TextClassifier getTextClassificationSession() { if (mTextClassificationSession == null || mTextClassificationSession.isDestroyed()) { - final TextClassificationManager tcm = - mContext.getSystemService(TextClassificationManager.class); + final TextClassificationManager tcm = getTextClassificationManagerForUser(); if (tcm != null) { final String widgetType; if (isTextEditable()) { diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 9a27e719e5d9..0aa0c5afc315 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -672,6 +672,8 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX]; char foregroundHeapGrowthMultiplierOptsBuf[ sizeof("-XX:ForegroundHeapGrowthMultiplier=")-1 + PROPERTY_VALUE_MAX]; + char finalizerTimeoutMsOptsBuf[sizeof("-XX:FinalizerTimeoutMs=")-1 + PROPERTY_VALUE_MAX]; + char threadSuspendTimeoutOptsBuf[sizeof("-XX:ThreadSuspendTimeout=")-1 + PROPERTY_VALUE_MAX]; char cachePruneBuf[sizeof("-Xzygote-max-boot-retry=")-1 + PROPERTY_VALUE_MAX]; char dex2oatXmsImageFlagsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX]; char dex2oatXmxImageFlagsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX]; @@ -825,7 +827,15 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p parseRuntimeOption("dalvik.vm.foreground-heap-growth-multiplier", foregroundHeapGrowthMultiplierOptsBuf, "-XX:ForegroundHeapGrowthMultiplier="); - + /* + * Finalizer and thread suspend timeouts. + */ + parseRuntimeOption("dalvik.vm.finalizer-timeout-ms", + finalizerTimeoutMsOptsBuf, + "-XX:FinalizerTimeoutMs="); + parseRuntimeOption("dalvik.vm.thread-suspend-timeout-ms", + threadSuspendTimeoutOptsBuf, + "-XX:ThreadSuspendTimeout="); /* * JIT related options. */ diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index e7539ba08dfb..f482a7f455be 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3647,6 +3647,13 @@ <permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE" android:protectionLevel="signature" /> + <!-- Allows an application to control the lights on the device. + @hide + @SystemApi + @TestApi --> + <permission android:name="android.permission.CONTROL_DEVICE_LIGHTS" + android:protectionLevel="signature|privileged" /> + <!-- Allows an application to control the color saturation of the display. @hide @SystemApi --> diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index b41792710cbf..aea7f333183f 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -1345,7 +1345,9 @@ public class ExifInterface { private ByteOrder mExifByteOrder = ByteOrder.BIG_ENDIAN; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private boolean mHasThumbnail; - // The following values used for indicating a thumbnail position. + private boolean mHasThumbnailStrips; + private boolean mAreThumbnailStripsConsecutive; + // Used to indicate the position of the thumbnail (includes offset to EXIF data segment). private int mThumbnailOffset; private int mThumbnailLength; private byte[] mThumbnailBytes; @@ -2043,10 +2045,12 @@ public class ExifInterface { /** * Returns the offset and length of thumbnail inside the image file, or - * {@code null} if there is no thumbnail. + * {@code null} if either there is no thumbnail or the thumbnail bytes are stored + * non-consecutively. * * @return two-element array, the offset in the first value, and length in - * the second, or {@code null} if no thumbnail was found. + * the second, or {@code null} if no thumbnail was found or the thumbnail strips are + * not placed consecutively. * @throws IllegalStateException if {@link #saveAttributes()} has been * called since the underlying file was initially parsed, since * that means offsets may have changed. @@ -2058,10 +2062,12 @@ public class ExifInterface { } if (mHasThumbnail) { + if (mHasThumbnailStrips && !mAreThumbnailStripsConsecutive) { + return null; + } return new long[] { mThumbnailOffset, mThumbnailLength }; - } else { - return null; } + return null; } /** @@ -2536,10 +2542,9 @@ public class ExifInterface { final byte[] value = Arrays.copyOfRange(bytes, IDENTIFIER_EXIF_APP1.length, bytes.length); - readExifSegment(value, imageType); - // Save offset values for createJpegThumbnailBitmap() function mExifOffset = (int) offset; + readExifSegment(value, imageType); } else if (ArrayUtils.startsWith(bytes, IDENTIFIER_XMP_APP1)) { // See XMP Specification Part 3: Storage in Files, 1.1.3 JPEG, Table 6 final long offset = start + IDENTIFIER_XMP_APP1.length; @@ -2843,6 +2848,8 @@ public class ExifInterface { if (in.read(bytes) != length) { throw new IOException("Can't read exif"); } + // Save offset values for handling thumbnail and attribute offsets. + mExifOffset = offset; readExifSegment(bytes, IFD_TYPE_PRIMARY); } @@ -2988,7 +2995,7 @@ public class ExifInterface { // Write EXIF APP1 segment dataOutputStream.writeByte(MARKER); dataOutputStream.writeByte(MARKER_APP1); - writeExifSegment(dataOutputStream, 6); + writeExifSegment(dataOutputStream); byte[] bytes = new byte[4096]; @@ -3319,7 +3326,7 @@ public class ExifInterface { continue; } - final int bytesOffset = dataInputStream.peek(); + final int bytesOffset = dataInputStream.peek() + mExifOffset; final byte[] bytes = new byte[(int) byteCount]; dataInputStream.readFully(bytes); ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, @@ -3451,31 +3458,28 @@ public class ExifInterface { // The following code limits the size of thumbnail size not to overflow EXIF data area. thumbnailLength = Math.min(thumbnailLength, in.getLength() - thumbnailOffset); - if (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_RAF - || mMimeType == IMAGE_TYPE_RW2) { - thumbnailOffset += mExifOffset; - } else if (mMimeType == IMAGE_TYPE_ORF) { + if (mMimeType == IMAGE_TYPE_ORF) { // Update offset value since RAF files have IFD data preceding MakerNote data. thumbnailOffset += mOrfMakerNoteOffset; } - if (DEBUG) { - Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset - + ", length: " + thumbnailLength); - } if (thumbnailOffset > 0 && thumbnailLength > 0) { mHasThumbnail = true; - mThumbnailOffset = thumbnailOffset; + mThumbnailOffset = thumbnailOffset + mExifOffset; mThumbnailLength = thumbnailLength; mThumbnailCompression = DATA_JPEG; if (mFilename == null && mAssetInputStream == null && mSeekableFileDescriptor == null) { // Save the thumbnail in memory if the input doesn't support reading again. - byte[] thumbnailBytes = new byte[thumbnailLength]; - in.seek(thumbnailOffset); + byte[] thumbnailBytes = new byte[mThumbnailLength]; + in.seek(mThumbnailOffset); in.readFully(thumbnailBytes); mThumbnailBytes = thumbnailBytes; } + if (DEBUG) { + Log.d(TAG, "Setting thumbnail attributes with offset: " + thumbnailOffset + + ", length: " + thumbnailLength); + } } } } @@ -3494,12 +3498,16 @@ public class ExifInterface { long[] stripByteCounts = convertToLongArray(stripByteCountsAttribute.getValue(mExifByteOrder)); - if (stripOffsets == null) { - Log.w(TAG, "stripOffsets should not be null."); + if (stripOffsets == null || stripOffsets.length == 0) { + Log.w(TAG, "stripOffsets should not be null or have zero length."); return; } - if (stripByteCounts == null) { - Log.w(TAG, "stripByteCounts should not be null."); + if (stripByteCounts == null || stripByteCounts.length == 0) { + Log.w(TAG, "stripByteCounts should not be null or have zero length."); + return; + } + if (stripOffsets.length != stripByteCounts.length) { + Log.w(TAG, "stripOffsets and stripByteCounts should have same length."); return; } @@ -3509,10 +3517,18 @@ public class ExifInterface { int bytesRead = 0; int bytesAdded = 0; + mHasThumbnail = mHasThumbnailStrips = mAreThumbnailStripsConsecutive = true; for (int i = 0; i < stripOffsets.length; i++) { int stripOffset = (int) stripOffsets[i]; int stripByteCount = (int) stripByteCounts[i]; + // Check if strips are consecutive + // TODO: Add test for non-consecutive thumbnail image + if (i < stripOffsets.length - 1 + && stripOffset + stripByteCount != stripOffsets[i + 1]) { + mAreThumbnailStripsConsecutive = false; + } + // Skip to offset int skipBytes = stripOffset - bytesRead; if (skipBytes < 0) { @@ -3531,10 +3547,13 @@ public class ExifInterface { stripBytes.length); bytesAdded += stripBytes.length; } - - mHasThumbnail = true; mThumbnailBytes = totalStripBytes; - mThumbnailLength = totalStripBytes.length; + + if (mAreThumbnailStripsConsecutive) { + // Need to add mExifOffset, which is the offset to the EXIF data segment + mThumbnailOffset = (int) stripOffsets[0] + mExifOffset; + mThumbnailLength = totalStripBytes.length; + } } } @@ -3691,8 +3710,7 @@ public class ExifInterface { } // Writes an Exif segment into the given output stream. - private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream, - int exifOffsetFromBeginning) throws IOException { + private int writeExifSegment(ByteOrderedDataOutputStream dataOutputStream) throws IOException { // The following variables are for calculating each IFD tag group size in bytes. int[] ifdOffsets = new int[EXIF_TAGS.length]; int[] ifdDataSizes = new int[EXIF_TAGS.length]; @@ -3751,6 +3769,8 @@ public class ExifInterface { } // Calculate IFD offsets. + // 8 bytes are for TIFF headers: 2 bytes (byte order) + 2 bytes (identifier) + 4 bytes + // (offset of IFDs) int position = 8; for (int ifdType = 0; ifdType < EXIF_TAGS.length; ++ifdType) { if (!mAttributes[ifdType].isEmpty()) { @@ -3762,7 +3782,8 @@ public class ExifInterface { int thumbnailOffset = position; mAttributes[IFD_TYPE_THUMBNAIL].put(JPEG_INTERCHANGE_FORMAT_TAG.name, ExifAttribute.createULong(thumbnailOffset, mExifByteOrder)); - mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset; + // Need to add mExifOffset, which is the offset to the EXIF data segment + mThumbnailOffset = thumbnailOffset + mExifOffset; position += mThumbnailLength; } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java index a2bd210b67a6..1ebe91736ba1 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java @@ -16,6 +16,10 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_AUDIO; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; @@ -42,6 +46,7 @@ public class A2dpProfile implements LocalBluetoothProfile { private boolean mIsProfileReady; private final CachedBluetoothDeviceManager mDeviceManager; + private final BluetoothAdapter mBluetoothAdapter; static final ParcelUuid[] SINK_UUIDS = { BluetoothUuid.A2DP_SINK, @@ -96,7 +101,8 @@ public class A2dpProfile implements LocalBluetoothProfile { mContext = context; mDeviceManager = deviceManager; mProfileManager = profileManager; - BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, new A2dpServiceListener(), + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + mBluetoothAdapter.getProfileProxy(context, new A2dpServiceListener(), BluetoothProfile.A2DP); } @@ -147,20 +153,6 @@ public class A2dpProfile implements LocalBluetoothProfile { return mService.getDevicesMatchingConnectionStates(states); } - public boolean connect(BluetoothDevice device) { - if (mService == null) return false; - return mService.connect(device); - } - - public boolean disconnect(BluetoothDevice device) { - if (mService == null) return false; - // Downgrade priority as user is disconnecting the headset. - if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); - } - return mService.disconnect(device); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -169,8 +161,10 @@ public class A2dpProfile implements LocalBluetoothProfile { } public boolean setActiveDevice(BluetoothDevice device) { - if (mService == null) return false; - return mService.setActiveDevice(device); + if (mBluetoothAdapter == null) { + return false; + } + return mBluetoothAdapter.setActiveDevice(device, ACTIVE_DEVICE_AUDIO); } public BluetoothDevice getActiveDevice() { @@ -178,31 +172,37 @@ public class A2dpProfile implements LocalBluetoothProfile { return mService.getActiveDevice(); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { if (mService == null) { return false; } - return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { if (mService == null) { - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } - public void setPreferred(BluetoothDevice device, boolean preferred) { + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; if (mService == null) { - return; + return false; } - if (preferred) { - if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (enabled) { + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } boolean isA2dpPlaying() { if (mService == null) return false; diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java index bc03c343a909..c7a5bd86c1cd 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothA2dpSink; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; @@ -112,24 +115,6 @@ final class A2dpSinkProfile implements LocalBluetoothProfile { BluetoothProfile.STATE_DISCONNECTING}); } - public boolean connect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.connect(device); - } - - public boolean disconnect(BluetoothDevice device) { - if (mService == null) { - return false; - } - // Downgrade priority as user is disconnecting the headset. - if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); - } - return mService.disconnect(device); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -137,31 +122,37 @@ final class A2dpSinkProfile implements LocalBluetoothProfile { return mService.getConnectionState(device); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { if (mService == null) { return false; } - return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { if (mService == null) { - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } - public void setPreferred(BluetoothDevice device, boolean preferred) { + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; if (mService == null) { - return; + return false; } - if (preferred) { - if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (enabled) { + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } boolean isAudioPlaying() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 9765074a4927..69e2044ad232 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -135,7 +135,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> synchronized (mProfileLock) { if (newProfileState == BluetoothProfile.STATE_CONNECTED) { if (profile instanceof MapProfile) { - profile.setPreferred(mDevice, true); + profile.setEnabled(mDevice, true); } if (!mProfiles.contains(profile)) { mRemovedProfiles.remove(profile); @@ -148,7 +148,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } } else if (profile instanceof MapProfile && newProfileState == BluetoothProfile.STATE_DISCONNECTED) { - profile.setPreferred(mDevice, false); + profile.setEnabled(mDevice, false); } else if (mLocalNapRoleConnected && profile instanceof PanProfile && ((PanProfile) profile).isLocalRoleNap(mDevice) && newProfileState == BluetoothProfile.STATE_DISCONNECTED) { @@ -172,12 +172,12 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> PbapServerProfile PbapProfile = mProfileManager.getPbapProfile(); if (PbapProfile != null && isConnectedProfile(PbapProfile)) { - PbapProfile.disconnect(mDevice); + PbapProfile.setEnabled(mDevice, false); } } public void disconnect(LocalBluetoothProfile profile) { - if (profile.disconnect(mDevice)) { + if (profile.setEnabled(mDevice, false)) { if (BluetoothUtils.D) { Log.d(TAG, "Command sent successfully:DISCONNECT " + describe(profile)); } @@ -264,7 +264,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> if (!ensurePaired()) { return; } - if (profile.connect(mDevice)) { + if (profile.setEnabled(mDevice, true)) { if (BluetoothUtils.D) { Log.d(TAG, "Command sent successfully:CONNECT " + describe(profile)); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java index 560cb3b9b5b4..9dfc4d986745 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java @@ -16,6 +16,10 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -42,6 +46,7 @@ public class HeadsetProfile implements LocalBluetoothProfile { private final CachedBluetoothDeviceManager mDeviceManager; private final LocalBluetoothProfileManager mProfileManager; + private final BluetoothAdapter mBluetoothAdapter; static final ParcelUuid[] UUIDS = { BluetoothUuid.HSP, @@ -96,7 +101,8 @@ public class HeadsetProfile implements LocalBluetoothProfile { LocalBluetoothProfileManager profileManager) { mDeviceManager = deviceManager; mProfileManager = profileManager; - BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, new HeadsetServiceListener(), + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + mBluetoothAdapter.getProfileProxy(context, new HeadsetServiceListener(), BluetoothProfile.HEADSET); } @@ -108,24 +114,6 @@ public class HeadsetProfile implements LocalBluetoothProfile { return true; } - public boolean connect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.connect(device); - } - - public boolean disconnect(BluetoothDevice device) { - if (mService == null) { - return false; - } - // Downgrade priority as user is disconnecting the headset. - if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); - } - return mService.disconnect(device); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -134,10 +122,10 @@ public class HeadsetProfile implements LocalBluetoothProfile { } public boolean setActiveDevice(BluetoothDevice device) { - if (mService == null) { + if (mBluetoothAdapter == null) { return false; } - return mService.setActiveDevice(device); + return mBluetoothAdapter.setActiveDevice(device, ACTIVE_DEVICE_PHONE_CALL); } public BluetoothDevice getActiveDevice() { @@ -161,31 +149,37 @@ public class HeadsetProfile implements LocalBluetoothProfile { return mService.getAudioState(device); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { if (mService == null) { return false; } - return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { if (mService == null) { - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } - public void setPreferred(BluetoothDevice device, boolean preferred) { + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; if (mService == null) { - return; + return false; } - if (preferred) { - if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (enabled) { + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } public List<BluetoothDevice> getConnectedDevices() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java index b4b55f363020..a3b68b4b90b3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java @@ -16,6 +16,10 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_ALL; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -42,6 +46,7 @@ public class HearingAidProfile implements LocalBluetoothProfile { static final String NAME = "HearingAid"; private final LocalBluetoothProfileManager mProfileManager; + private final BluetoothAdapter mBluetoothAdapter; // Order of this profile in device profiles list private static final int ORDINAL = 1; @@ -94,7 +99,8 @@ public class HearingAidProfile implements LocalBluetoothProfile { mContext = context; mDeviceManager = deviceManager; mProfileManager = profileManager; - BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + mBluetoothAdapter.getProfileProxy(context, new HearingAidServiceListener(), BluetoothProfile.HEARING_AID); } @@ -145,20 +151,6 @@ public class HearingAidProfile implements LocalBluetoothProfile { return mService.getDevicesMatchingConnectionStates(states); } - public boolean connect(BluetoothDevice device) { - if (mService == null) return false; - return mService.connect(device); - } - - public boolean disconnect(BluetoothDevice device) { - if (mService == null) return false; - // Downgrade priority as user is disconnecting the hearing aid. - if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); - } - return mService.disconnect(device); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -167,8 +159,10 @@ public class HearingAidProfile implements LocalBluetoothProfile { } public boolean setActiveDevice(BluetoothDevice device) { - if (mService == null) return false; - return mService.setActiveDevice(device); + if (mBluetoothAdapter == null) { + return false; + } + return mBluetoothAdapter.setActiveDevice(device, ACTIVE_DEVICE_ALL); } public List<BluetoothDevice> getActiveDevices() { @@ -176,31 +170,37 @@ public class HearingAidProfile implements LocalBluetoothProfile { return mService.getActiveDevices(); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { if (mService == null) { return false; } - return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { if (mService == null) { - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } - public void setPreferred(BluetoothDevice device, boolean preferred) { + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; if (mService == null) { - return; + return false; } - if (preferred) { - if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (enabled) { + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } public void setVolume(int volume) { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java index a372e23654e0..66225a2bffca 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -122,26 +125,6 @@ final class HfpClientProfile implements LocalBluetoothProfile { } @Override - public boolean connect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.connect(device); - } - - @Override - public boolean disconnect(BluetoothDevice device) { - if (mService == null) { - return false; - } - // Downgrade priority as user is disconnecting the headset. - if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); - } - return mService.disconnect(device); - } - - @Override public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -150,33 +133,36 @@ final class HfpClientProfile implements LocalBluetoothProfile { } @Override - public boolean isPreferred(BluetoothDevice device) { + public boolean isEnabled(BluetoothDevice device) { if (mService == null) { return false; } - return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } @Override - public int getPreferred(BluetoothDevice device) { + public int getConnectionPolicy(BluetoothDevice device) { if (mService == null) { - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } @Override - public void setPreferred(BluetoothDevice device, boolean preferred) { + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; if (mService == null) { - return; + return false; } - if (preferred) { - if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (enabled) { + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } @Override diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java index 35600b538d4d..8a2c4f8a1230 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java @@ -16,6 +16,8 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -102,20 +104,6 @@ public class HidDeviceProfile implements LocalBluetoothProfile { } @Override - public boolean connect(BluetoothDevice device) { - // Don't invoke method in service because settings is not allowed to connect this profile. - return false; - } - - @Override - public boolean disconnect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.disconnect(device); - } - - @Override public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -124,21 +112,24 @@ public class HidDeviceProfile implements LocalBluetoothProfile { } @Override - public boolean isPreferred(BluetoothDevice device) { + public boolean isEnabled(BluetoothDevice device) { return getConnectionStatus(device) != BluetoothProfile.STATE_DISCONNECTED; } @Override - public int getPreferred(BluetoothDevice device) { + public int getConnectionPolicy(BluetoothDevice device) { return PREFERRED_VALUE; } @Override - public void setPreferred(BluetoothDevice device, boolean preferred) { + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; // if set preferred to false, then disconnect to the current device - if (!preferred) { - mService.disconnect(device); + if (!enabled) { + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } @Override diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java index 975a1e67af5b..3c24b4a095b9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -98,16 +101,6 @@ public class HidProfile implements LocalBluetoothProfile { return true; } - public boolean connect(BluetoothDevice device) { - if (mService == null) return false; - return mService.connect(device); - } - - public boolean disconnect(BluetoothDevice device) { - if (mService == null) return false; - return mService.disconnect(device); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -115,29 +108,37 @@ public class HidProfile implements LocalBluetoothProfile { return mService.getConnectionState(device); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { if (mService == null) { return false; } - return mService.getConnectionPolicy(device) != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return mService.getConnectionPolicy(device) != CONNECTION_POLICY_FORBIDDEN; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { if (mService == null) { - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } - public void setPreferred(BluetoothDevice device, boolean preferred) { - if (mService == null) return; - if (preferred) { - if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; + if (mService == null) { + return false; + } + if (enabled) { + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } public String toString() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java index 4b0ca7434f9a..f609e4311082 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java @@ -35,17 +35,26 @@ public interface LocalBluetoothProfile { */ boolean isAutoConnectable(); - boolean connect(BluetoothDevice device); - - boolean disconnect(BluetoothDevice device); - int getConnectionStatus(BluetoothDevice device); - boolean isPreferred(BluetoothDevice device); + /** + * Return {@code true} if the profile is enabled, otherwise return {@code false}. + * @param device the device to query for enable status + */ + boolean isEnabled(BluetoothDevice device); - int getPreferred(BluetoothDevice device); + /** + * Get the connection policy of the profile. + * @param device the device to query for enable status + */ + int getConnectionPolicy(BluetoothDevice device); - void setPreferred(BluetoothDevice device, boolean preferred); + /** + * Enable the profile if {@code enabled} is {@code true}, otherwise disable profile. + * @param device the device to set profile status + * @param enabled {@code true} for enable profile, otherwise disable profile. + */ + boolean setEnabled(BluetoothDevice device, boolean enabled); boolean isProfileReady(); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java index ae2acbea8e4d..c72efb7eec83 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java @@ -528,14 +528,14 @@ public class LocalBluetoothProfileManager { (mMapProfile.getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED)) { profiles.add(mMapProfile); removedProfiles.remove(mMapProfile); - mMapProfile.setPreferred(device, true); + mMapProfile.setEnabled(device, true); } if ((mPbapProfile != null) && (mPbapProfile.getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED)) { profiles.add(mPbapProfile); removedProfiles.remove(mPbapProfile); - mPbapProfile.setPreferred(device, true); + mPbapProfile.setEnabled(device, true); } if (mMapClientProfile != null) { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java index 95139a1bfab9..19cb2f59f321 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -111,24 +114,6 @@ public final class MapClientProfile implements LocalBluetoothProfile { return true; } - public boolean connect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.connect(device); - } - - public boolean disconnect(BluetoothDevice device) { - if (mService == null) { - return false; - } - // Downgrade priority as user is disconnecting. - if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); - } - return mService.disconnect(device); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -136,31 +121,37 @@ public final class MapClientProfile implements LocalBluetoothProfile { return mService.getConnectionState(device); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { if (mService == null) { return false; } - return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { if (mService == null) { - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } - public void setPreferred(BluetoothDevice device, boolean preferred) { + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; if (mService == null) { - return; + return false; } - if (preferred) { - if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (enabled) { + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } public List<BluetoothDevice> getConnectedDevices() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java index 31a0eea56b42..75c1926683ef 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -110,21 +113,6 @@ public class MapProfile implements LocalBluetoothProfile { return true; } - public boolean connect(BluetoothDevice device) { - Log.d(TAG, "connect() - should not get called"); - return false; // MAP never connects out - } - - public boolean disconnect(BluetoothDevice device) { - if (mService == null) { - return false; - } - if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); - } - return mService.disconnect(device); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -132,31 +120,37 @@ public class MapProfile implements LocalBluetoothProfile { return mService.getConnectionState(device); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { if (mService == null) { return false; } - return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { if (mService == null) { - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } - public void setPreferred(BluetoothDevice device, boolean preferred) { + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; if (mService == null) { - return; + return false; } - if (preferred) { - if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (enabled) { + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } public List<BluetoothDevice> getConnectedDevices() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java index 8e3f3edcef10..5a6e6e8b830d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java @@ -40,27 +40,23 @@ final class OppProfile implements LocalBluetoothProfile { return false; } - public boolean connect(BluetoothDevice device) { - return false; - } - - public boolean disconnect(BluetoothDevice device) { - return false; - } - public int getConnectionStatus(BluetoothDevice device) { return BluetoothProfile.STATE_DISCONNECTED; // Settings app doesn't handle OPP } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { return false; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; // Settings app doesn't handle OPP } - public void setPreferred(BluetoothDevice device, boolean preferred) { + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + return false; } public boolean isProfileReady() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java index 6638592e8be5..767df352b70f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -83,22 +86,6 @@ public class PanProfile implements LocalBluetoothProfile { return false; } - public boolean connect(BluetoothDevice device) { - if (mService == null) return false; - List<BluetoothDevice> sinks = mService.getConnectedDevices(); - if (sinks != null) { - for (BluetoothDevice sink : sinks) { - mService.disconnect(sink); - } - } - return mService.connect(device); - } - - public boolean disconnect(BluetoothDevice device) { - if (mService == null) return false; - return mService.disconnect(device); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -106,16 +93,36 @@ public class PanProfile implements LocalBluetoothProfile { return mService.getConnectionState(device); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { return true; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { return -1; } - public void setPreferred(BluetoothDevice device, boolean preferred) { - // ignore: isPreferred is always true for PAN + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; + if (mService == null) { + return false; + } + + if (enabled) { + final List<BluetoothDevice> sinks = mService.getConnectedDevices(); + if (sinks != null) { + for (BluetoothDevice sink : sinks) { + mService.setConnectionPolicy(sink, CONNECTION_POLICY_FORBIDDEN); + } + } + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); + } else { + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); + } + + return isEnabled; } public String toString() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java index 4ea0df621bea..0d11293a01b7 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -123,23 +126,6 @@ public final class PbapClientProfile implements LocalBluetoothProfile { BluetoothProfile.STATE_DISCONNECTING}); } - public boolean connect(BluetoothDevice device) { - Log.d(TAG,"PBAPClientProfile got connect request"); - if (mService == null) { - return false; - } - Log.d(TAG,"PBAPClientProfile attempting to connect to " + device.getAddress()); - return mService.connect(device); - } - - public boolean disconnect(BluetoothDevice device) { - Log.d(TAG,"PBAPClientProfile got disconnect request"); - if (mService == null) { - return false; - } - return mService.disconnect(device); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -147,31 +133,37 @@ public final class PbapClientProfile implements LocalBluetoothProfile { return mService.getConnectionState(device); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { if (mService == null) { return false; } - return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { if (mService == null) { - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } - public void setPreferred(BluetoothDevice device, boolean preferred) { + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; if (mService == null) { - return; + return false; } - if (preferred) { - if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (enabled) { + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } public String toString() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java index 3f920a8cf1dd..9e2e4a14124a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java @@ -16,6 +16,8 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -89,32 +91,33 @@ public class PbapServerProfile implements LocalBluetoothProfile { return false; } - public boolean connect(BluetoothDevice device) { - /*Can't connect from server */ - return false; - - } - - public boolean disconnect(BluetoothDevice device) { - if (mService == null) return false; - return mService.disconnect(device); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) return BluetoothProfile.STATE_DISCONNECTED; return mService.getConnectionState(device); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { return false; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { return -1; } - public void setPreferred(BluetoothDevice device, boolean preferred) { - // ignore: isPreferred is always true for PBAP + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; + if (mService == null) { + return false; + } + + if (!enabled) { + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); + } + + return isEnabled; } public String toString() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java index 0ca4d6195a32..104f1d738000 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -108,23 +111,6 @@ final class SapProfile implements LocalBluetoothProfile { return true; } - public boolean connect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.connect(device); - } - - public boolean disconnect(BluetoothDevice device) { - if (mService == null) { - return false; - } - if (mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); - } - return mService.disconnect(device); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -132,31 +118,37 @@ final class SapProfile implements LocalBluetoothProfile { return mService.getConnectionState(device); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { if (mService == null) { return false; } - return mService.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { if (mService == null) { - return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } - public void setPreferred(BluetoothDevice device, boolean preferred) { + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; if (mService == null) { - return; + return false; } - if (preferred) { - if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (enabled) { + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } public List<BluetoothDevice> getConnectedDevices() { diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java index 008943c99033..eb35c44bd690 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java @@ -108,9 +108,9 @@ public class BluetoothMediaManager extends MediaManager implements BluetoothCall Log.d(TAG, "addConnectableA2dpDevices() device : " + cachedDevice.getName() + ", is connected : " + cachedDevice.isConnected() - + ", is preferred : " + a2dpProfile.isPreferred(device)); + + ", is enabled : " + a2dpProfile.isEnabled(device)); - if (a2dpProfile.isPreferred(device) + if (a2dpProfile.isEnabled(device) && BluetoothDevice.BOND_BONDED == cachedDevice.getBondState()) { addMediaDevice(cachedDevice); } @@ -140,13 +140,13 @@ public class BluetoothMediaManager extends MediaManager implements BluetoothCall Log.d(TAG, "addConnectableHearingAidDevices() device : " + cachedDevice.getName() + ", is connected : " + cachedDevice.isConnected() - + ", is preferred : " + hapProfile.isPreferred(device)); + + ", is enabled : " + hapProfile.isEnabled(device)); final long hiSyncId = hapProfile.getHiSyncId(device); // device with same hiSyncId should not be shown in the UI. // So do not add it into connectedDevices. - if (!devicesHiSyncIds.contains(hiSyncId) && hapProfile.isPreferred(device) + if (!devicesHiSyncIds.contains(hiSyncId) && hapProfile.isEnabled(device) && BluetoothDevice.BOND_BONDED == cachedDevice.getBondState()) { devicesHiSyncIds.add(hiSyncId); addMediaDevice(cachedDevice); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java index 976445eb8c04..9bb2f22ddbcf 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java @@ -18,7 +18,6 @@ package com.android.settingslib.bluetooth; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothA2dpSink; @@ -65,18 +64,6 @@ public class A2dpSinkProfileTest { } @Test - public void connect_shouldConnectBluetoothA2dpSink() { - mProfile.connect(mBluetoothDevice); - verify(mService).connect(mBluetoothDevice); - } - - @Test - public void disconnect_shouldDisconnectBluetoothA2dpSink() { - mProfile.disconnect(mBluetoothDevice); - verify(mService).disconnect(mBluetoothDevice); - } - - @Test public void getConnectionStatus_shouldReturnConnectionState() { when(mService.getConnectionState(mBluetoothDevice)). thenReturn(BluetoothProfile.STATE_CONNECTED); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java index 69c020dd5c08..d121e0b2d2fb 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java @@ -18,7 +18,6 @@ package com.android.settingslib.bluetooth; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; @@ -65,18 +64,6 @@ public class HfpClientProfileTest { } @Test - public void connect_shouldConnectBluetoothHeadsetClient() { - mProfile.connect(mBluetoothDevice); - verify(mService).connect(mBluetoothDevice); - } - - @Test - public void disconnect_shouldDisconnectBluetoothHeadsetClient() { - mProfile.disconnect(mBluetoothDevice); - verify(mService).disconnect(mBluetoothDevice); - } - - @Test public void getConnectionStatus_shouldReturnConnectionState() { when(mService.getConnectionState(mBluetoothDevice)). thenReturn(BluetoothProfile.STATE_CONNECTED); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java index f38af70c7498..3665d9c10165 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java @@ -18,7 +18,6 @@ package com.android.settingslib.bluetooth; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; @@ -65,17 +64,6 @@ public class HidDeviceProfileTest { } @Test - public void connect_shouldReturnFalse() { - assertThat(mProfile.connect(mBluetoothDevice)).isFalse(); - } - - @Test - public void disconnect_shouldDisconnectBluetoothHidDevice() { - mProfile.disconnect(mBluetoothDevice); - verify(mService).disconnect(mBluetoothDevice); - } - - @Test public void getConnectionStatus_shouldReturnConnectionState() { when(mService.getConnectionState(mBluetoothDevice)). thenReturn(BluetoothProfile.STATE_CONNECTED); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java index 6f667094a5aa..25031a62294c 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java @@ -18,7 +18,6 @@ package com.android.settingslib.bluetooth; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; @@ -65,18 +64,6 @@ public class MapClientProfileTest { } @Test - public void connect_shouldConnectBluetoothMapClient() { - mProfile.connect(mBluetoothDevice); - verify(mService).connect(mBluetoothDevice); - } - - @Test - public void disconnect_shouldDisconnectBluetoothMapClient() { - mProfile.disconnect(mBluetoothDevice); - verify(mService).disconnect(mBluetoothDevice); - } - - @Test public void getConnectionStatus_shouldReturnConnectionState() { when(mService.getConnectionState(mBluetoothDevice)). thenReturn(BluetoothProfile.STATE_CONNECTED); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java index b21ec9c3e52a..4305a3bc25a3 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java @@ -18,7 +18,6 @@ package com.android.settingslib.bluetooth; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; @@ -65,18 +64,6 @@ public class PbapClientProfileTest { } @Test - public void connect_shouldConnectBluetoothPbapClient() { - mProfile.connect(mBluetoothDevice); - verify(mService).connect(mBluetoothDevice); - } - - @Test - public void disconnect_shouldDisconnectBluetoothPbapClient() { - mProfile.disconnect(mBluetoothDevice); - verify(mService).disconnect(mBluetoothDevice); - } - - @Test public void getConnectionStatus_shouldReturnConnectionState() { when(mService.getConnectionState(mBluetoothDevice)). thenReturn(BluetoothProfile.STATE_CONNECTED); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java index ec880345f6f0..e460eaf16bbf 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java @@ -18,7 +18,6 @@ package com.android.settingslib.bluetooth; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; @@ -64,18 +63,6 @@ public class SapProfileTest { } @Test - public void connect_shouldConnectBluetoothSap() { - mProfile.connect(mBluetoothDevice); - verify(mService).connect(mBluetoothDevice); - } - - @Test - public void disconnect_shouldDisconnectBluetoothSap() { - mProfile.disconnect(mBluetoothDevice); - verify(mService).disconnect(mBluetoothDevice); - } - - @Test public void getConnectionStatus_shouldReturnConnectionState() { when(mService.getConnectionState(mBluetoothDevice)). thenReturn(BluetoothProfile.STATE_CONNECTED); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaManagerTest.java index 030bab6a2034..7f463636a47a 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaManagerTest.java @@ -96,7 +96,7 @@ public class BluetoothMediaManagerTest { when(mA2dpProfile.getConnectableDevices()).thenReturn(devices); when(mCachedDeviceManager.findDevice(bluetoothDevice)).thenReturn(cachedDevice); when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); - when(mA2dpProfile.isPreferred(bluetoothDevice)).thenReturn(true); + when(mA2dpProfile.isEnabled(bluetoothDevice)).thenReturn(true); assertThat(mMediaManager.mMediaDevices).isEmpty(); mMediaManager.startScan(); @@ -113,7 +113,7 @@ public class BluetoothMediaManagerTest { when(mA2dpProfile.getConnectableDevices()).thenReturn(devices); when(mCachedDeviceManager.findDevice(bluetoothDevice)).thenReturn(cachedDevice); when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE); - when(mA2dpProfile.isPreferred(bluetoothDevice)).thenReturn(true); + when(mA2dpProfile.isEnabled(bluetoothDevice)).thenReturn(true); assertThat(mMediaManager.mMediaDevices).isEmpty(); mMediaManager.startScan(); @@ -141,7 +141,7 @@ public class BluetoothMediaManagerTest { when(mHapProfile.getConnectableDevices()).thenReturn(devices); when(mCachedDeviceManager.findDevice(bluetoothDevice)).thenReturn(cachedDevice); when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); - when(mHapProfile.isPreferred(bluetoothDevice)).thenReturn(true); + when(mHapProfile.isEnabled(bluetoothDevice)).thenReturn(true); assertThat(mMediaManager.mMediaDevices).isEmpty(); mMediaManager.startScan(); diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index b997f85bebd3..0ffbe80d4ab3 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -282,6 +282,22 @@ android:excludeFromRecents="true" android:exported="false" /> + <!-- + The following is used as a no-op/null home activity when + no other MAIN/HOME activity is present (e.g., in CSI). + --> + <activity android:name=".NullHome" + android:excludeFromRecents="true" + android:label="" + android:screenOrientation="nosensor"> + <!-- The priority here is set to be lower than that for Settings --> + <intent-filter android:priority="-1100"> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.HOME" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + <receiver android:name=".BugreportReceiver" android:permission="android.permission.DUMP"> diff --git a/packages/Shell/res/layout/null_home_finishing_boot.xml b/packages/Shell/res/layout/null_home_finishing_boot.xml new file mode 100644 index 000000000000..5f9563a5d25c --- /dev/null +++ b/packages/Shell/res/layout/null_home_finishing_boot.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 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 + --> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="#80000000" + android:forceHasOverlappingRendering="false"> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_gravity="center" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="40sp" + android:textColor="?android:attr/textColorPrimary" + android:text="@*android:string/android_start_title"/> + <ProgressBar + style="@android:style/Widget.Material.ProgressBar.Horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="12.75dp" + android:colorControlActivated="?android:attr/textColorPrimary" + android:indeterminate="true"/> + </LinearLayout> +</FrameLayout> diff --git a/packages/Shell/src/com/android/shell/NullHome.java b/packages/Shell/src/com/android/shell/NullHome.java new file mode 100644 index 000000000000..bd975614a50a --- /dev/null +++ b/packages/Shell/src/com/android/shell/NullHome.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2020 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.shell; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; + +/** + * This covers the fallback case where no launcher is available. + * Usually Settings.apk has one fallback home activity. + * Settings.apk, however, is not part of CSI, which needs to be + * standalone (bootable and testable). + */ +public class NullHome extends Activity { + private static final String TAG = "NullHome"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Log.i(TAG, "onCreate"); + setContentView(R.layout.null_home_finishing_boot); + } + + protected void onDestroy() { + super.onDestroy(); + Log.i(TAG, "onDestroy"); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index a37367e4bb25..da0f83da5a71 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -89,7 +89,6 @@ public class NotificationEntryManager implements private NotificationRowBinder mNotificationRowBinder; private NotificationPresenter mPresenter; - private NotificationListenerService.RankingMap mLatestRankingMap; @VisibleForTesting protected NotificationData mNotificationData; @@ -168,8 +167,7 @@ public class NotificationEntryManager implements /** Adds a {@link NotificationLifetimeExtender}. */ public void addNotificationLifetimeExtender(NotificationLifetimeExtender extender) { mNotificationLifetimeExtenders.add(extender); - extender.setCallback(key -> removeNotification(key, mLatestRankingMap, - UNDEFINED_DISMISS_REASON)); + extender.setCallback(key -> removeNotification(key, null, UNDEFINED_DISMISS_REASON)); } public NotificationData getNotificationData() { @@ -307,7 +305,6 @@ public class NotificationEntryManager implements if (!forceRemove && !entryDismissed) { for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) { if (extender.shouldExtendLifetime(entry)) { - mLatestRankingMap = ranking; extendLifetime(entry, extender); lifetimeExtended = true; break; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java index 00092929fd49..4ad7487091e4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java @@ -201,6 +201,9 @@ public class NotificationData { removed = mEntries.remove(key); } if (removed == null) return null; + // NEM may pass us a null ranking map if removing a lifetime-extended notification, + // so use the most recent ranking + if (ranking == null) ranking = mRankingMap; mGroupManager.onEntryRemoved(removed); updateRankingAndSort(ranking); return removed; diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 19e4c105b95c..1edbbf8af1ff 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -1944,7 +1944,8 @@ public class Tethering { parcel.tetheringSupported = mDeps.isTetheringSupported(); parcel.upstreamNetwork = mTetherUpstream; parcel.config = mConfig.toStableParcelable(); - parcel.states = mTetherStatesParcel; + parcel.states = + mTetherStatesParcel != null ? mTetherStatesParcel : emptyTetherStatesParcel(); try { callback.onCallbackStarted(parcel); } catch (RemoteException e) { @@ -1953,6 +1954,17 @@ public class Tethering { }); } + private TetherStatesParcel emptyTetherStatesParcel() { + final TetherStatesParcel parcel = new TetherStatesParcel(); + parcel.availableList = new String[0]; + parcel.tetheredList = new String[0]; + parcel.localOnlyList = new String[0]; + parcel.erroredIfaceList = new String[0]; + parcel.lastErrorList = new int[0]; + + return parcel; + } + /** Unregister tethering event callback */ void unregisterTetheringEventCallback(ITetheringEventCallback callback) { mHandler.post(() -> { diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index e7c3e5567049..a9178494806c 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -127,6 +127,7 @@ import com.android.internal.util.StateMachine; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.FakeSettingsProvider; import com.android.networkstack.tethering.R; +import com.android.testutils.MiscAssertsKt; import org.junit.After; import org.junit.Before; @@ -1220,6 +1221,16 @@ public class TetheringTest { } } + private void assertTetherStatesNotNullButEmpty(final TetherStatesParcel parcel) { + assertFalse(parcel == null); + assertEquals(0, parcel.availableList.length); + assertEquals(0, parcel.tetheredList.length); + assertEquals(0, parcel.localOnlyList.length); + assertEquals(0, parcel.erroredIfaceList.length); + assertEquals(0, parcel.lastErrorList.length); + MiscAssertsKt.assertFieldCountEquals(5, TetherStatesParcel.class); + } + @Test public void testRegisterTetheringEventCallback() throws Exception { TestTetheringEventCallback callback = new TestTetheringEventCallback(); @@ -1232,7 +1243,7 @@ public class TetheringTest { callback.expectConfigurationChanged( mTethering.getTetheringConfiguration().toStableParcelable()); TetherStatesParcel tetherState = callback.pollTetherStatesChanged(); - assertEquals(tetherState, null); + assertTetherStatesNotNullButEmpty(tetherState); // 2. Enable wifi tethering. UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState(); when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); diff --git a/services/core/Android.bp b/services/core/Android.bp index 4c569efe4c0a..649b5ea8223d 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -43,6 +43,7 @@ java_library_static { "android.hardware.broadcastradio-V2.0-java", "android.hardware.health-V1.0-java", "android.hardware.health-V2.0-java", + "android.hardware.light-java", "android.hardware.weaver-V1.0-java", "android.hardware.biometrics.face-V1.0-java", "android.hardware.biometrics.fingerprint-V2.1-java", diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index d18b4f64f039..0816955ed531 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -66,8 +66,8 @@ import com.android.internal.app.IBatteryStats; import com.android.internal.logging.MetricsLogger; import com.android.internal.util.DumpUtils; import com.android.server.am.BatteryStatsService; -import com.android.server.lights.Light; import com.android.server.lights.LightsManager; +import com.android.server.lights.LogicalLight; import java.io.File; import java.io.FileDescriptor; @@ -1063,7 +1063,7 @@ public final class BatteryService extends SystemService { } private final class Led { - private final Light mBatteryLight; + private final LogicalLight mBatteryLight; private final int mBatteryLowARGB; private final int mBatteryMediumARGB; @@ -1098,7 +1098,7 @@ public final class BatteryService extends SystemService { mBatteryLight.setColor(mBatteryLowARGB); } else { // Flash red when battery is low and not charging - mBatteryLight.setFlashing(mBatteryLowARGB, Light.LIGHT_FLASH_TIMED, + mBatteryLight.setFlashing(mBatteryLowARGB, LogicalLight.LIGHT_FLASH_TIMED, mBatteryLedOn, mBatteryLedOff); } } else if (status == BatteryManager.BATTERY_STATUS_CHARGING diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index b03dc3b2ae93..46ee4ea614b5 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -38,8 +38,8 @@ import android.view.Surface; import android.view.SurfaceControl; import com.android.server.LocalServices; -import com.android.server.lights.Light; import com.android.server.lights.LightsManager; +import com.android.server.lights.LogicalLight; import java.io.PrintWriter; import java.util.ArrayList; @@ -160,7 +160,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { private final class LocalDisplayDevice extends DisplayDevice { private final long mPhysicalDisplayId; - private final Light mBacklight; + private final LogicalLight mBacklight; private final SparseArray<DisplayModeRecord> mSupportedModes = new SparseArray<>(); private final ArrayList<Integer> mSupportedColorModes = new ArrayList<>(); private final boolean mIsInternal; diff --git a/services/core/java/com/android/server/lights/LightsManager.java b/services/core/java/com/android/server/lights/LightsManager.java index be20a445432b..521913a0c439 100644 --- a/services/core/java/com/android/server/lights/LightsManager.java +++ b/services/core/java/com/android/server/lights/LightsManager.java @@ -29,5 +29,8 @@ public abstract class LightsManager { public static final int LIGHT_ID_WIFI = Type.WIFI; public static final int LIGHT_ID_COUNT = Type.COUNT; - public abstract Light getLight(int id); + /** + * Returns the logical light with the given type. + */ + public abstract LogicalLight getLight(int id); } diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java index ac906bb23d32..5861c9d55279 100644 --- a/services/core/java/com/android/server/lights/LightsService.java +++ b/services/core/java/com/android/server/lights/LightsService.java @@ -15,32 +15,223 @@ package com.android.server.lights; +import android.Manifest; +import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Context; +import android.hardware.light.HwLight; +import android.hardware.light.HwLightState; +import android.hardware.light.ILights; +import android.hardware.lights.ILightsManager; +import android.hardware.lights.Light; +import android.hardware.lights.LightState; import android.os.Handler; import android.os.IBinder; -import android.os.Message; +import android.os.Looper; import android.os.PowerManager; +import android.os.RemoteException; +import android.os.ServiceManager; import android.os.Trace; import android.provider.Settings; import android.util.Slog; +import android.util.SparseArray; import android.view.SurfaceControl; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; import com.android.server.SystemService; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + public class LightsService extends SystemService { static final String TAG = "LightsService"; static final boolean DEBUG = false; - final LightImpl mLights[] = new LightImpl[LightsManager.LIGHT_ID_COUNT]; + private LightImpl[] mLights = null; + private SparseArray<LightImpl> mLightsById = null; + + private ILights mVintfLights = null; + + @VisibleForTesting + final LightsManagerBinderService mManagerService; + + private Handler mH; + + private final class LightsManagerBinderService extends ILightsManager.Stub { + + private final class Session { + final IBinder mToken; + final SparseArray<LightState> mRequests = new SparseArray<>(); + + Session(IBinder token) { + mToken = token; + } + + void setRequest(int lightId, LightState state) { + if (state != null) { + mRequests.put(lightId, state); + } else { + mRequests.remove(lightId); + } + } + } + + @GuardedBy("LightsService.this") + private final List<Session> mSessions = new ArrayList<>(); + + /** + * Returns the lights available for apps to control on the device. Only lights that aren't + * reserved for system use are available to apps. + */ + @Override + public List<Light> getLights() { + getContext().enforceCallingOrSelfPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS, + "getLights requires CONTROL_DEVICE_LIGHTS_PERMISSION"); + + synchronized (LightsService.this) { + final List<Light> lights = new ArrayList<Light>(); + for (int i = 0; i < mLightsById.size(); i++) { + HwLight hwLight = mLightsById.valueAt(i).getHwLight(); + if (!isSystemLight(hwLight)) { + lights.add(new Light(hwLight.id, hwLight.ordinal, hwLight.type)); + } + } + return lights; + } + } + + /** + * Updates the set of light requests for {@param token} with additions and removals from + * {@param lightIds} and {@param lightStates}. + * + * <p>Null values mean that the request should be removed, and the light turned off if it + * is not being used by anything else. + */ + @Override + public void setLightStates(IBinder token, int[] lightIds, LightState[] lightStates) { + getContext().enforceCallingOrSelfPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS, + "setLightStates requires CONTROL_DEVICE_LIGHTS permission"); + Preconditions.checkState(lightIds.length == lightStates.length); + + synchronized (LightsService.this) { + Session session = getSessionLocked(Preconditions.checkNotNull(token)); + Preconditions.checkState(session != null, "not registered"); + + checkRequestIsValid(lightIds); + + for (int i = 0; i < lightIds.length; i++) { + session.setRequest(lightIds[i], lightStates[i]); + } + invalidateLightStatesLocked(); + } + } + + @Override + public @Nullable LightState getLightState(int lightId) { + getContext().enforceCallingOrSelfPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS, + "getLightState(@TestApi) requires CONTROL_DEVICE_LIGHTS permission"); - private final class LightImpl extends Light { + synchronized (LightsService.this) { + final LightImpl light = mLightsById.get(lightId); + if (light == null || isSystemLight(light.getHwLight())) { + throw new IllegalArgumentException("Invalid light: " + lightId); + } + return new LightState(light.getColor()); + } + } + @Override + public void openSession(IBinder token) { + getContext().enforceCallingOrSelfPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS, + "openSession requires CONTROL_DEVICE_LIGHTS permission"); + Preconditions.checkNotNull(token); + + synchronized (LightsService.this) { + Preconditions.checkState(getSessionLocked(token) == null, "already registered"); + try { + token.linkToDeath(() -> closeSessionInternal(token), 0); + mSessions.add(new Session(token)); + } catch (RemoteException e) { + Slog.e(TAG, "Couldn't open session, client already died" , e); + throw new IllegalArgumentException("Client is already dead."); + } + } + } + + @Override + public void closeSession(IBinder token) { + getContext().enforceCallingOrSelfPermission(Manifest.permission.CONTROL_DEVICE_LIGHTS, + "closeSession requires CONTROL_DEVICE_LIGHTS permission"); + Preconditions.checkNotNull(token); + closeSessionInternal(token); + } + + private void closeSessionInternal(IBinder token) { + synchronized (LightsService.this) { + final Session session = getSessionLocked(token); + if (session != null) { + mSessions.remove(session); + invalidateLightStatesLocked(); + } + } + } + + private void checkRequestIsValid(int[] lightIds) { + for (int i = 0; i < lightIds.length; i++) { + final LightImpl light = mLightsById.get(lightIds[i]); + final HwLight hwLight = light.getHwLight(); + Preconditions.checkState(light != null && !isSystemLight(hwLight), + "invalid lightId " + hwLight.id); + } + } + + /** + * Apply light state requests for all light IDs. + * + * <p>In case of conflict, the session that started earliest wins. + */ + private void invalidateLightStatesLocked() { + final Map<Integer, LightState> states = new HashMap<>(); + for (int i = mSessions.size() - 1; i >= 0; i--) { + SparseArray<LightState> requests = mSessions.get(i).mRequests; + for (int j = 0; j < requests.size(); j++) { + states.put(requests.keyAt(j), requests.valueAt(j)); + } + } + for (int i = 0; i < mLightsById.size(); i++) { + LightImpl light = mLightsById.valueAt(i); + HwLight hwLight = light.getHwLight(); + if (!isSystemLight(hwLight)) { + LightState state = states.get(hwLight.id); + if (state != null) { + light.setColor(state.getColor()); + } else { + light.turnOff(); + } + } + } + } + + private @Nullable Session getSessionLocked(IBinder token) { + for (int i = 0; i < mSessions.size(); i++) { + if (token.equals(mSessions.get(i).mToken)) { + return mSessions.get(i); + } + } + return null; + } + } + + private final class LightImpl extends LogicalLight { private final IBinder mDisplayToken; private final int mSurfaceControlMaximumBrightness; - private LightImpl(Context context, int id) { - mId = id; + private LightImpl(Context context, HwLight hwLight) { + mHwLight = hwLight; mDisplayToken = SurfaceControl.getInternalDisplayToken(); final boolean brightnessSupport = SurfaceControl.getDisplayBrightnessSupport( mDisplayToken); @@ -67,8 +258,8 @@ public class LightsService extends SystemService { synchronized (this) { // LOW_PERSISTENCE cannot be manually set if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) { - Slog.w(TAG, "setBrightness with LOW_PERSISTENCE unexpected #" + mId + - ": brightness=0x" + Integer.toHexString(brightness)); + Slog.w(TAG, "setBrightness with LOW_PERSISTENCE unexpected #" + mHwLight.id + + ": brightness=0x" + Integer.toHexString(brightness)); return; } // Ideally, we'd like to set the brightness mode through the SF/HWC as well, but @@ -120,7 +311,7 @@ public class LightsService extends SystemService { setLightLocked(color, LIGHT_FLASH_HARDWARE, onMS, 1000, BRIGHTNESS_MODE_USER); mColor = 0; - mH.sendMessageDelayed(Message.obtain(mH, 1, this), onMS); + mH.postDelayed(this::stopFlashing, onMS); } } } @@ -167,8 +358,10 @@ public class LightsService extends SystemService { if (!mInitialized || color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS || mBrightnessMode != brightnessMode) { - if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#" - + Integer.toHexString(color) + ": brightnessMode=" + brightnessMode); + if (DEBUG) { + Slog.v(TAG, "setLight #" + mHwLight.id + ": color=#" + + Integer.toHexString(color) + ": brightnessMode=" + brightnessMode); + } mInitialized = true; mLastColor = mColor; mColor = color; @@ -176,10 +369,31 @@ public class LightsService extends SystemService { mOnMS = onMS; mOffMS = offMS; mBrightnessMode = brightnessMode; - Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", 0x" - + Integer.toHexString(color) + ")"); + setLightUnchecked(color, mode, onMS, offMS, brightnessMode); + } + } + + private void setLightUnchecked(int color, int mode, int onMS, int offMS, + int brightnessMode) { + Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLightState(" + mHwLight.id + ", 0x" + + Integer.toHexString(color) + ")"); + if (mVintfLights != null) { + HwLightState lightState = new HwLightState(); + lightState.color = color; + lightState.flashMode = (byte) mode; + lightState.flashOnMs = onMS; + lightState.flashOffMs = offMS; + lightState.brightnessMode = (byte) brightnessMode; try { - setLight_native(mId, color, mode, onMS, offMS, brightnessMode); + mVintfLights.setLightState(mHwLight.id, lightState); + } catch (RemoteException | UnsupportedOperationException ex) { + Slog.e(TAG, "Failed issuing setLightState", ex); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_POWER); + } + } else { + try { + setLight_native(mHwLight.id, color, mode, onMS, offMS, brightnessMode); } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } @@ -190,7 +404,15 @@ public class LightsService extends SystemService { return mVrModeEnabled && mUseLowPersistenceForVR; } - private int mId; + private HwLight getHwLight() { + return mHwLight; + } + + private int getColor() { + return mColor; + } + + private HwLight mHwLight; private int mColor; private int mMode; private int mOnMS; @@ -205,16 +427,59 @@ public class LightsService extends SystemService { } public LightsService(Context context) { + this(context, + ILights.Stub.asInterface( + ServiceManager.getService("android.hardware.light.ILights/default")), + Looper.myLooper()); + } + + @VisibleForTesting + LightsService(Context context, ILights service, Looper looper) { super(context); + mH = new Handler(looper); + mVintfLights = service; + mManagerService = new LightsManagerBinderService(); + populateAvailableLights(context); + } + + private void populateAvailableLights(Context context) { + mLights = new LightImpl[LightsManager.LIGHT_ID_COUNT]; + mLightsById = new SparseArray<>(); - for (int i = 0; i < LightsManager.LIGHT_ID_COUNT; i++) { - mLights[i] = new LightImpl(context, i); + if (mVintfLights != null) { + try { + for (HwLight availableLight : mVintfLights.getLights()) { + LightImpl light = new LightImpl(context, availableLight); + int type = (int) availableLight.type; + if (0 <= type && type < mLights.length && mLights[type] == null) { + mLights[type] = light; + } + mLightsById.put(availableLight.id, light); + } + } catch (RemoteException ex) { + Slog.e(TAG, "Unable to get lights for initialization", ex); + } + } + + // In the case where only the old HAL is available, all lights will be initialized here + for (int i = 0; i < mLights.length; i++) { + if (mLights[i] == null) { + // The ordinal can be anything if there is only 1 light of each type. Set it to 1. + HwLight light = new HwLight(); + light.id = (byte) i; + light.ordinal = 1; + light.type = (byte) i; + + mLights[i] = new LightImpl(context, light); + mLightsById.put(i, mLights[i]); + } } } @Override public void onStart() { publishLocalService(LightsManager.class, mService); + publishBinderService(Context.LIGHTS_SERVICE, mManagerService); } @Override @@ -231,22 +496,25 @@ public class LightsService extends SystemService { private final LightsManager mService = new LightsManager() { @Override - public Light getLight(int id) { - if (0 <= id && id < LIGHT_ID_COUNT) { - return mLights[id]; + public LogicalLight getLight(int lightType) { + if (mLights != null && 0 <= lightType && lightType < mLights.length) { + return mLights[lightType]; } else { return null; } } }; - private Handler mH = new Handler() { - @Override - public void handleMessage(Message msg) { - LightImpl light = (LightImpl)msg.obj; - light.stopFlashing(); - } - }; + /** + * Returns whether a light is system-use-only or should be accessible to + * applications using the {@link android.hardware.lights.LightsManager} API. + */ + private static boolean isSystemLight(HwLight light) { + // LIGHT_ID_COUNT comes from the 2.0 HIDL HAL and only contains system + // lights. Newly added lights will be made available via the + // LightsManager API. + return 0 <= light.type && light.type < LightsManager.LIGHT_ID_COUNT; + } static native void setLight_native(int light, int color, int mode, int onMS, int offMS, int brightnessMode); diff --git a/services/core/java/com/android/server/lights/Light.java b/services/core/java/com/android/server/lights/LogicalLight.java index 717e3dae479d..30e24735281b 100644 --- a/services/core/java/com/android/server/lights/Light.java +++ b/services/core/java/com/android/server/lights/LogicalLight.java @@ -19,9 +19,24 @@ package com.android.server.lights; import android.hardware.light.V2_0.Brightness; import android.hardware.light.V2_0.Flash; -public abstract class Light { +/** + * Allow control over a logical light of a given type. The mapping of logical lights to physical + * lights is HAL implementation-dependent. + */ +public abstract class LogicalLight { + /** + * Keep the light steady on or off. + */ public static final int LIGHT_FLASH_NONE = Flash.NONE; + + /** + * Flash the light at specified rate. + */ public static final int LIGHT_FLASH_TIMED = Flash.TIMED; + + /** + * Flash the light using hardware assist. + */ public static final int LIGHT_FLASH_HARDWARE = Flash.HARDWARE; /** @@ -49,10 +64,33 @@ public abstract class Light { */ public abstract void setBrightness(int brightness, int brightnessMode); + /** + * Set the color of a light. + */ public abstract void setColor(int color); + + /** + * Set the color of a light and control flashing. + */ public abstract void setFlashing(int color, int mode, int onMS, int offMS); + + /** + * Pulses the light. + */ public abstract void pulse(); + + /** + * Pulses the light with a specified color for a specified duration. + */ public abstract void pulse(int color, int onMS); + + /** + * Turns off the light. + */ public abstract void turnOff(); + + /** + * Set the VR mode of a display. + */ public abstract void setVrMode(boolean enabled); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 6f45825e81fe..85ba3672884a 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -233,8 +233,8 @@ import com.android.server.EventLogTags; import com.android.server.IoThread; import com.android.server.LocalServices; import com.android.server.SystemService; -import com.android.server.lights.Light; import com.android.server.lights.LightsManager; +import com.android.server.lights.LogicalLight; import com.android.server.notification.ManagedServices.ManagedServiceInfo; import com.android.server.notification.ManagedServices.UserProfiles; import com.android.server.pm.PackageManagerService; @@ -376,8 +376,8 @@ public class NotificationManagerService extends SystemService { private final HandlerThread mRankingThread = new HandlerThread("ranker", Process.THREAD_PRIORITY_BACKGROUND); - private Light mNotificationLight; - Light mAttentionLight; + private LogicalLight mNotificationLight; + LogicalLight mAttentionLight; private long[] mFallbackVibrationPattern; private boolean mUseAttentionLight; @@ -1507,7 +1507,7 @@ public class NotificationManagerService extends SystemService { } @VisibleForTesting - void setLights(Light light) { + void setLights(LogicalLight light) { mNotificationLight = light; mAttentionLight = light; mNotificationPulseEnabled = true; @@ -7085,7 +7085,7 @@ public class NotificationManagerService extends SystemService { NotificationRecord.Light light = ledNotification.getLight(); if (light != null && mNotificationPulseEnabled) { // pulse repeatedly - mNotificationLight.setFlashing(light.color, Light.LIGHT_FLASH_TIMED, + mNotificationLight.setFlashing(light.color, LogicalLight.LIGHT_FLASH_TIMED, light.onMs, light.offMs); } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 6fced8acf1df..d30afaac0967 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -48,6 +48,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.PermissionWhitelistFlags; import android.content.pm.PackageManagerInternal; @@ -2614,7 +2615,7 @@ public class PermissionManagerService { // Make sure all dynamic permissions have been assigned to a package, // and make sure there are no dangling permissions. - flags = updatePermissions(changingPkgName, changingPkg, flags); + flags = updatePermissions(changingPkgName, changingPkg, flags, callback); synchronized (mLock) { if (mBackgroundPermissions == null) { @@ -2664,7 +2665,8 @@ public class PermissionManagerService { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } - private int updatePermissions(String packageName, PackageParser.Package pkg, int flags) { + private int updatePermissions(String packageName, PackageParser.Package pkg, int flags, + @Nullable PermissionCallback callback) { Set<BasePermission> needsUpdate = null; synchronized (mLock) { final Iterator<BasePermission> it = mSettings.mPermissions.values().iterator(); @@ -2678,6 +2680,44 @@ public class PermissionManagerService { && (pkg == null || !hasPermission(pkg, bp.getName()))) { Slog.i(TAG, "Removing old permission tree: " + bp.getName() + " from package " + bp.getSourcePackageName()); + if (bp.isRuntime()) { + final int[] userIds = mUserManagerInt.getUserIds(); + final int numUserIds = userIds.length; + for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) { + final int userId = userIds[userIdNum]; + + mPackageManagerInt.forEachPackage((Package p) -> { + final String pName = p.packageName; + final ApplicationInfo appInfo = + mPackageManagerInt.getApplicationInfo(pName, 0, + Process.SYSTEM_UID, UserHandle.USER_SYSTEM); + if (appInfo != null + && appInfo.targetSdkVersion < Build.VERSION_CODES.M) { + return; + } + + final String permissionName = bp.getName(); + if (checkPermission(permissionName, pName, Process.SYSTEM_UID, + userId) == PackageManager.PERMISSION_GRANTED) { + try { + revokeRuntimePermission( + permissionName, + pName, + false, + userId, + callback); + } catch (IllegalArgumentException e) { + Slog.e(TAG, + "Failed to revoke " + + permissionName + + " from " + + pName, + e); + } + } + }); + } + } flags |= UPDATE_PERMISSIONS_ALL; it.remove(); } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index e1b3e4d6fbcf..3a7604aa7d69 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -93,8 +93,8 @@ import com.android.server.SystemService; import com.android.server.UiThread; import com.android.server.Watchdog; import com.android.server.am.BatteryStatsService; -import com.android.server.lights.Light; import com.android.server.lights.LightsManager; +import com.android.server.lights.LogicalLight; import com.android.server.policy.WindowManagerPolicy; import com.android.server.power.batterysaver.BatterySaverController; import com.android.server.power.batterysaver.BatterySaverPolicy; @@ -247,7 +247,7 @@ public final class PowerManagerService extends SystemService private WirelessChargerDetector mWirelessChargerDetector; private SettingsObserver mSettingsObserver; private DreamManagerInternal mDreamManager; - private Light mAttentionLight; + private LogicalLight mAttentionLight; private final Object mLock = LockGuard.installNewLock(LockGuard.INDEX_POWER); @@ -3130,7 +3130,7 @@ public final class PowerManagerService extends SystemService } private void setAttentionLightInternal(boolean on, int color) { - Light light; + LogicalLight light; synchronized (mLock) { if (!mSystemReady) { return; @@ -3139,7 +3139,7 @@ public final class PowerManagerService extends SystemService } // Control light outside of lock. - light.setFlashing(color, Light.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0); + light.setFlashing(color, LogicalLight.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0); } private void setDozeAfterScreenOffInternal(boolean on) { diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java index 5f00148335a7..89a530514263 100644 --- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java +++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java @@ -30,6 +30,7 @@ import android.os.UserHandle; import android.service.textclassifier.ITextClassifierCallback; import android.service.textclassifier.ITextClassifierService; import android.service.textclassifier.TextClassifierService; +import android.util.ArrayMap; import android.util.Slog; import android.util.SparseArray; import android.view.textclassifier.ConversationActions; @@ -54,6 +55,7 @@ import com.android.server.SystemService; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayDeque; +import java.util.Map; import java.util.Queue; /** @@ -119,6 +121,8 @@ public final class TextClassificationManagerService extends ITextClassifierServi private final Object mLock; @GuardedBy("mLock") final SparseArray<UserState> mUserStates = new SparseArray<>(); + @GuardedBy("mLock") + private final Map<TextClassificationSessionId, Integer> mSessionUserIds = new ArrayMap<>(); private TextClassificationManagerService(Context context) { mContext = Preconditions.checkNotNull(context); @@ -127,15 +131,16 @@ public final class TextClassificationManagerService extends ITextClassifierServi @Override public void onSuggestSelection( - TextClassificationSessionId sessionId, + @Nullable TextClassificationSessionId sessionId, TextSelection.Request request, ITextClassifierCallback callback) throws RemoteException { Preconditions.checkNotNull(request); Preconditions.checkNotNull(callback); - validateInput(mContext, request.getCallingPackageName()); + final int userId = request.getUserId(); + validateInput(mContext, request.getCallingPackageName(), userId); synchronized (mLock) { - UserState userState = getCallingUserStateLocked(); + UserState userState = getUserStateLocked(userId); if (!userState.bindLocked()) { callback.onFailure(); } else if (userState.isBoundLocked()) { @@ -150,15 +155,16 @@ public final class TextClassificationManagerService extends ITextClassifierServi @Override public void onClassifyText( - TextClassificationSessionId sessionId, + @Nullable TextClassificationSessionId sessionId, TextClassification.Request request, ITextClassifierCallback callback) throws RemoteException { Preconditions.checkNotNull(request); Preconditions.checkNotNull(callback); - validateInput(mContext, request.getCallingPackageName()); + final int userId = request.getUserId(); + validateInput(mContext, request.getCallingPackageName(), userId); synchronized (mLock) { - UserState userState = getCallingUserStateLocked(); + UserState userState = getUserStateLocked(userId); if (!userState.bindLocked()) { callback.onFailure(); } else if (userState.isBoundLocked()) { @@ -173,15 +179,16 @@ public final class TextClassificationManagerService extends ITextClassifierServi @Override public void onGenerateLinks( - TextClassificationSessionId sessionId, + @Nullable TextClassificationSessionId sessionId, TextLinks.Request request, ITextClassifierCallback callback) throws RemoteException { Preconditions.checkNotNull(request); Preconditions.checkNotNull(callback); - validateInput(mContext, request.getCallingPackageName()); + final int userId = request.getUserId(); + validateInput(mContext, request.getCallingPackageName(), userId); synchronized (mLock) { - UserState userState = getCallingUserStateLocked(); + UserState userState = getUserStateLocked(userId); if (!userState.bindLocked()) { callback.onFailure(); } else if (userState.isBoundLocked()) { @@ -196,12 +203,14 @@ public final class TextClassificationManagerService extends ITextClassifierServi @Override public void onSelectionEvent( - TextClassificationSessionId sessionId, SelectionEvent event) throws RemoteException { + @Nullable TextClassificationSessionId sessionId, SelectionEvent event) + throws RemoteException { Preconditions.checkNotNull(event); - validateInput(mContext, event.getPackageName()); + final int userId = event.getUserId(); + validateInput(mContext, event.getPackageName(), userId); synchronized (mLock) { - UserState userState = getCallingUserStateLocked(); + UserState userState = getUserStateLocked(userId); if (userState.isBoundLocked()) { userState.mService.onSelectionEvent(sessionId, event); } else { @@ -213,16 +222,19 @@ public final class TextClassificationManagerService extends ITextClassifierServi } @Override public void onTextClassifierEvent( - TextClassificationSessionId sessionId, + @Nullable TextClassificationSessionId sessionId, TextClassifierEvent event) throws RemoteException { Preconditions.checkNotNull(event); final String packageName = event.getEventContext() == null ? null : event.getEventContext().getPackageName(); - validateInput(mContext, packageName); + final int userId = event.getEventContext() == null + ? UserHandle.getCallingUserId() + : event.getEventContext().getUserId(); + validateInput(mContext, packageName, userId); synchronized (mLock) { - UserState userState = getCallingUserStateLocked(); + UserState userState = getUserStateLocked(userId); if (userState.isBoundLocked()) { userState.mService.onTextClassifierEvent(sessionId, event); } else { @@ -235,15 +247,16 @@ public final class TextClassificationManagerService extends ITextClassifierServi @Override public void onDetectLanguage( - TextClassificationSessionId sessionId, + @Nullable TextClassificationSessionId sessionId, TextLanguage.Request request, ITextClassifierCallback callback) throws RemoteException { Preconditions.checkNotNull(request); Preconditions.checkNotNull(callback); - validateInput(mContext, request.getCallingPackageName()); + final int userId = request.getUserId(); + validateInput(mContext, request.getCallingPackageName(), userId); synchronized (mLock) { - UserState userState = getCallingUserStateLocked(); + UserState userState = getUserStateLocked(userId); if (!userState.bindLocked()) { callback.onFailure(); } else if (userState.isBoundLocked()) { @@ -258,15 +271,16 @@ public final class TextClassificationManagerService extends ITextClassifierServi @Override public void onSuggestConversationActions( - TextClassificationSessionId sessionId, + @Nullable TextClassificationSessionId sessionId, ConversationActions.Request request, ITextClassifierCallback callback) throws RemoteException { Preconditions.checkNotNull(request); Preconditions.checkNotNull(callback); - validateInput(mContext, request.getCallingPackageName()); + final int userId = request.getUserId(); + validateInput(mContext, request.getCallingPackageName(), userId); synchronized (mLock) { - UserState userState = getCallingUserStateLocked(); + UserState userState = getUserStateLocked(userId); if (!userState.bindLocked()) { callback.onFailure(); } else if (userState.isBoundLocked()) { @@ -285,13 +299,15 @@ public final class TextClassificationManagerService extends ITextClassifierServi throws RemoteException { Preconditions.checkNotNull(sessionId); Preconditions.checkNotNull(classificationContext); - validateInput(mContext, classificationContext.getPackageName()); + final int userId = classificationContext.getUserId(); + validateInput(mContext, classificationContext.getPackageName(), userId); synchronized (mLock) { - UserState userState = getCallingUserStateLocked(); + UserState userState = getUserStateLocked(userId); if (userState.isBoundLocked()) { userState.mService.onCreateTextClassificationSession( classificationContext, sessionId); + mSessionUserIds.put(sessionId, userId); } else { userState.mPendingRequests.add(new PendingRequest( () -> onCreateTextClassificationSession(classificationContext, sessionId), @@ -306,9 +322,15 @@ public final class TextClassificationManagerService extends ITextClassifierServi Preconditions.checkNotNull(sessionId); synchronized (mLock) { - UserState userState = getCallingUserStateLocked(); + final int userId = mSessionUserIds.containsKey(sessionId) + ? mSessionUserIds.get(sessionId) + : UserHandle.getCallingUserId(); + validateInput(mContext, null /* packageName */, userId); + + UserState userState = getUserStateLocked(userId); if (userState.isBoundLocked()) { userState.mService.onDestroyTextClassificationSession(sessionId); + mSessionUserIds.remove(sessionId); } else { userState.mPendingRequests.add(new PendingRequest( () -> onDestroyTextClassificationSession(sessionId), @@ -318,11 +340,6 @@ public final class TextClassificationManagerService extends ITextClassifierServi } @GuardedBy("mLock") - private UserState getCallingUserStateLocked() { - return getUserStateLocked(UserHandle.getCallingUserId()); - } - - @GuardedBy("mLock") private UserState getUserStateLocked(int userId) { UserState result = mUserStates.get(userId); if (result == null) { @@ -356,6 +373,7 @@ public final class TextClassificationManagerService extends ITextClassifierServi pw.decreaseIndent(); } } + pw.println("Number of active sessions: " + mSessionUserIds.size()); } } @@ -420,20 +438,32 @@ public final class TextClassificationManagerService extends ITextClassifierServi e -> Slog.d(LOG_TAG, "Error " + opDesc + ": " + e.getMessage())); } - private static void validateInput(Context context, @Nullable String packageName) + private static void validateInput( + Context context, @Nullable String packageName, @UserIdInt int userId) throws RemoteException { - if (packageName == null) return; try { - final int packageUid = context.getPackageManager() - .getPackageUidAsUser(packageName, UserHandle.getCallingUserId()); - final int callingUid = Binder.getCallingUid(); - Preconditions.checkArgument(callingUid == packageUid - // Trust the system process: - || callingUid == android.os.Process.SYSTEM_UID); + if (packageName != null) { + final int packageUid = context.getPackageManager() + .getPackageUidAsUser(packageName, UserHandle.getCallingUserId()); + final int callingUid = Binder.getCallingUid(); + Preconditions.checkArgument(callingUid == packageUid + // Trust the system process: + || callingUid == android.os.Process.SYSTEM_UID, + "Invalid package name. Package=" + packageName + + ", CallingUid=" + callingUid); + } + + Preconditions.checkArgument(userId != UserHandle.USER_NULL, "Null userId"); + final int callingUserId = UserHandle.getCallingUserId(); + if (callingUserId != userId) { + context.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "Invalid userId. UserId=" + userId + ", CallingUserId=" + callingUserId); + } } catch (Exception e) { - throw new RemoteException( - String.format("Invalid package: name=%s, error=%s", packageName, e)); + throw new RemoteException("Invalid request: " + e.getMessage(), e, + /* enableSuppression */ true, /* writableStackTrace */ true); } } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 99a9db316c63..d4a462822ff3 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -865,6 +865,8 @@ public class DisplayPolicy { if (canToastShowWhenLocked(callingPid)) { attrs.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; } + // Toasts can't be clickable + attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; break; } diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 3b66c72c1025..81b42da7d953 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -93,7 +93,8 @@ cc_defaults { "libschedulerservicehidl", "libsensorservice", "libsensorservicehidl", - "libgui", + "libstatshidl", + "libgui", "libusbhost", "libtinyalsa", "libEGL", @@ -132,7 +133,8 @@ cc_defaults { "android.hardware.vr@1.0", "android.frameworks.schedulerservice@1.0", "android.frameworks.sensorservice@1.0", - "android.system.suspend@1.0", + "android.frameworks.stats@1.0", + "android.system.suspend@1.0", "suspend_control_aidl_interface-cpp", ], diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp index 67254b811ee0..279ea4b9a790 100644 --- a/services/core/jni/com_android_server_SystemServer.cpp +++ b/services/core/jni/com_android_server_SystemServer.cpp @@ -29,6 +29,7 @@ #include <schedulerservice/SchedulingPolicyService.h> #include <sensorservice/SensorService.h> #include <sensorservicehidl/SensorManager.h> +#include <stats/StatsHal.h> #include <bionic/malloc.h> #include <bionic/reserved_signals.h> @@ -59,6 +60,8 @@ static void android_server_SystemServer_startHidlServices(JNIEnv* env, jobject / using ::android::frameworks::schedulerservice::V1_0::implementation::SchedulingPolicyService; using ::android::frameworks::sensorservice::V1_0::ISensorManager; using ::android::frameworks::sensorservice::V1_0::implementation::SensorManager; + using ::android::frameworks::stats::V1_0::IStats; + using ::android::frameworks::stats::V1_0::implementation::StatsHal; using ::android::hardware::configureRpcThreadpool; status_t err; @@ -75,6 +78,10 @@ static void android_server_SystemServer_startHidlServices(JNIEnv* env, jobject / sp<ISchedulingPolicyService> schedulingService = new SchedulingPolicyService(); err = schedulingService->registerAsService(); ALOGE_IF(err != OK, "Cannot register %s: %d", ISchedulingPolicyService::descriptor, err); + + sp<IStats> statsHal = new StatsHal(); + err = statsHal->registerAsService(); + ALOGE_IF(err != OK, "Cannot register %s: %d", IStats::descriptor, err); } static void android_server_SystemServer_initZygoteChildHeapProfiling(JNIEnv* /* env */, diff --git a/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java new file mode 100644 index 000000000000..b0def605db79 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/lights/LightsServiceTest.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2020 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.lights; + +import static android.hardware.lights.LightsRequest.Builder; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.hardware.light.HwLight; +import android.hardware.light.HwLightState; +import android.hardware.light.ILights; +import android.hardware.lights.Light; +import android.hardware.lights.LightState; +import android.hardware.lights.LightsManager; +import android.os.Looper; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class LightsServiceTest { + + private final ILights mHal = new ILights.Stub() { + @Override + public void setLightState(int id, HwLightState state) { + return; + } + + @Override + public HwLight[] getLights() { + return new HwLight[] { + fakeHwLight(101, 3, 1), + fakeHwLight(102, LightsManager.LIGHT_TYPE_MICROPHONE, 4), + fakeHwLight(103, LightsManager.LIGHT_TYPE_MICROPHONE, 3), + fakeHwLight(104, LightsManager.LIGHT_TYPE_MICROPHONE, 1), + fakeHwLight(105, LightsManager.LIGHT_TYPE_MICROPHONE, 2) + }; + } + }; + + private static HwLight fakeHwLight(int id, int type, int ordinal) { + HwLight light = new HwLight(); + light.id = id; + light.type = (byte) type; + light.ordinal = ordinal; + return light; + } + + @Mock + Context mContext; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testGetLights_filtersSystemLights() { + LightsService service = new LightsService(mContext, mHal, Looper.getMainLooper()); + LightsManager manager = new LightsManager(mContext, service.mManagerService); + + // When lights are listed, only the 4 MICROPHONE lights should be visible. + assertThat(manager.getLights().size()).isEqualTo(4); + } + + @Test + public void testControlMultipleLights() { + LightsService service = new LightsService(mContext, mHal, Looper.getMainLooper()); + LightsManager manager = new LightsManager(mContext, service.mManagerService); + + // When the session requests to turn 3/4 lights on: + LightsManager.LightsSession session = manager.openSession(); + session.setLights(new Builder() + .setLight(manager.getLights().get(0), new LightState(0xf1)) + .setLight(manager.getLights().get(1), new LightState(0xf2)) + .setLight(manager.getLights().get(2), new LightState(0xf3)) + .build()); + + // Then all 3 should turn on. + assertThat(manager.getLightState(manager.getLights().get(0)).getColor()).isEqualTo(0xf1); + assertThat(manager.getLightState(manager.getLights().get(1)).getColor()).isEqualTo(0xf2); + assertThat(manager.getLightState(manager.getLights().get(2)).getColor()).isEqualTo(0xf3); + + // And the 4th should remain off. + assertThat(manager.getLightState(manager.getLights().get(3)).getColor()).isEqualTo(0x00); + } + + @Test + public void testControlLights_onlyEffectiveForLifetimeOfClient() { + LightsService service = new LightsService(mContext, mHal, Looper.getMainLooper()); + LightsManager manager = new LightsManager(mContext, service.mManagerService); + Light micLight = manager.getLights().get(0); + + // The light should begin by being off. + assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0x00000000); + + // When a session commits changes: + LightsManager.LightsSession session = manager.openSession(); + session.setLights(new Builder().setLight(micLight, new LightState(0xff00ff00)).build()); + // Then the light should turn on. + assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0xff00ff00); + + // When the session goes away: + session.close(); + // Then the light should turn off. + assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0x00000000); + } + + @Test + public void testControlLights_firstCallerWinsContention() { + LightsService service = new LightsService(mContext, mHal, Looper.getMainLooper()); + LightsManager manager = new LightsManager(mContext, service.mManagerService); + Light micLight = manager.getLights().get(0); + + LightsManager.LightsSession session1 = manager.openSession(); + LightsManager.LightsSession session2 = manager.openSession(); + + // When session1 and session2 both request the same light: + session1.setLights(new Builder().setLight(micLight, new LightState(0xff0000ff)).build()); + session2.setLights(new Builder().setLight(micLight, new LightState(0xffffffff)).build()); + // Then session1 should win because it was created first. + assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0xff0000ff); + + // When session1 goes away: + session1.close(); + // Then session2 should have its request go into effect. + assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0xffffffff); + + // When session2 goes away: + session2.close(); + // Then the light should turn off because there are no more sessions. + assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0); + } + + @Test + public void testClearLight() { + LightsService service = new LightsService(mContext, mHal, Looper.getMainLooper()); + LightsManager manager = new LightsManager(mContext, service.mManagerService); + Light micLight = manager.getLights().get(0); + + // When the session turns a light on: + LightsManager.LightsSession session = manager.openSession(); + session.setLights(new Builder().setLight(micLight, new LightState(0xffffffff)).build()); + + // And then the session clears it again: + session.setLights(new Builder().clearLight(micLight).build()); + + // Then the light should turn back off. + assertThat(manager.getLightState(micLight).getColor()).isEqualTo(0); + } +} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java index 8c3373faa0d4..32263e1cfe3d 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java @@ -72,7 +72,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.IntPair; import com.android.server.UiServiceTestCase; -import com.android.server.lights.Light; +import com.android.server.lights.LogicalLight; import org.junit.Before; import org.junit.Test; @@ -89,7 +89,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { @Mock AudioManager mAudioManager; @Mock Vibrator mVibrator; @Mock android.media.IRingtonePlayer mRingtonePlayer; - @Mock Light mLight; + @Mock LogicalLight mLight; @Mock NotificationManagerService.WorkerHandler mHandler; @Mock diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 0fce618012f9..f390ae6f2e8f 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -142,8 +142,8 @@ import com.android.internal.statusbar.NotificationVisibility; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.UiServiceTestCase; -import com.android.server.lights.Light; import com.android.server.lights.LightsManager; +import com.android.server.lights.LogicalLight; import com.android.server.notification.NotificationManagerService.NotificationAssistants; import com.android.server.notification.NotificationManagerService.NotificationListeners; import com.android.server.uri.UriGrantsManagerInternal; @@ -351,7 +351,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .thenReturn(applicationInfo); when(mPackageManagerClient.getPackageUidAsUser(any(), anyInt())).thenReturn(mUid); final LightsManager mockLightsManager = mock(LightsManager.class); - when(mockLightsManager.getLight(anyInt())).thenReturn(mock(Light.class)); + when(mockLightsManager.getLight(anyInt())).thenReturn(mock(LogicalLight.class)); when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false); when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner); diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java index 2abcc76fdccc..ec1c6c90905a 100644 --- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java +++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java @@ -28,6 +28,8 @@ import android.os.RemoteException; import android.os.SystemProperties; import java.io.PrintWriter; +import java.util.Collections; +import java.util.List; import java.util.function.Supplier; /** @@ -59,6 +61,11 @@ public final class TelephonyUtils { return str == null ? "" : str; } + /** Returns an empty list if the input is {@code null}. */ + public static @NonNull <T> List<T> emptyIfNull(@Nullable List<T> cur) { + return cur == null ? Collections.emptyList() : cur; + } + /** Throws a {@link RuntimeException} that wrapps the {@link RemoteException}. */ public static RuntimeException rethrowAsRuntimeException(RemoteException remoteException) { throw new RuntimeException(remoteException); diff --git a/telephony/common/com/google/android/mms/pdu/CharacterSets.java b/telephony/common/com/google/android/mms/pdu/CharacterSets.java index 5172b7b67f88..a3894d6d2d34 100644 --- a/telephony/common/com/google/android/mms/pdu/CharacterSets.java +++ b/telephony/common/com/google/android/mms/pdu/CharacterSets.java @@ -48,6 +48,51 @@ public class CharacterSets { public static final int UTF_16 = 0x03F7; /** + * Extend charsets. + * + * From http://www.iana.org/assignments/character-sets/ + */ + public static final int BIG5_HKSCS = 0x0835; //2101 + public static final int BOCU_1 = 0x03FC; //1020 + public static final int CESU_8 = 0x03F8; //1016 + public static final int CP864 = 0x0803; //2051 + public static final int EUC_JP = 0x12; //18 + public static final int EUC_KR = 0x26; //38 + public static final int GB18030 = 0x72; //114 + public static final int GBK = 0x71; //113 + public static final int HZ_GB_2312 = 0x0825; //2085 + public static final int GB_2312 = 0x07E9; //2025 + public static final int ISO_2022_CN = 0x68; //104 + public static final int ISO_2022_CN_EXT = 0x69; //105 + public static final int ISO_2022_JP = 0x27; //39 + public static final int ISO_2022_KR = 0x25; //37 + public static final int ISO_8859_10 = 0x0D; //13 + public static final int ISO_8859_13 = 0x6D; //109 + public static final int ISO_8859_14 = 0x6E; //110 + public static final int ISO_8859_15 = 0x6F; //111 + public static final int ISO_8859_16 = 0x70; //112 + public static final int KOI8_R = 0x0824; //2084 + public static final int KOI8_U = 0x0828; //2088 + public static final int MACINTOSH = 0x07EB; //2027 + public static final int SCSU = 0x03F3; //1011 + public static final int TIS_620 = 0x08D3; //2259 + public static final int UTF_16BE = 0x03F5; //1013 + public static final int UTF_16LE = 0x03F6; //1014 + public static final int UTF_32 = 0x03F9; //1017 + public static final int UTF_32BE = 0x03FA; //1018 + public static final int UTF_32LE = 0x03FB; //1019 + public static final int UTF_7 = 0x03F4; //1012 + public static final int WINDOWS_1250 = 0x08CA; //2250 + public static final int WINDOWS_1251 = 0x08CB; //2251 + public static final int WINDOWS_1252 = 0x08CC; //2252 + public static final int WINDOWS_1253 = 0x08CD; //2253 + public static final int WINDOWS_1254 = 0x08CE; //2254 + public static final int WINDOWS_1255 = 0x08CF; //2255 + public static final int WINDOWS_1256 = 0x08D0; //2256 + public static final int WINDOWS_1257 = 0x08D1; //2257 + public static final int WINDOWS_1258 = 0x08D2; //2258 + + /** * If the encoding of given data is unsupported, use UTF_8 to decode it. */ public static final int DEFAULT_CHARSET = UTF_8; @@ -72,6 +117,45 @@ public class CharacterSets { BIG5, UCS2, UTF_16, + BIG5_HKSCS, + BOCU_1, + CESU_8, + CP864, + EUC_JP, + EUC_KR, + GB18030, + GBK, + HZ_GB_2312, + GB_2312, + ISO_2022_CN, + ISO_2022_CN_EXT, + ISO_2022_JP, + ISO_2022_KR, + ISO_8859_10, + ISO_8859_13, + ISO_8859_14, + ISO_8859_15, + ISO_8859_16, + KOI8_R, + KOI8_U, + MACINTOSH, + SCSU, + TIS_620, + UTF_16BE, + UTF_16LE, + UTF_32, + UTF_32BE, + UTF_32LE, + UTF_7, + WINDOWS_1250, + WINDOWS_1251, + WINDOWS_1252, + WINDOWS_1253, + WINDOWS_1254, + WINDOWS_1255, + WINDOWS_1256, + WINDOWS_1257, + WINDOWS_1258, }; /** @@ -94,6 +178,51 @@ public class CharacterSets { public static final String MIMENAME_UCS2 = "iso-10646-ucs-2"; public static final String MIMENAME_UTF_16 = "utf-16"; + /** + * Extend charsets. + * + * From http://www.iana.org/assignments/character-sets/ + */ + public static final String MIMENAME_BIG5_HKSCS = "Big5-HKSCS"; + public static final String MIMENAME_BOCU_1 = "BOCU-1"; + public static final String MIMENAME_CESU_8 = "CESU-8"; + public static final String MIMENAME_CP864 = "cp864"; + public static final String MIMENAME_EUC_JP = "EUC-JP"; + public static final String MIMENAME_EUC_KR = "EUC-KR"; + public static final String MIMENAME_GB18030 = "GB18030"; + public static final String MIMENAME_GBK = "GBK"; + public static final String MIMENAME_HZ_GB_2312 = "HZ-GB-2312"; + public static final String MIMENAME_GB_2312 = "GB2312"; + public static final String MIMENAME_ISO_2022_CN = "ISO-2022-CN"; + public static final String MIMENAME_ISO_2022_CN_EXT = "ISO-2022-CN-EXT"; + public static final String MIMENAME_ISO_2022_JP = "ISO-2022-JP"; + public static final String MIMENAME_ISO_2022_KR = "ISO-2022-KR"; + public static final String MIMENAME_ISO_8859_10 = "ISO-8859-10"; + public static final String MIMENAME_ISO_8859_13 = "ISO-8859-13"; + public static final String MIMENAME_ISO_8859_14 = "ISO-8859-14"; + public static final String MIMENAME_ISO_8859_15 = "ISO-8859-15"; + public static final String MIMENAME_ISO_8859_16 = "ISO-8859-16"; + public static final String MIMENAME_KOI8_R = "KOI8-R"; + public static final String MIMENAME_KOI8_U = "KOI8-U"; + public static final String MIMENAME_MACINTOSH = "macintosh"; + public static final String MIMENAME_SCSU = "SCSU"; + public static final String MIMENAME_TIS_620 = "TIS-620"; + public static final String MIMENAME_UTF_16BE = "UTF-16BE"; + public static final String MIMENAME_UTF_16LE = "UTF-16LE"; + public static final String MIMENAME_UTF_32 = "UTF-32"; + public static final String MIMENAME_UTF_32BE = "UTF-32BE"; + public static final String MIMENAME_UTF_32LE = "UTF-32LE"; + public static final String MIMENAME_UTF_7 = "UTF-7"; + public static final String MIMENAME_WINDOWS_1250 = "windows-1250"; + public static final String MIMENAME_WINDOWS_1251 = "windows-1251"; + public static final String MIMENAME_WINDOWS_1252 = "windows-1252"; + public static final String MIMENAME_WINDOWS_1253 = "windows-1253"; + public static final String MIMENAME_WINDOWS_1254 = "windows-1254"; + public static final String MIMENAME_WINDOWS_1255 = "windows-1255"; + public static final String MIMENAME_WINDOWS_1256 = "windows-1256"; + public static final String MIMENAME_WINDOWS_1257 = "windows-1257"; + public static final String MIMENAME_WINDOWS_1258 = "windows-1258"; + public static final String DEFAULT_CHARSET_NAME = MIMENAME_UTF_8; /** @@ -116,6 +245,45 @@ public class CharacterSets { MIMENAME_BIG5, MIMENAME_UCS2, MIMENAME_UTF_16, + MIMENAME_BIG5_HKSCS, + MIMENAME_BOCU_1, + MIMENAME_CESU_8, + MIMENAME_CP864, + MIMENAME_EUC_JP, + MIMENAME_EUC_KR, + MIMENAME_GB18030, + MIMENAME_GBK, + MIMENAME_HZ_GB_2312, + MIMENAME_GB_2312, + MIMENAME_ISO_2022_CN, + MIMENAME_ISO_2022_CN_EXT, + MIMENAME_ISO_2022_JP, + MIMENAME_ISO_2022_KR, + MIMENAME_ISO_8859_10, + MIMENAME_ISO_8859_13, + MIMENAME_ISO_8859_14, + MIMENAME_ISO_8859_15, + MIMENAME_ISO_8859_16, + MIMENAME_KOI8_R, + MIMENAME_KOI8_U, + MIMENAME_MACINTOSH, + MIMENAME_SCSU, + MIMENAME_TIS_620, + MIMENAME_UTF_16BE, + MIMENAME_UTF_16LE, + MIMENAME_UTF_32, + MIMENAME_UTF_32BE, + MIMENAME_UTF_32LE, + MIMENAME_UTF_7, + MIMENAME_WINDOWS_1250, + MIMENAME_WINDOWS_1251, + MIMENAME_WINDOWS_1252, + MIMENAME_WINDOWS_1253, + MIMENAME_WINDOWS_1254, + MIMENAME_WINDOWS_1255, + MIMENAME_WINDOWS_1256, + MIMENAME_WINDOWS_1257, + MIMENAME_WINDOWS_1258, }; private static final HashMap<Integer, String> MIBENUM_TO_NAME_MAP; diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java index 90244b3df350..c66aceba95d4 100644 --- a/telephony/java/android/telephony/PhoneCapability.java +++ b/telephony/java/android/telephony/PhoneCapability.java @@ -25,7 +25,7 @@ import android.telephony.AccessNetworkConstants.AccessNetworkType; import android.telephony.AccessNetworkConstants.RadioAccessNetworkType; import android.telephony.TelephonyManager.NetworkTypeBitMask; -import com.android.internal.util.CollectionUtils; +import com.android.internal.telephony.util.TelephonyUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -181,13 +181,13 @@ public final class PhoneCapability implements Parcelable { this.mEutranUeCategoryUl = eutranUeCategoryUl; this.mPsDataConnectionLingerTimeMillis = psDataConnectionLingerTimeMillis; this.mSupportedRats = supportedRats; - this.mGeranBands = CollectionUtils.emptyIfNull(geranBands); - this.mUtranBands = CollectionUtils.emptyIfNull(utranBands); - this.mEutranBands = CollectionUtils.emptyIfNull(eutranBands); - this.mNgranBands = CollectionUtils.emptyIfNull(ngranBands); - this.mLogicalModemUuids = CollectionUtils.emptyIfNull(logicalModemUuids); - this.mSimSlotCapabilities = CollectionUtils.emptyIfNull(simSlotCapabilities); - this.mConcurrentFeaturesSupport = CollectionUtils.emptyIfNull(concurrentFeaturesSupport); + this.mGeranBands = TelephonyUtils.emptyIfNull(geranBands); + this.mUtranBands = TelephonyUtils.emptyIfNull(utranBands); + this.mEutranBands = TelephonyUtils.emptyIfNull(eutranBands); + this.mNgranBands = TelephonyUtils.emptyIfNull(ngranBands); + this.mLogicalModemUuids = TelephonyUtils.emptyIfNull(logicalModemUuids); + this.mSimSlotCapabilities = TelephonyUtils.emptyIfNull(simSlotCapabilities); + this.mConcurrentFeaturesSupport = TelephonyUtils.emptyIfNull(concurrentFeaturesSupport); } private PhoneCapability(Parcel in) { |