diff options
135 files changed, 4813 insertions, 1791 deletions
diff --git a/Android.bp b/Android.bp index fc713ff25e54..5851f436f5af 100644 --- a/Android.bp +++ b/Android.bp @@ -82,6 +82,7 @@ filegroup { ":framework-mca-filterpacks-sources", ":framework-media-sources", ":framework-mms-sources", + ":framework-omapi-sources", ":framework-opengl-sources", ":framework-rs-sources", ":framework-sax-sources", @@ -268,6 +269,7 @@ java_library { "android.hardware.vibrator-V1.2-java", "android.hardware.vibrator-V1.3-java", "android.hardware.vibrator-V2-java", + "android.se.omapi-V1-java", "android.system.suspend.control.internal-java", "devicepolicyprotosnano", @@ -328,6 +330,7 @@ java_defaults { "TeleService-platform-compat-config", "documents-ui-compat-config", "calendar-provider-compat-config", + "contacts-provider-platform-compat-config", ], libs: [ "app-compat-annotations", @@ -563,6 +566,7 @@ stubs_defaults { "android.hardware.vibrator-V1.3-java", "framework-protos", "art.module.public.api", + "sdk_module-lib_current_framework-tethering", // There are a few classes from modules used by the core that // need to be resolved by metalava. We use a prebuilt stub of the // full sdk to ensure we can resolve them. If a new class gets added, diff --git a/GAME_MANAGER_OWNERS b/GAME_MANAGER_OWNERS new file mode 100644 index 000000000000..502a9e362202 --- /dev/null +++ b/GAME_MANAGER_OWNERS @@ -0,0 +1,2 @@ +lpy@google.com +timvp@google.com diff --git a/StubLibraries.bp b/StubLibraries.bp index 44c55c26153d..d090296523ed 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -318,6 +318,7 @@ java_library { defaults: ["android-non-updatable_defaults_stubs_current"], srcs: [":module-lib-api-stubs-docs-non-updatable"], libs: [ + "sdk_module-lib_current_framework-tethering", "sdk_system_current_android", // NOTE: The below can be removed once the prebuilt stub contains IKE. "sdk_system_current_android.net.ipsec.ike", diff --git a/boot/boot-image-profile.txt b/boot/boot-image-profile.txt index 5f27cc722105..82269d4a2467 100644 --- a/boot/boot-image-profile.txt +++ b/boot/boot-image-profile.txt @@ -2899,10 +2899,8 @@ HSPLandroid/app/assist/AssistStructure$ViewNode;->getAutofillId()Landroid/view/a HSPLandroid/app/assist/AssistStructure$ViewNode;->getChildCount()I HSPLandroid/app/assist/AssistStructure$ViewNode;->writeSelfToParcel(Landroid/os/Parcel;Landroid/os/PooledStringWriter;Z[FZ)I+]Landroid/view/autofill/AutofillId;Landroid/view/autofill/AutofillId;]Landroid/graphics/Matrix;Landroid/graphics/Matrix;]Landroid/app/assist/AssistStructure$ViewNodeText;Landroid/app/assist/AssistStructure$ViewNodeText;]Landroid/os/Parcel;Landroid/os/Parcel; HSPLandroid/app/assist/AssistStructure$ViewNode;->writeString(Landroid/os/Parcel;Landroid/os/PooledStringWriter;Ljava/lang/String;)V+]Landroid/os/PooledStringWriter;Landroid/os/PooledStringWriter;]Landroid/os/Parcel;Landroid/os/Parcel; -HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;-><init>()V HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;->getChildCount()I HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;->getNodeText()Landroid/app/assist/AssistStructure$ViewNodeText; -HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;->getViewNode()Landroid/app/assist/AssistStructure$ViewNode; HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;->newChild(I)Landroid/view/ViewStructure; HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;->setAutofillHints([Ljava/lang/String;)V HSPLandroid/app/assist/AssistStructure$ViewNodeBuilder;->setAutofillId(Landroid/view/autofill/AutofillId;)V @@ -3883,8 +3881,6 @@ HSPLandroid/content/ContentResolver$ResultListener;-><init>()V HSPLandroid/content/ContentResolver$ResultListener;-><init>(Landroid/content/ContentResolver$1;)V HSPLandroid/content/ContentResolver$ResultListener;->onResult(Landroid/os/Bundle;)V+]Landroid/os/ParcelableException;Landroid/os/ParcelableException;]Ljava/lang/Object;Landroid/content/ContentResolver$StringResultListener;,Landroid/content/ContentResolver$UriResultListener;]Landroid/os/Bundle;Landroid/os/Bundle;]Landroid/content/ContentResolver$ResultListener;Landroid/content/ContentResolver$UriResultListener;,Landroid/content/ContentResolver$StringResultListener; HSPLandroid/content/ContentResolver$ResultListener;->waitForResult(J)V+]Ljava/lang/Object;Landroid/content/ContentResolver$StringResultListener; -HSPLandroid/content/ContentResolver$StringResultListener;-><init>()V -HSPLandroid/content/ContentResolver$StringResultListener;-><init>(Landroid/content/ContentResolver$1;)V HSPLandroid/content/ContentResolver$StringResultListener;->getResultFromBundle(Landroid/os/Bundle;)Ljava/lang/Object;+]Landroid/content/ContentResolver$StringResultListener;Landroid/content/ContentResolver$StringResultListener; HSPLandroid/content/ContentResolver$StringResultListener;->getResultFromBundle(Landroid/os/Bundle;)Ljava/lang/String;+]Landroid/os/Bundle;Landroid/os/Bundle; HSPLandroid/content/ContentResolver;-><init>(Landroid/content/Context;)V @@ -7029,7 +7025,6 @@ HSPLandroid/graphics/RectF;->width()F HSPLandroid/graphics/Region$1;->createFromParcel(Landroid/os/Parcel;)Landroid/graphics/Region; HSPLandroid/graphics/Region$1;->createFromParcel(Landroid/os/Parcel;)Ljava/lang/Object;+]Landroid/graphics/Region$1;Landroid/graphics/Region$1; HSPLandroid/graphics/Region;-><init>()V -HSPLandroid/graphics/Region;-><init>(IIII)V HSPLandroid/graphics/Region;-><init>(J)V HSPLandroid/graphics/Region;->access$000(Landroid/os/Parcel;)J HSPLandroid/graphics/Region;->equals(Ljava/lang/Object;)Z @@ -7075,7 +7070,6 @@ HSPLandroid/graphics/RenderNode;->getTranslationZ()F HSPLandroid/graphics/RenderNode;->hasDisplayList()Z HSPLandroid/graphics/RenderNode;->hasIdentityMatrix()Z HSPLandroid/graphics/RenderNode;->isAttached()Z+]Landroid/graphics/RenderNode$AnimationHost;Landroid/view/ViewAnimationHostBridge; -HSPLandroid/graphics/RenderNode;->isPivotExplicitlySet()Z HSPLandroid/graphics/RenderNode;->offsetTopAndBottom(I)Z HSPLandroid/graphics/RenderNode;->setAlpha(F)Z HSPLandroid/graphics/RenderNode;->setAnimationMatrix(Landroid/graphics/Matrix;)Z+]Landroid/graphics/Matrix;Landroid/graphics/Matrix; @@ -8283,7 +8277,6 @@ HSPLandroid/hardware/CameraStatus$1;->newArray(I)[Ljava/lang/Object; HSPLandroid/hardware/GeomagneticField$LegendreTable;-><init>(IF)V HSPLandroid/hardware/GeomagneticField;-><init>(FFFJ)V HSPLandroid/hardware/GeomagneticField;->computeGeocentricCoordinates(FFF)V -HSPLandroid/hardware/GeomagneticField;->getDeclination()F HSPLandroid/hardware/HardwareBuffer$1;->createFromParcel(Landroid/os/Parcel;)Landroid/hardware/HardwareBuffer; HSPLandroid/hardware/HardwareBuffer$1;->createFromParcel(Landroid/os/Parcel;)Ljava/lang/Object; HSPLandroid/hardware/HardwareBuffer;-><init>(J)V+]Llibcore/util/NativeAllocationRegistry;Llibcore/util/NativeAllocationRegistry;]Ljava/lang/Class;Ljava/lang/Class;]Ldalvik/system/CloseGuard;Ldalvik/system/CloseGuard; @@ -11353,7 +11346,6 @@ HSPLandroid/media/SoundPool$Builder;->setMaxStreams(I)Landroid/media/SoundPool$B HSPLandroid/media/SoundPool$EventHandler;->handleMessage(Landroid/os/Message;)V HSPLandroid/media/SoundPool;-><init>(ILandroid/media/AudioAttributes;)V HSPLandroid/media/SoundPool;-><init>(ILandroid/media/AudioAttributes;Landroid/media/SoundPool$1;)V -HSPLandroid/media/SoundPool;->load(Ljava/lang/String;I)I HSPLandroid/media/SoundPool;->postEventFromNative(Ljava/lang/Object;IIILjava/lang/Object;)V HSPLandroid/media/SoundPool;->setOnLoadCompleteListener(Landroid/media/SoundPool$OnLoadCompleteListener;)V HSPLandroid/media/SubtitleController$1;->handleMessage(Landroid/os/Message;)Z @@ -11389,10 +11381,8 @@ HSPLandroid/media/Utils;->parseSizeRange(Ljava/lang/Object;)Landroid/util/Pair; HSPLandroid/media/Utils;->sortDistinctRanges([Landroid/util/Range;)V HSPLandroid/media/audiofx/AudioEffect$Descriptor;-><init>(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V HSPLandroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;-><init>(II[Landroid/media/AudioAttributes;)V -HSPLandroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;->supportsStreamType(I)Z HSPLandroid/media/audiopolicy/AudioProductStrategy;-><init>(Ljava/lang/String;I[Landroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;)V HSPLandroid/media/audiopolicy/AudioProductStrategy;->attributesMatches(Landroid/media/AudioAttributes;Landroid/media/AudioAttributes;)Z+]Landroid/media/AudioAttributes;Landroid/media/AudioAttributes; -HSPLandroid/media/audiopolicy/AudioProductStrategy;->getAudioAttributesForLegacyStreamType(I)Landroid/media/AudioAttributes;+]Landroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;Landroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup; HSPLandroid/media/audiopolicy/AudioProductStrategy;->getAudioAttributesForStrategyWithLegacyStreamType(I)Landroid/media/AudioAttributes; HSPLandroid/media/audiopolicy/AudioProductStrategy;->getAudioProductStrategies()Ljava/util/List; HSPLandroid/media/audiopolicy/AudioProductStrategy;->getLegacyStreamTypeForStrategyWithAudioAttributes(Landroid/media/AudioAttributes;)I+]Landroid/media/audiopolicy/AudioProductStrategy;Landroid/media/audiopolicy/AudioProductStrategy;]Ljava/util/List;Ljava/util/ArrayList;]Ljava/util/Iterator;Ljava/util/ArrayList$Itr; @@ -13125,7 +13115,6 @@ HSPLandroid/os/ParcelableException;-><init>(Ljava/lang/Throwable;)V HSPLandroid/os/ParcelableParcel$1;->createFromParcel(Landroid/os/Parcel;)Landroid/os/ParcelableParcel; HSPLandroid/os/ParcelableParcel$1;->createFromParcel(Landroid/os/Parcel;)Ljava/lang/Object; HSPLandroid/os/ParcelableParcel;-><init>(Landroid/os/Parcel;Ljava/lang/ClassLoader;)V -HSPLandroid/os/ParcelableParcel;-><init>(Ljava/lang/ClassLoader;)V HSPLandroid/os/ParcelableParcel;->getClassLoader()Ljava/lang/ClassLoader; HSPLandroid/os/ParcelableParcel;->getParcel()Landroid/os/Parcel; HSPLandroid/os/ParcelableParcel;->writeToParcel(Landroid/os/Parcel;I)V @@ -13481,7 +13470,6 @@ HSPLandroid/os/TelephonyServiceManager;->getTelephonyServiceRegisterer()Landroid HSPLandroid/os/Temperature;-><init>(FILjava/lang/String;I)V HSPLandroid/os/Temperature;->getStatus()I HSPLandroid/os/Temperature;->isValidStatus(I)Z -HSPLandroid/os/Temperature;->isValidType(I)Z HSPLandroid/os/ThreadLocalWorkSource$$ExternalSyntheticLambda0;->get()Ljava/lang/Object; HSPLandroid/os/ThreadLocalWorkSource;->getToken()J+]Ljava/lang/Integer;Ljava/lang/Integer;]Ljava/lang/ThreadLocal;Ljava/lang/ThreadLocal$SuppliedThreadLocal; HSPLandroid/os/ThreadLocalWorkSource;->getUid()I+]Ljava/lang/Integer;Ljava/lang/Integer;]Ljava/lang/ThreadLocal;Ljava/lang/ThreadLocal$SuppliedThreadLocal; @@ -18488,7 +18476,6 @@ HSPLandroid/view/ViewRootImpl$HighContrastTextManager;-><init>(Landroid/view/Vie HSPLandroid/view/ViewRootImpl$ImeInputStage;-><init>(Landroid/view/ViewRootImpl;Landroid/view/ViewRootImpl$InputStage;Ljava/lang/String;)V HSPLandroid/view/ViewRootImpl$ImeInputStage;->onFinishedInputEvent(Ljava/lang/Object;Z)V HSPLandroid/view/ViewRootImpl$ImeInputStage;->onProcess(Landroid/view/ViewRootImpl$QueuedInputEvent;)I -HSPLandroid/view/ViewRootImpl$InputMetricsListener;-><init>(Landroid/view/ViewRootImpl;)V HSPLandroid/view/ViewRootImpl$InputMetricsListener;->onFrameMetricsAvailable(I)V+]Landroid/view/ViewRootImpl$WindowInputEventReceiver;Landroid/view/ViewRootImpl$WindowInputEventReceiver;]Ljava/lang/StringBuilder;Ljava/lang/StringBuilder;]Landroid/view/InputEventReceiver;Landroid/view/ViewRootImpl$WindowInputEventReceiver; HSPLandroid/view/ViewRootImpl$InputStage;-><init>(Landroid/view/ViewRootImpl;Landroid/view/ViewRootImpl$InputStage;)V HSPLandroid/view/ViewRootImpl$InputStage;->apply(Landroid/view/ViewRootImpl$QueuedInputEvent;I)V+]Landroid/view/ViewRootImpl$InputStage;megamorphic_types @@ -20395,7 +20382,6 @@ HSPLandroid/widget/OverScroller$SplineOverScroller;->finish()V HSPLandroid/widget/OverScroller$SplineOverScroller;->fling(IIIII)V HSPLandroid/widget/OverScroller$SplineOverScroller;->getSplineDeceleration(I)D HSPLandroid/widget/OverScroller$SplineOverScroller;->getSplineFlingDistance(I)D -HSPLandroid/widget/OverScroller$SplineOverScroller;->getSplineFlingDuration(I)I HSPLandroid/widget/OverScroller$SplineOverScroller;->onEdgeReached()V HSPLandroid/widget/OverScroller$SplineOverScroller;->springback(III)Z HSPLandroid/widget/OverScroller$SplineOverScroller;->startScroll(III)V @@ -22568,10 +22554,7 @@ HSPLcom/android/internal/widget/ILockSettings$Stub;->asInterface(Landroid/os/IBi HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker$1;->onIsNonStrongBiometricAllowedChanged(ZI)V HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker$1;->onStrongAuthRequiredChanged(II)V HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker$H;->handleMessage(Landroid/os/Message;)V -HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;-><init>(Landroid/content/Context;)V -HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;-><init>(Landroid/content/Context;Landroid/os/Looper;)V HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;->getStrongAuthForUser(I)I+]Landroid/util/SparseIntArray;Landroid/util/SparseIntArray; -HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;->getStub()Landroid/app/trust/IStrongAuthTracker$Stub; HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;->handleIsNonStrongBiometricAllowedChanged(ZI)V HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;->handleStrongAuthRequiredChanged(II)V HSPLcom/android/internal/widget/LockPatternUtils$StrongAuthTracker;->isNonStrongBiometricAllowedAfterIdleTimeout(I)Z diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 3109c5c1e075..6b8a775ef838 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -1348,7 +1348,7 @@ bool BootAnimation::playAnimation(const Animation& animation) { int err; do { err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, nullptr); - } while (err<0 && errno == EINTR); + } while (err == EINTR); } checkExit(); diff --git a/config/generate-preloaded-classes.sh b/config/generate-preloaded-classes.sh deleted file mode 100755 index b17a3660e1f1..000000000000 --- a/config/generate-preloaded-classes.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -if [ "$#" -lt 2 ]; then - echo "Usage $0 <input classes file> <denylist file> [extra classes files]" - exit 1 -fi - -# Write file headers first -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -cat "$DIR/copyright-header" -echo "# Preloaded-classes filter file for phones. -# -# Classes in this file will be allocated into the boot image, and forcibly initialized in -# the zygote during initialization. This is a trade-off, using virtual address space to share -# common heap between apps. -# -# This file has been derived for mainline phone (and tablet) usage. -#" - -input=$1 -denylist=$2 -shift 2 -extra_classes_files=("$@") - -# Disable locale to enable lexicographical sorting -LC_ALL=C sort "$input" "${extra_classes_files[@]}" | uniq | grep -f "$denylist" -v -F -x | grep -v "\$NoPreloadHolder" diff --git a/config/preloaded-classes-extra b/config/preloaded-classes-extra deleted file mode 100644 index 09f393ad4844..000000000000 --- a/config/preloaded-classes-extra +++ /dev/null @@ -1,14 +0,0 @@ -android.icu.impl.coll.CollationRoot -android.icu.impl.IDNA2003 -android.icu.impl.number.Parse -android.icu.util.TimeZone -android.media.ImageReader -android.media.MediaCodecList -android.media.MediaPlayer -android.media.SoundPool -android.text.format.Formatter -android.text.Html$HtmlParser -android.util.Log$PreloadHolder -com.android.org.conscrypt.TrustedCertificateStore -org.ccil.cowan.tagsoup.HTMLScanner -sun.security.jca.Providers diff --git a/core/api/Android.bp b/core/api/Android.bp index 170febb4f766..114a957674ae 100644 --- a/core/api/Android.bp +++ b/core/api/Android.bp @@ -51,11 +51,17 @@ filegroup { filegroup { name: "non-updatable-module-lib-current.txt", srcs: ["module-lib-current.txt"], - visibility: ["//frameworks/base/api"], + visibility: [ + "//frameworks/base/api", + "//cts/tests/signature/api", + ], } filegroup { name: "non-updatable-module-lib-removed.txt", srcs: ["module-lib-removed.txt"], - visibility: ["//frameworks/base/api"], + visibility: [ + "//frameworks/base/api", + "//cts/tests/signature/api", + ], } diff --git a/core/api/current.txt b/core/api/current.txt index f21518c7f83d..a70a0a904290 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -9546,6 +9546,7 @@ package android.bluetooth.le { method public java.util.Map<android.os.ParcelUuid,byte[]> getServiceData(); method @NonNull public java.util.List<android.os.ParcelUuid> getServiceSolicitationUuids(); method public java.util.List<android.os.ParcelUuid> getServiceUuids(); + method @NonNull public java.util.List<android.bluetooth.le.TransportDiscoveryData> getTransportDiscoveryData(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.AdvertiseData> CREATOR; } @@ -9556,6 +9557,7 @@ package android.bluetooth.le { method public android.bluetooth.le.AdvertiseData.Builder addServiceData(android.os.ParcelUuid, byte[]); method @NonNull public android.bluetooth.le.AdvertiseData.Builder addServiceSolicitationUuid(@NonNull android.os.ParcelUuid); method public android.bluetooth.le.AdvertiseData.Builder addServiceUuid(android.os.ParcelUuid); + method @NonNull public android.bluetooth.le.AdvertiseData.Builder addTransportDiscoveryData(@NonNull android.bluetooth.le.TransportDiscoveryData); method public android.bluetooth.le.AdvertiseData build(); method public android.bluetooth.le.AdvertiseData.Builder setIncludeDeviceName(boolean); method public android.bluetooth.le.AdvertiseData.Builder setIncludeTxPowerLevel(boolean); @@ -9815,6 +9817,31 @@ package android.bluetooth.le { method public android.bluetooth.le.ScanSettings.Builder setScanMode(int); } + public final class TransportBlock implements android.os.Parcelable { + ctor public TransportBlock(int, int, int, @Nullable byte[]); + method public int describeContents(); + method public int getOrgId(); + method public int getTdsFlags(); + method @Nullable public byte[] getTransportData(); + method public int getTransportDataLength(); + method @Nullable public byte[] toByteArray(); + method public int totalBytes(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.TransportBlock> CREATOR; + } + + public final class TransportDiscoveryData implements android.os.Parcelable { + ctor public TransportDiscoveryData(int, @NonNull java.util.List<android.bluetooth.le.TransportBlock>); + ctor public TransportDiscoveryData(@NonNull byte[]); + method public int describeContents(); + method @NonNull public java.util.List<android.bluetooth.le.TransportBlock> getTransportBlocks(); + method public int getTransportDataType(); + method @Nullable public byte[] toByteArray(); + method public int totalBytes(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.le.TransportDiscoveryData> CREATOR; + } + } package android.companion { @@ -27386,6 +27413,7 @@ package android.nfc.cardemulation { field public static final String CATEGORY_PAYMENT = "payment"; field public static final String EXTRA_CATEGORY = "category"; field public static final String EXTRA_SERVICE_COMPONENT = "component"; + field public static final String EXTRA_USERID = "android.nfc.cardemulation.extra.USERID"; field public static final int SELECTION_MODE_ALWAYS_ASK = 1; // 0x1 field public static final int SELECTION_MODE_ASK_IF_CONFLICT = 2; // 0x2 field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0 @@ -31446,9 +31474,9 @@ package android.os { method public byte[] marshall(); method @NonNull public static android.os.Parcel obtain(); method @NonNull public static android.os.Parcel obtain(@NonNull android.os.IBinder); - method @Nullable public Object[] readArray(@Nullable ClassLoader); + method @Deprecated @Nullable public Object[] readArray(@Nullable ClassLoader); method @Nullable public <T> T[] readArray(@Nullable ClassLoader, @NonNull Class<T>); - method @Nullable public java.util.ArrayList readArrayList(@Nullable ClassLoader); + method @Deprecated @Nullable public java.util.ArrayList readArrayList(@Nullable ClassLoader); method @Nullable public <T> java.util.ArrayList<T> readArrayList(@Nullable ClassLoader, @NonNull Class<? extends T>); method public void readBinderArray(@NonNull android.os.IBinder[]); method public void readBinderList(@NonNull java.util.List<android.os.IBinder>); @@ -31466,32 +31494,32 @@ package android.os { method public android.os.ParcelFileDescriptor readFileDescriptor(); method public float readFloat(); method public void readFloatArray(@NonNull float[]); - method @Nullable public java.util.HashMap readHashMap(@Nullable ClassLoader); + method @Deprecated @Nullable public java.util.HashMap readHashMap(@Nullable ClassLoader); method @Nullable public <K, V> java.util.HashMap<K,V> readHashMap(@Nullable ClassLoader, @NonNull Class<? extends K>, @NonNull Class<? extends V>); method public int readInt(); method public void readIntArray(@NonNull int[]); method public <T extends android.os.IInterface> void readInterfaceArray(@NonNull T[], @NonNull java.util.function.Function<android.os.IBinder,T>); method public <T extends android.os.IInterface> void readInterfaceList(@NonNull java.util.List<T>, @NonNull java.util.function.Function<android.os.IBinder,T>); - method public void readList(@NonNull java.util.List, @Nullable ClassLoader); + method @Deprecated public void readList(@NonNull java.util.List, @Nullable ClassLoader); method public <T> void readList(@NonNull java.util.List<? super T>, @Nullable ClassLoader, @NonNull Class<T>); method public long readLong(); method public void readLongArray(@NonNull long[]); - method public void readMap(@NonNull java.util.Map, @Nullable ClassLoader); + method @Deprecated public void readMap(@NonNull java.util.Map, @Nullable ClassLoader); method public <K, V> void readMap(@NonNull java.util.Map<? super K,? super V>, @Nullable ClassLoader, @NonNull Class<K>, @NonNull Class<V>); - method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader); + method @Deprecated @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader); method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>); method @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader); method @Nullable public <T> T[] readParcelableArray(@Nullable ClassLoader, @NonNull Class<T>); - method @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader); + method @Deprecated @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader); method @Nullable public <T> android.os.Parcelable.Creator<T> readParcelableCreator(@Nullable ClassLoader, @NonNull Class<T>); method @NonNull public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader); method @Nullable public android.os.PersistableBundle readPersistableBundle(); method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader); - method @Nullable public java.io.Serializable readSerializable(); + method @Deprecated @Nullable public java.io.Serializable readSerializable(); method @Nullable public <T extends java.io.Serializable> T readSerializable(@Nullable ClassLoader, @NonNull Class<T>); method @NonNull public android.util.Size readSize(); method @NonNull public android.util.SizeF readSizeF(); - method @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader); + method @Deprecated @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader); method @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader, @NonNull Class<? extends T>); method @Nullable public android.util.SparseBooleanArray readSparseBooleanArray(); method @Nullable public String readString(); @@ -43474,8 +43502,10 @@ package android.telephony.ims { method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiSettingEnabled(); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVtSettingEnabled(); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public void registerImsStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsStateCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void registerMmTelCapabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); + method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void unregisterMmTelCapabilityCallback(@NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback); field public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; // 0x1 field public static final int WIFI_MODE_WIFI_ONLY = 0; // 0x0 @@ -43492,7 +43522,9 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter(); method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRECISE_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE", "android.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE"}) public void registerImsStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsStateCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); + method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback); field public static final String ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN = "android.telephony.ims.action.SHOW_CAPABILITY_DISCOVERY_OPT_IN"; } @@ -43691,6 +43723,19 @@ package android.telephony.ims { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsRegistrationAttributes> CREATOR; } + public abstract class ImsStateCallback { + ctor public ImsStateCallback(); + method public abstract void onAvailable(); + method public abstract void onError(); + method public abstract void onUnavailable(int); + field public static final int REASON_IMS_SERVICE_DISCONNECTED = 3; // 0x3 + field public static final int REASON_IMS_SERVICE_NOT_READY = 6; // 0x6 + field public static final int REASON_NO_IMS_SERVICE_CONFIGURED = 4; // 0x4 + field public static final int REASON_SUBSCRIPTION_INACTIVE = 5; // 0x5 + field public static final int REASON_UNKNOWN_PERMANENT_ERROR = 2; // 0x2 + field public static final int REASON_UNKNOWN_TEMPORARY_ERROR = 1; // 0x1 + } + public class RcsUceAdapter { method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException; } diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 9af6c1b340b6..3a35f249d177 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -113,6 +113,7 @@ package android.media { public class AudioManager { method public void adjustStreamVolumeForUid(int, int, int, @NonNull String, int, int, int); method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int); + method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void handleBluetoothActiveDeviceChanged(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothDevice, @NonNull android.media.BtProfileConnectionInfo); method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setA2dpSuspended(boolean); method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setBluetoothHeadsetProperties(@NonNull String, boolean, boolean); method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpEnabled(boolean); @@ -122,6 +123,19 @@ package android.media { field public static final int FLAG_FROM_KEY = 4096; // 0x1000 } + public final class BtProfileConnectionInfo implements android.os.Parcelable { + method @NonNull public static android.media.BtProfileConnectionInfo a2dpInfo(boolean, int); + method public int describeContents(); + method public boolean getIsLeOutput(); + method public int getProfile(); + method public boolean getSuppressNoisyIntent(); + method public int getVolume(); + method @NonNull public static android.media.BtProfileConnectionInfo hearingAidInfo(boolean); + method @NonNull public static android.media.BtProfileConnectionInfo leAudio(boolean, boolean); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.media.BtProfileConnectionInfo> CREATOR; + } + public class MediaMetadataRetriever implements java.lang.AutoCloseable { field public static final int METADATA_KEY_VIDEO_CODEC_MIME_TYPE = 40; // 0x28 } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 78e6a07c8705..84a4a44c5cf0 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -13741,7 +13741,9 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void createSipDelegate(@NonNull android.telephony.ims.DelegateRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.DelegateConnectionStateCallback, @NonNull android.telephony.ims.stub.DelegateConnectionMessageCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void destroySipDelegate(@NonNull android.telephony.ims.SipDelegateConnection, int); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public boolean isSupported() throws android.telephony.ims.ImsException; + method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) public void registerImsStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsStateCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void triggerFullNetworkRegistration(@NonNull android.telephony.ims.SipDelegateConnection, @IntRange(from=100, to=699) int, @Nullable String); + method public void unregisterImsStateCallback(@NonNull android.telephony.ims.ImsStateCallback); field public static final int DENIED_REASON_INVALID = 4; // 0x4 field public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1; // 0x1 field public static final int DENIED_REASON_NOT_ALLOWED = 2; // 0x2 diff --git a/core/java/Android.bp b/core/java/Android.bp index e08a4931fc7d..ca9a46847dea 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -343,7 +343,6 @@ aidl_interface { filegroup { name: "framework-telephony-common-shared-srcs", srcs: [ - ":modules-utils-preconditions-srcs", "android/os/RegistrantList.java", "android/os/Registrant.java", "android/util/IndentingPrintWriter.java", @@ -355,10 +354,7 @@ filegroup { "com/android/internal/util/BitwiseInputStream.java", "com/android/internal/util/FastXmlSerializer.java", "com/android/internal/util/HexDump.java", - "com/android/internal/util/IState.java", "com/android/internal/util/IndentingPrintWriter.java", - "com/android/internal/util/State.java", - "com/android/internal/util/StateMachine.java", "com/android/internal/util/UserIcons.java", ], } diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS index 4da51c13045b..2564228f23aa 100644 --- a/core/java/android/app/OWNERS +++ b/core/java/android/app/OWNERS @@ -29,6 +29,8 @@ per-file Service* = file:/services/core/java/com/android/server/am/OWNERS per-file SystemServiceRegistry.java = file:/services/core/java/com/android/server/am/OWNERS per-file *UserSwitchObserver* = file:/services/core/java/com/android/server/am/OWNERS per-file UiAutomation.java = file:/services/accessibility/OWNERS +per-file GameManager* = file:/GAME_MANAGER_OWNERS +per-file IGameManager* = file:/GAME_MANAGER_OWNERS # ActivityThread per-file ActivityThread.java = file:/services/core/java/com/android/server/am/OWNERS diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 8aa27853b462..51435796502e 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -552,6 +552,11 @@ public class AppWidgetHostView extends FrameLayout { inflateAsync(rvToApply); return; } + + // Prepare a local reference to the remote Context so we're ready to + // inflate any requested LayoutParams + mRemoteContext = getRemoteContext(); + int layoutId = rvToApply.getLayoutId(); if (rvToApply.canRecycleView(mView)) { try { diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index dac8ffe5e008..2c875fee101b 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -51,7 +51,6 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.Attributable; import android.content.AttributionSource; import android.content.Context; -import android.os.BatteryStats; import android.os.Binder; import android.os.Build; import android.os.IBinder; @@ -59,7 +58,6 @@ import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; -import android.os.SynchronousResultReceiver; import android.os.SystemProperties; import android.util.Log; import android.util.Pair; @@ -82,7 +80,6 @@ import java.util.Set; import java.util.UUID; import java.util.WeakHashMap; import java.util.concurrent.Executor; -import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantReadWriteLock; /** @@ -2022,6 +2019,7 @@ public final class BluetoothAdapter { * {@link BluetoothProfile#HEADSET}, * {@link BluetoothProfile#A2DP}, * {@link BluetoothProfile#HEARING_AID} + * {@link BluetoothProfile#LE_AUDIO} * @return A list of active bluetooth devices * @throws IllegalArgumentException If profile is not one of {@link ActiveDeviceProfile} * @hide @@ -2034,12 +2032,14 @@ public final class BluetoothAdapter { public @NonNull List<BluetoothDevice> getActiveDevices(@ActiveDeviceProfile int profile) { if (profile != BluetoothProfile.HEADSET && profile != BluetoothProfile.A2DP - && profile != BluetoothProfile.HEARING_AID) { + && profile != BluetoothProfile.HEARING_AID + && profile != BluetoothProfile.LE_AUDIO) { Log.e(TAG, "Invalid profile param value in getActiveDevices"); throw new IllegalArgumentException("Profiles must be one of " + "BluetoothProfile.A2DP, " + "BluetoothProfile.HEARING_AID, or" - + "BluetoothProfile.HEARING_AID"); + + "BluetoothProfile.HEARING_AID" + + "BluetoothProfile.LE_AUDIO"); } try { mServiceLock.readLock().lock(); @@ -2421,38 +2421,6 @@ public final class BluetoothAdapter { } /** - * Return the record of {@link BluetoothActivityEnergyInfo} object that - * has the activity and energy info. This can be used to ascertain what - * the controller has been up to, since the last sample. - * - * @param updateType Type of info, cached vs refreshed. - * @return a record with {@link BluetoothActivityEnergyInfo} or null if report is unavailable or - * unsupported - * @hide - * @deprecated use the asynchronous {@link #requestControllerActivityEnergyInfo(ResultReceiver)} - * instead. - */ - @Deprecated - @RequiresBluetoothConnectPermission - @RequiresPermission(allOf = { - android.Manifest.permission.BLUETOOTH_CONNECT, - android.Manifest.permission.BLUETOOTH_PRIVILEGED, - }) - public BluetoothActivityEnergyInfo getControllerActivityEnergyInfo(int updateType) { - SynchronousResultReceiver receiver = new SynchronousResultReceiver(); - requestControllerActivityEnergyInfo(receiver); - try { - SynchronousResultReceiver.Result result = receiver.awaitResult(1000); - if (result.bundle != null) { - return result.bundle.getParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY); - } - } catch (TimeoutException e) { - Log.e(TAG, "getControllerActivityEnergyInfo timed out"); - } - return null; - } - - /** * Request the record of {@link BluetoothActivityEnergyInfo} object that * has the activity and energy info. This can be used to ascertain what * the controller has been up to, since the last sample. diff --git a/core/java/android/bluetooth/le/AdvertiseData.java b/core/java/android/bluetooth/le/AdvertiseData.java index cec658049ca5..fdf62ec3a647 100644 --- a/core/java/android/bluetooth/le/AdvertiseData.java +++ b/core/java/android/bluetooth/le/AdvertiseData.java @@ -25,6 +25,7 @@ import android.util.ArrayMap; import android.util.SparseArray; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -47,6 +48,9 @@ public final class AdvertiseData implements Parcelable { @NonNull private final List<ParcelUuid> mServiceSolicitationUuids; + @Nullable + private final List<TransportDiscoveryData> mTransportDiscoveryData; + private final SparseArray<byte[]> mManufacturerSpecificData; private final Map<ParcelUuid, byte[]> mServiceData; private final boolean mIncludeTxPowerLevel; @@ -54,12 +58,14 @@ public final class AdvertiseData implements Parcelable { private AdvertiseData(List<ParcelUuid> serviceUuids, List<ParcelUuid> serviceSolicitationUuids, + List<TransportDiscoveryData> transportDiscoveryData, SparseArray<byte[]> manufacturerData, Map<ParcelUuid, byte[]> serviceData, boolean includeTxPowerLevel, boolean includeDeviceName) { mServiceUuids = serviceUuids; mServiceSolicitationUuids = serviceSolicitationUuids; + mTransportDiscoveryData = transportDiscoveryData; mManufacturerSpecificData = manufacturerData; mServiceData = serviceData; mIncludeTxPowerLevel = includeTxPowerLevel; @@ -83,6 +89,17 @@ public final class AdvertiseData implements Parcelable { } /** + * Returns a list of {@link TransportDiscoveryData} within the advertisement. + */ + @NonNull + public List<TransportDiscoveryData> getTransportDiscoveryData() { + if (mTransportDiscoveryData == null) { + return Collections.emptyList(); + } + return mTransportDiscoveryData; + } + + /** * Returns an array of manufacturer Id and the corresponding manufacturer specific data. The * manufacturer id is a non-negative number assigned by Bluetooth SIG. */ @@ -116,8 +133,8 @@ public final class AdvertiseData implements Parcelable { */ @Override public int hashCode() { - return Objects.hash(mServiceUuids, mServiceSolicitationUuids, mManufacturerSpecificData, - mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel); + return Objects.hash(mServiceUuids, mServiceSolicitationUuids, mTransportDiscoveryData, + mManufacturerSpecificData, mServiceData, mIncludeDeviceName, mIncludeTxPowerLevel); } /** @@ -134,6 +151,7 @@ public final class AdvertiseData implements Parcelable { AdvertiseData other = (AdvertiseData) obj; return Objects.equals(mServiceUuids, other.mServiceUuids) && Objects.equals(mServiceSolicitationUuids, other.mServiceSolicitationUuids) + && Objects.equals(mTransportDiscoveryData, other.mTransportDiscoveryData) && BluetoothLeUtils.equals(mManufacturerSpecificData, other.mManufacturerSpecificData) && BluetoothLeUtils.equals(mServiceData, other.mServiceData) @@ -144,7 +162,8 @@ public final class AdvertiseData implements Parcelable { @Override public String toString() { return "AdvertiseData [mServiceUuids=" + mServiceUuids + ", mServiceSolicitationUuids=" - + mServiceSolicitationUuids + ", mManufacturerSpecificData=" + + mServiceSolicitationUuids + ", mTransportDiscoveryData=" + + mTransportDiscoveryData + ", mManufacturerSpecificData=" + BluetoothLeUtils.toString(mManufacturerSpecificData) + ", mServiceData=" + BluetoothLeUtils.toString(mServiceData) + ", mIncludeTxPowerLevel=" + mIncludeTxPowerLevel + ", mIncludeDeviceName=" @@ -162,6 +181,8 @@ public final class AdvertiseData implements Parcelable { dest.writeTypedArray(mServiceSolicitationUuids.toArray( new ParcelUuid[mServiceSolicitationUuids.size()]), flags); + dest.writeTypedList(mTransportDiscoveryData); + // mManufacturerSpecificData could not be null. dest.writeInt(mManufacturerSpecificData.size()); for (int i = 0; i < mManufacturerSpecificData.size(); ++i) { @@ -197,6 +218,12 @@ public final class AdvertiseData implements Parcelable { builder.addServiceSolicitationUuid(uuid); } + List<TransportDiscoveryData> transportDiscoveryData = + in.createTypedArrayList(TransportDiscoveryData.CREATOR); + for (TransportDiscoveryData tdd : transportDiscoveryData) { + builder.addTransportDiscoveryData(tdd); + } + int manufacturerSize = in.readInt(); for (int i = 0; i < manufacturerSize; ++i) { int manufacturerId = in.readInt(); @@ -223,6 +250,9 @@ public final class AdvertiseData implements Parcelable { private List<ParcelUuid> mServiceUuids = new ArrayList<ParcelUuid>(); @NonNull private List<ParcelUuid> mServiceSolicitationUuids = new ArrayList<ParcelUuid>(); + @Nullable + private List<TransportDiscoveryData> mTransportDiscoveryData = + new ArrayList<TransportDiscoveryData>(); private SparseArray<byte[]> mManufacturerSpecificData = new SparseArray<byte[]>(); private Map<ParcelUuid, byte[]> mServiceData = new ArrayMap<ParcelUuid, byte[]>(); private boolean mIncludeTxPowerLevel; @@ -256,6 +286,7 @@ public final class AdvertiseData implements Parcelable { mServiceSolicitationUuids.add(serviceSolicitationUuid); return this; } + /** * Add service data to advertise data. * @@ -274,6 +305,23 @@ public final class AdvertiseData implements Parcelable { } /** + * Add Transport Discovery Data to advertise data. + * + * @param transportDiscoveryData Transport Discovery Data, consisting of one or more + * Transport Blocks. Transport Discovery Data AD Type Code is already included. + * @throws IllegalArgumentException If the {@code transportDiscoveryData} is empty + */ + @NonNull + public Builder addTransportDiscoveryData( + @NonNull TransportDiscoveryData transportDiscoveryData) { + if (transportDiscoveryData == null) { + throw new IllegalArgumentException("transportDiscoveryData is null"); + } + mTransportDiscoveryData.add(transportDiscoveryData); + return this; + } + + /** * Add manufacturer specific data. * <p> * Please refer to the Bluetooth Assigned Numbers document provided by the <a @@ -319,8 +367,8 @@ public final class AdvertiseData implements Parcelable { */ public AdvertiseData build() { return new AdvertiseData(mServiceUuids, mServiceSolicitationUuids, - mManufacturerSpecificData, mServiceData, mIncludeTxPowerLevel, - mIncludeDeviceName); + mTransportDiscoveryData, mManufacturerSpecificData, mServiceData, + mIncludeTxPowerLevel, mIncludeDeviceName); } } } diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java index 58029745ab9c..b9f8a57a75ea 100644 --- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -567,6 +567,9 @@ public final class BluetoothLeAdvertiser { + num128BitUuids * BluetoothUuid.UUID_BYTES_128_BIT; } } + for (TransportDiscoveryData transportDiscoveryData : data.getTransportDiscoveryData()) { + size += OVERHEAD_BYTES_PER_FIELD + transportDiscoveryData.totalBytes(); + } for (ParcelUuid uuid : data.getServiceData().keySet()) { int uuidLen = BluetoothUuid.uuidToBytes(uuid).length; size += OVERHEAD_BYTES_PER_FIELD + uuidLen diff --git a/core/java/android/bluetooth/le/TransportBlock.java b/core/java/android/bluetooth/le/TransportBlock.java new file mode 100644 index 000000000000..b388beda6b3b --- /dev/null +++ b/core/java/android/bluetooth/le/TransportBlock.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2014 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.bluetooth.le; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; + +/** + * Wrapper for Transport Discovery Data Transport Blocks. + * This class represents a Transport Block from a Transport Discovery Data. + * + * @see TransportDiscoveryData + * @see AdvertiseData + */ +public final class TransportBlock implements Parcelable { + private static final String TAG = "TransportBlock"; + private final int mOrgId; + private final int mTdsFlags; + private final int mTransportDataLength; + private final byte[] mTransportData; + + /** + * Creates an instance of TransportBlock from raw data. + * + * @param orgId the Organization ID + * @param tdsFlags the TDS flags + * @param transportDataLength the total length of the Transport Data + * @param transportData the Transport Data + */ + public TransportBlock(int orgId, int tdsFlags, int transportDataLength, + @Nullable byte[] transportData) { + mOrgId = orgId; + mTdsFlags = tdsFlags; + mTransportDataLength = transportDataLength; + mTransportData = transportData; + } + + private TransportBlock(Parcel in) { + mOrgId = in.readInt(); + mTdsFlags = in.readInt(); + mTransportDataLength = in.readInt(); + mTransportData = new byte[mTransportDataLength]; + in.readByteArray(mTransportData); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mOrgId); + dest.writeInt(mTdsFlags); + dest.writeInt(mTransportDataLength); + dest.writeByteArray(mTransportData); + } + + /** + * @hide + */ + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Creator<TransportBlock> CREATOR = new Creator<TransportBlock>() { + @Override + public TransportBlock createFromParcel(Parcel in) { + return new TransportBlock(in); + } + + @Override + public TransportBlock[] newArray(int size) { + return new TransportBlock[size]; + } + }; + + /** + * Gets the Organization ID of the Transport Block which corresponds to one of the + * the Bluetooth SIG Assigned Numbers. + */ + public int getOrgId() { + return mOrgId; + } + + /** + * Gets the TDS flags of the Transport Block which represents the role of the device and + * information about its state and supported features. + */ + public int getTdsFlags() { + return mTdsFlags; + } + + /** + * Gets the total number of octets in the Transport Data field in this Transport Block. + */ + public int getTransportDataLength() { + return mTransportDataLength; + } + + /** + * Gets the Transport Data of the Transport Block which contains organization-specific data. + */ + @Nullable + public byte[] getTransportData() { + return mTransportData; + } + + /** + * Converts this TransportBlock to byte array + * + * @return byte array representation of this Transport Block or null if the conversion failed + */ + @Nullable + public byte[] toByteArray() { + try { + ByteBuffer buffer = ByteBuffer.allocate(totalBytes()); + buffer.put((byte) mOrgId); + buffer.put((byte) mTdsFlags); + buffer.put((byte) mTransportDataLength); + if (mTransportData != null) { + buffer.put(mTransportData); + } + return buffer.array(); + } catch (BufferOverflowException e) { + Log.e(TAG, "Error converting to byte array: " + e.toString()); + return null; + } + } + + /** + * @return total byte count of this TransportBlock + */ + public int totalBytes() { + // 3 uint8 + byte[] length + int size = 3 + mTransportDataLength; + return size; + } +} diff --git a/core/java/android/bluetooth/le/TransportDiscoveryData.java b/core/java/android/bluetooth/le/TransportDiscoveryData.java new file mode 100644 index 000000000000..c8e97f9a823a --- /dev/null +++ b/core/java/android/bluetooth/le/TransportDiscoveryData.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2014 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.bluetooth.le; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.nio.BufferOverflowException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Wrapper for Transport Discovery Data AD Type. + * This class contains the Transport Discovery Data AD Type Code as well as + * a list of potential Transport Blocks. + * + * @see AdvertiseData + */ +public final class TransportDiscoveryData implements Parcelable { + private static final String TAG = "TransportDiscoveryData"; + private final int mTransportDataType; + private final List<TransportBlock> mTransportBlocks; + + /** + * Creates a TransportDiscoveryData instance. + * + * @param transportDataType the Transport Discovery Data AD Type + * @param transportBlocks the list of Transport Blocks + */ + public TransportDiscoveryData(int transportDataType, + @NonNull List<TransportBlock> transportBlocks) { + mTransportDataType = transportDataType; + mTransportBlocks = transportBlocks; + } + + /** + * Creates a TransportDiscoveryData instance from byte arrays. + * + * Uses the transport discovery data bytes and parses them into an usable class. + * + * @param transportDiscoveryData the raw discovery data + */ + public TransportDiscoveryData(@NonNull byte[] transportDiscoveryData) { + ByteBuffer byteBuffer = ByteBuffer.wrap(transportDiscoveryData); + mTransportBlocks = new ArrayList(); + if (byteBuffer.remaining() > 0) { + mTransportDataType = byteBuffer.get(); + } else { + mTransportDataType = -1; + } + try { + while (byteBuffer.remaining() > 0) { + int orgId = byteBuffer.get(); + int tdsFlags = byteBuffer.get(); + int transportDataLength = byteBuffer.get(); + byte[] transportData = new byte[transportDataLength]; + byteBuffer.get(transportData, 0, transportDataLength); + mTransportBlocks.add(new TransportBlock(orgId, tdsFlags, + transportDataLength, transportData)); + } + } catch (BufferUnderflowException e) { + Log.e(TAG, "Error while parsing data: " + e.toString()); + } + } + + private TransportDiscoveryData(Parcel in) { + mTransportDataType = in.readInt(); + mTransportBlocks = in.createTypedArrayList(TransportBlock.CREATOR); + } + + /** + * @hide + */ + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mTransportDataType); + dest.writeTypedList(mTransportBlocks); + } + + public static final @NonNull Creator<TransportDiscoveryData> CREATOR = + new Creator<TransportDiscoveryData>() { + @Override + public TransportDiscoveryData createFromParcel(Parcel in) { + return new TransportDiscoveryData(in); + } + + @Override + public TransportDiscoveryData[] newArray(int size) { + return new TransportDiscoveryData[size]; + } + }; + + /** + * Gets the transport data type. + */ + public int getTransportDataType() { + return mTransportDataType; + } + + /** + * @return the list of {@link TransportBlock} in this TransportDiscoveryData + * or an empty list if there are no Transport Blocks + */ + @NonNull + public List<TransportBlock> getTransportBlocks() { + if (mTransportBlocks == null) { + return Collections.emptyList(); + } + return mTransportBlocks; + } + + /** + * Converts this TransportDiscoveryData to byte array + * + * @return byte array representation of this Transport Discovery Data or null if the + * conversion failed + */ + @Nullable + public byte[] toByteArray() { + try { + ByteBuffer buffer = ByteBuffer.allocate(totalBytes()); + buffer.put((byte) mTransportDataType); + for (TransportBlock transportBlock : getTransportBlocks()) { + buffer.put(transportBlock.toByteArray()); + } + return buffer.array(); + } catch (BufferOverflowException e) { + Log.e(TAG, "Error converting to byte array: " + e.toString()); + return null; + } + } + + /** + * @return total byte count of this TransportDataDiscovery + */ + public int totalBytes() { + int size = 1; // Counting Transport Data Type here. + for (TransportBlock transportBlock : getTransportBlocks()) { + size += transportBlock.totalBytes(); + } + return size; + } +} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 6885c10b4889..ceba01ec62e0 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -5462,6 +5462,12 @@ public abstract class Context { public static final String STATS_COMPANION_SERVICE = "statscompanion"; /** + * Service to assist statsd in logging atoms from bootstrap atoms. + * @hide + */ + public static final String STATS_BOOTSTRAP_ATOM_SERVICE = "statsbootstrap"; + + /** * Use with {@link #getSystemService(String)} to retrieve an {@link android.app.StatsManager}. * @hide */ @@ -6441,30 +6447,24 @@ public abstract class Context { @NonNull Configuration overrideConfiguration); /** - * Return a new Context object for the current Context but whose resources - * are adjusted to match the metrics of the given Display. Each call to this method - * returns a new instance of a Context object; Context objects are not - * shared, however common state (ClassLoader, other Resources for the - * same configuration) may be so the Context itself can be fairly lightweight. - * - * To obtain an instance of a {@link WindowManager} (see {@link #getSystemService(String)}) that - * is configured to show windows on the given display call - * {@link #createWindowContext(int, Bundle)} on the returned display Context or use an - * {@link android.app.Activity}. - * + * Returns a new <code>Context</code> object from the current context but with resources + * adjusted to match the metrics of <code>display</code>. Each call to this method + * returns a new instance of a context object. Context objects are not shared; however, + * common state (such as the {@link ClassLoader} and other resources for the same + * configuration) can be shared, so the <code>Context</code> itself is lightweight. * <p> - * Note that invoking #createDisplayContext(Display) from an UI context is not regarded - * as an UI context. In other words, it is not suggested to access UI components (such as - * obtain a {@link WindowManager} by {@link #getSystemService(String)}) - * from the context created from #createDisplayContext(Display). - * </p> - * - * @param display A {@link Display} object specifying the display for whose metrics the - * Context's resources should be tailored. + * To obtain an instance of {@link WindowManager} configured to show windows on the given + * display, call {@link #createWindowContext(int, Bundle)} on the returned display context, + * then call {@link #getSystemService(String)} or {@link #getSystemService(Class)} on the + * returned window context. + * <p> + * <b>Note:</b> The context returned by <code>createDisplayContext(Display)</code> is not a UI + * context. Do not access UI components or obtain a {@link WindowManager} from the context + * created by <code>createDisplayContext(Display)</code>. * - * @return A {@link Context} for the display. + * @param display The display to which the current context's resources are adjusted. * - * @see #getSystemService(String) + * @return A context for the display. */ @DisplayContext public abstract Context createDisplayContext(@NonNull Display display); diff --git a/core/java/android/net/nsd/INsdManager.aidl b/core/java/android/net/nsd/INsdManager.aidl index e9e8935a19b2..89e9cdbd4445 100644 --- a/core/java/android/net/nsd/INsdManager.aidl +++ b/core/java/android/net/nsd/INsdManager.aidl @@ -1,5 +1,5 @@ /** - * Copyright (c) 2012, The Android Open Source Project + * Copyright (c) 2021, 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. @@ -16,16 +16,15 @@ package android.net.nsd; +import android.net.nsd.INsdManagerCallback; +import android.net.nsd.INsdServiceConnector; import android.os.Messenger; /** - * Interface that NsdService implements + * Interface that NsdService implements to connect NsdManager clients. * * {@hide} */ -interface INsdManager -{ - @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553) - Messenger getMessenger(); - void setEnabled(boolean enable); +interface INsdManager { + INsdServiceConnector connect(INsdManagerCallback cb); } diff --git a/core/java/android/net/nsd/INsdManagerCallback.aidl b/core/java/android/net/nsd/INsdManagerCallback.aidl new file mode 100644 index 000000000000..1a262ec0e9dd --- /dev/null +++ b/core/java/android/net/nsd/INsdManagerCallback.aidl @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2021, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.nsd; + +import android.os.Messenger; +import android.net.nsd.NsdServiceInfo; + +/** + * Callbacks from NsdService to NsdManager + * @hide + */ +oneway interface INsdManagerCallback { + void onDiscoverServicesStarted(int listenerKey, in NsdServiceInfo info); + void onDiscoverServicesFailed(int listenerKey, int error); + void onServiceFound(int listenerKey, in NsdServiceInfo info); + void onServiceLost(int listenerKey, in NsdServiceInfo info); + void onStopDiscoveryFailed(int listenerKey, int error); + void onStopDiscoverySucceeded(int listenerKey); + void onRegisterServiceFailed(int listenerKey, int error); + void onRegisterServiceSucceeded(int listenerKey, in NsdServiceInfo info); + void onUnregisterServiceFailed(int listenerKey, int error); + void onUnregisterServiceSucceeded(int listenerKey); + void onResolveServiceFailed(int listenerKey, int error); + void onResolveServiceSucceeded(int listenerKey, in NsdServiceInfo info); +} diff --git a/core/java/android/net/nsd/INsdServiceConnector.aidl b/core/java/android/net/nsd/INsdServiceConnector.aidl new file mode 100644 index 000000000000..b06ae55b150e --- /dev/null +++ b/core/java/android/net/nsd/INsdServiceConnector.aidl @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2021, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.nsd; + +import android.net.nsd.INsdManagerCallback; +import android.net.nsd.NsdServiceInfo; +import android.os.Messenger; + +/** + * Interface that NsdService implements for each NsdManager client. + * + * {@hide} + */ +interface INsdServiceConnector { + void registerService(int listenerKey, in NsdServiceInfo serviceInfo); + void unregisterService(int listenerKey); + void discoverServices(int listenerKey, in NsdServiceInfo serviceInfo); + void stopDiscovery(int listenerKey); + void resolveService(int listenerKey, in NsdServiceInfo serviceInfo); + void startDaemon(); +}
\ No newline at end of file diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java index ae8d0101947d..6c597e26e042 100644 --- a/core/java/android/net/nsd/NsdManager.java +++ b/core/java/android/net/nsd/NsdManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2021 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. @@ -31,17 +31,13 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; -import android.os.Messenger; import android.os.RemoteException; import android.util.Log; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; -import java.util.concurrent.CountDownLatch; - /** * The Network Service Discovery Manager class provides the API to discover services * on a network. As an example, if device A and device B are connected over a Wi-Fi @@ -234,6 +230,11 @@ public final class NsdManager { /** @hide */ public static final int NATIVE_DAEMON_EVENT = BASE + 26; + /** @hide */ + public static final int REGISTER_CLIENT = BASE + 27; + /** @hide */ + public static final int UNREGISTER_CLIENT = BASE + 28; + /** Dns based service discovery protocol */ public static final int PROTOCOL_DNS_SD = 0x0001; @@ -274,7 +275,7 @@ public final class NsdManager { private static final int FIRST_LISTENER_KEY = 1; - private final INsdManager mService; + private final INsdServiceConnector mService; private final Context mContext; private int mListenerKey = FIRST_LISTENER_KEY; @@ -282,9 +283,7 @@ public final class NsdManager { private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<>(); private final Object mMapLock = new Object(); - private final AsyncChannel mAsyncChannel = new AsyncChannel(); - private ServiceHandler mHandler; - private final CountDownLatch mConnected = new CountDownLatch(1); + private final ServiceHandler mHandler; /** * Create a new Nsd instance. Applications use @@ -295,18 +294,108 @@ public final class NsdManager { * is a system private class. */ public NsdManager(Context context, INsdManager service) { - mService = service; mContext = context; - init(); + + HandlerThread t = new HandlerThread("NsdManager"); + t.start(); + mHandler = new ServiceHandler(t.getLooper()); + + try { + mService = service.connect(new NsdCallbackImpl(mHandler)); + } catch (RemoteException e) { + throw new RuntimeException("Failed to connect to NsdService"); + } + + // Only proactively start the daemon if the target SDK < S, otherwise the internal service + // would automatically start/stop the native daemon as needed. + if (!CompatChanges.isChangeEnabled(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)) { + try { + mService.startDaemon(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to proactively start daemon"); + // Continue: the daemon can still be started on-demand later + } + } } - /** - * @hide - */ - @VisibleForTesting - public void disconnect() { - mAsyncChannel.disconnect(); - mHandler.getLooper().quitSafely(); + private static class NsdCallbackImpl extends INsdManagerCallback.Stub { + private final Handler mServHandler; + + NsdCallbackImpl(Handler serviceHandler) { + mServHandler = serviceHandler; + } + + private void sendInfo(int message, int listenerKey, NsdServiceInfo info) { + mServHandler.sendMessage(mServHandler.obtainMessage(message, 0, listenerKey, info)); + } + + private void sendError(int message, int listenerKey, int error) { + mServHandler.sendMessage(mServHandler.obtainMessage(message, error, listenerKey)); + } + + private void sendNoArg(int message, int listenerKey) { + mServHandler.sendMessage(mServHandler.obtainMessage(message, 0, listenerKey)); + } + + @Override + public void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) { + sendInfo(DISCOVER_SERVICES_STARTED, listenerKey, info); + } + + @Override + public void onDiscoverServicesFailed(int listenerKey, int error) { + sendError(DISCOVER_SERVICES_FAILED, listenerKey, error); + } + + @Override + public void onServiceFound(int listenerKey, NsdServiceInfo info) { + sendInfo(SERVICE_FOUND, listenerKey, info); + } + + @Override + public void onServiceLost(int listenerKey, NsdServiceInfo info) { + sendInfo(SERVICE_LOST, listenerKey, info); + } + + @Override + public void onStopDiscoveryFailed(int listenerKey, int error) { + sendError(STOP_DISCOVERY_FAILED, listenerKey, error); + } + + @Override + public void onStopDiscoverySucceeded(int listenerKey) { + sendNoArg(STOP_DISCOVERY_SUCCEEDED, listenerKey); + } + + @Override + public void onRegisterServiceFailed(int listenerKey, int error) { + sendError(REGISTER_SERVICE_FAILED, listenerKey, error); + } + + @Override + public void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) { + sendInfo(REGISTER_SERVICE_SUCCEEDED, listenerKey, info); + } + + @Override + public void onUnregisterServiceFailed(int listenerKey, int error) { + sendError(UNREGISTER_SERVICE_FAILED, listenerKey, error); + } + + @Override + public void onUnregisterServiceSucceeded(int listenerKey) { + sendNoArg(UNREGISTER_SERVICE_SUCCEEDED, listenerKey); + } + + @Override + public void onResolveServiceFailed(int listenerKey, int error) { + sendError(RESOLVE_SERVICE_FAILED, listenerKey, error); + } + + @Override + public void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) { + sendInfo(RESOLVE_SERVICE_SUCCEEDED, listenerKey, info); + } } /** @@ -376,19 +465,6 @@ public final class NsdManager { public void handleMessage(Message message) { final int what = message.what; final int key = message.arg2; - switch (what) { - case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: - mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); - return; - case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED: - mConnected.countDown(); - return; - case AsyncChannel.CMD_CHANNEL_DISCONNECTED: - Log.e(TAG, "Channel lost"); - return; - default: - break; - } final Object listener; final NsdServiceInfo ns; synchronized (mMapLock) { @@ -504,36 +580,6 @@ public final class NsdManager { } /** - * Initialize AsyncChannel - */ - private void init() { - final Messenger messenger = getMessenger(); - if (messenger == null) { - fatal("Failed to obtain service Messenger"); - } - HandlerThread t = new HandlerThread("NsdManager"); - t.start(); - mHandler = new ServiceHandler(t.getLooper()); - mAsyncChannel.connect(mContext, mHandler, messenger); - try { - mConnected.await(); - } catch (InterruptedException e) { - fatal("Interrupted wait at init"); - } - if (CompatChanges.isChangeEnabled(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)) { - return; - } - // Only proactively start the daemon if the target SDK < S, otherwise the internal service - // would automatically start/stop the native daemon as needed. - mAsyncChannel.sendMessage(DAEMON_STARTUP); - } - - private static void fatal(String msg) { - Log.e(TAG, msg); - throw new RuntimeException(msg); - } - - /** * Register a service to be discovered by other services. * * <p> The function call immediately returns after sending a request to register service @@ -556,7 +602,11 @@ public final class NsdManager { checkServiceInfo(serviceInfo); checkProtocol(protocolType); int key = putListener(listener, serviceInfo); - mAsyncChannel.sendMessage(REGISTER_SERVICE, 0, key, serviceInfo); + try { + mService.registerService(key, serviceInfo); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } /** @@ -574,7 +624,11 @@ public final class NsdManager { */ public void unregisterService(RegistrationListener listener) { int id = getListenerKey(listener); - mAsyncChannel.sendMessage(UNREGISTER_SERVICE, 0, id); + try { + mService.unregisterService(id); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } /** @@ -613,7 +667,11 @@ public final class NsdManager { s.setServiceType(serviceType); int key = putListener(listener, s); - mAsyncChannel.sendMessage(DISCOVER_SERVICES, 0, key, s); + try { + mService.discoverServices(key, s); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } /** @@ -634,7 +692,11 @@ public final class NsdManager { */ public void stopServiceDiscovery(DiscoveryListener listener) { int id = getListenerKey(listener); - mAsyncChannel.sendMessage(STOP_DISCOVERY, 0, id); + try { + mService.stopDiscovery(id); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } } /** @@ -649,29 +711,10 @@ public final class NsdManager { public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) { checkServiceInfo(serviceInfo); int key = putListener(listener, serviceInfo); - mAsyncChannel.sendMessage(RESOLVE_SERVICE, 0, key, serviceInfo); - } - - /** Internal use only @hide */ - public void setEnabled(boolean enabled) { - try { - mService.setEnabled(enabled); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Get a reference to NsdService handler. This is used to establish - * an AsyncChannel communication with the service - * - * @return Messenger pointing to the NsdService handler - */ - private Messenger getMessenger() { try { - return mService.getMessenger(); + mService.resolveService(key, serviceInfo); } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + e.rethrowFromSystemServer(); } } diff --git a/core/java/android/net/nsd/NsdServiceInfo.aidl b/core/java/android/net/nsd/NsdServiceInfo.aidl new file mode 100644 index 000000000000..657bdd1e8706 --- /dev/null +++ b/core/java/android/net/nsd/NsdServiceInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.nsd; + +@JavaOnlyStableParcelable parcelable NsdServiceInfo;
\ No newline at end of file diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java index 0af322e885b1..09540132fe0d 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -528,6 +528,7 @@ public final class ApduServiceInfo implements Parcelable { public String toString() { StringBuilder out = new StringBuilder("ApduService: "); out.append(getComponent()); + out.append(", UID: " + mUid); out.append(", description: " + mDescription); out.append(", Static AID Groups: "); for (AidGroup aidGroup : mStaticAidGroups.values()) { @@ -546,7 +547,8 @@ public final class ApduServiceInfo implements Parcelable { if (!(o instanceof ApduServiceInfo)) return false; ApduServiceInfo thatService = (ApduServiceInfo) o; - return thatService.getComponent().equals(this.getComponent()); + return thatService.getComponent().equals(this.getComponent()) + && thatService.getUid() == this.getUid(); } @Override @@ -619,8 +621,9 @@ public final class ApduServiceInfo implements Parcelable { }; public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(" " + getComponent() + - " (Description: " + getDescription() + ")"); + pw.println(" " + getComponent() + + " (Description: " + getDescription() + ")" + + " (UID: " + getUid() + ")"); if (mOnHost) { pw.println(" On Host Service"); } else { diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java index d498535ce52c..0a9fe90f2524 100644 --- a/core/java/android/nfc/cardemulation/CardEmulation.java +++ b/core/java/android/nfc/cardemulation/CardEmulation.java @@ -30,6 +30,7 @@ import android.content.pm.PackageManager; import android.nfc.INfcCardEmulation; import android.nfc.NfcAdapter; import android.os.RemoteException; +import android.os.UserHandle; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Log; @@ -83,6 +84,13 @@ public final class CardEmulation { public static final String EXTRA_SERVICE_COMPONENT = "component"; /** + * The caller userId extra for {@link #ACTION_CHANGE_DEFAULT}. + * + * @see #ACTION_CHANGE_DEFAULT + */ + public static final String EXTRA_USERID = "android.nfc.cardemulation.extra.USERID"; + + /** * Category used for NFC payment services. */ public static final String CATEGORY_PAYMENT = "payment"; @@ -269,8 +277,8 @@ public final class CardEmulation { if (CATEGORY_PAYMENT.equals(category)) { boolean preferForeground = false; try { - preferForeground = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.NFC_PAYMENT_FOREGROUND) != 0; + preferForeground = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.NFC_PAYMENT_FOREGROUND, UserHandle.myUserId()) != 0; } catch (SettingNotFoundException e) { } return preferForeground; @@ -829,6 +837,28 @@ public final class CardEmulation { /** * @hide */ + public boolean setDefaultForNextTap(int userId, ComponentName service) { + try { + return sService.setDefaultForNextTap(userId, service); + } catch (RemoteException e) { + // Try one more time + recoverService(); + if (sService == null) { + Log.e(TAG, "Failed to recover CardEmulationService."); + return false; + } + try { + return sService.setDefaultForNextTap(userId, service); + } catch (RemoteException ee) { + Log.e(TAG, "Failed to reach CardEmulationService."); + return false; + } + } + } + + /** + * @hide + */ public List<ApduServiceInfo> getServices(String category) { try { return sService.getServices(mContext.getUserId(), category); @@ -849,6 +879,28 @@ public final class CardEmulation { } /** + * @hide + */ + public List<ApduServiceInfo> getServices(String category, int userId) { + try { + return sService.getServices(userId, category); + } catch (RemoteException e) { + // Try one more time + recoverService(); + if (sService == null) { + Log.e(TAG, "Failed to recover CardEmulationService."); + return null; + } + try { + return sService.getServices(userId, category); + } catch (RemoteException ee) { + Log.e(TAG, "Failed to reach CardEmulationService."); + return null; + } + } + } + + /** * A valid AID according to ISO/IEC 7816-4: * <ul> * <li>Has >= 5 bytes and <=16 bytes (>=10 hex chars and <= 32 hex chars) diff --git a/core/java/android/nfc/cardemulation/NfcFCardEmulation.java b/core/java/android/nfc/cardemulation/NfcFCardEmulation.java index 80e8579dd73f..557e41a2b103 100644 --- a/core/java/android/nfc/cardemulation/NfcFCardEmulation.java +++ b/core/java/android/nfc/cardemulation/NfcFCardEmulation.java @@ -25,7 +25,6 @@ import android.content.pm.PackageManager; import android.nfc.INfcFCardEmulation; import android.nfc.NfcAdapter; import android.os.RemoteException; -import android.os.UserHandle; import android.util.Log; import java.util.HashMap; diff --git a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java index c2b33dd51b0c..f8f7dfe034b5 100644 --- a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java +++ b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java @@ -237,6 +237,7 @@ public final class NfcFServiceInfo implements Parcelable { public String toString() { StringBuilder out = new StringBuilder("NfcFService: "); out.append(getComponent()); + out.append(", UID: " + mUid); out.append(", description: " + mDescription); out.append(", System Code: " + mSystemCode); if (mDynamicSystemCode != null) { @@ -257,6 +258,7 @@ public final class NfcFServiceInfo implements Parcelable { NfcFServiceInfo thatService = (NfcFServiceInfo) o; if (!thatService.getComponent().equals(this.getComponent())) return false; + if (thatService.getUid() != this.getUid()) return false; if (!thatService.mSystemCode.equalsIgnoreCase(this.mSystemCode)) return false; if (!thatService.mNfcid2.equalsIgnoreCase(this.mNfcid2)) return false; if (!thatService.mT3tPmm.equalsIgnoreCase(this.mT3tPmm)) return false; @@ -321,8 +323,9 @@ public final class NfcFServiceInfo implements Parcelable { }; public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(" " + getComponent() + - " (Description: " + getDescription() + ")"); + pw.println(" " + getComponent() + + " (Description: " + getDescription() + ")" + + " (UID: " + getUid() + ")"); pw.println(" System Code: " + getSystemCode()); pw.println(" NFCID2: " + getNfcid2()); pw.println(" T3tPmm: " + getT3tPmm()); diff --git a/core/java/android/os/IStatsBootstrapAtomService.aidl b/core/java/android/os/IStatsBootstrapAtomService.aidl new file mode 100644 index 000000000000..9d1df67fa19d --- /dev/null +++ b/core/java/android/os/IStatsBootstrapAtomService.aidl @@ -0,0 +1,36 @@ +/* + * Copyright 2021, 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.os; + +import android.os.StatsBootstrapAtom; + +/** + * IBootstrapAtomService interface exposes an interface for processes that launch in the + * bootstrap namespace to push atoms to statsd. + * + * @hide + */ +oneway interface IStatsBootstrapAtomService { + /** + * Push an atom to StatsBootstrapAtomService, which will forward it to statsd. + * + * @param atom - parcelled representation of the atom to log. + * + * Errors are reported as service specific errors. + */ + void reportBootstrapAtom(in StatsBootstrapAtom atom); +}
\ No newline at end of file diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 09e5a8f7382c..7bdb6b90c07b 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -2990,7 +2990,12 @@ public final class Parcel { * Please use {@link #readBundle(ClassLoader)} instead (whose data must have * been written with {@link #writeBundle}. Read into an existing Map object * from the parcel at the current dataPosition(). + * + * @deprecated Consider using {@link #readBundle(ClassLoader)} as stated above, in case this + * method is still preferred use the type-safer version {@link #readMap(Map, ClassLoader, + * Class, Class)} starting from Android {@link Build.VERSION_CODES#TIRAMISU}. */ + @Deprecated public final void readMap(@NonNull Map outVal, @Nullable ClassLoader loader) { int n = readInt(); readMapInternal(outVal, n, loader, /* clazzKey */ null, /* clazzValue */ null); @@ -3016,7 +3021,14 @@ public final class Parcel { * Read into an existing List object from the parcel at the current * dataPosition(), using the given class loader to load any enclosed * Parcelables. If it is null, the default class loader is used. + * + * @deprecated Use the type-safer version {@link #readList(List, ClassLoader, Class)} starting + * from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to + * use {@link #readTypedList(List, Parcelable.Creator)} if possible (eg. if the items' + * class is final) since this is also more performant. Note that changing to the latter + * also requires changing the writes. */ + @Deprecated public final void readList(@NonNull List outVal, @Nullable ClassLoader loader) { int N = readInt(); readListInternal(outVal, N, loader, /* clazz */ null); @@ -3043,10 +3055,14 @@ public final class Parcel { * object from the parcel at the current dataPosition(), using the given * class loader to load any enclosed Parcelables. Returns null if * the previously written map object was null. + * + * @deprecated Consider using {@link #readBundle(ClassLoader)} as stated above, in case this + * method is still preferred use the type-safer version {@link #readHashMap(ClassLoader, + * Class, Class)} starting from Android {@link Build.VERSION_CODES#TIRAMISU}. */ + @Deprecated @Nullable - public final HashMap readHashMap(@Nullable ClassLoader loader) - { + public HashMap readHashMap(@Nullable ClassLoader loader) { int n = readInt(); if (n < 0) { return null; @@ -3247,7 +3263,14 @@ public final class Parcel { * dataPosition(). Returns null if the previously written list object was * null. The given class loader will be used to load any enclosed * Parcelables. + * + * @deprecated Use the type-safer version {@link #readArrayList(ClassLoader, Class)} starting + * from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to + * use {@link #createTypedArrayList(Parcelable.Creator)} if possible (eg. if the items' + * class is final) since this is also more performant. Note that changing to the latter + * also requires changing the writes. */ + @Deprecated @Nullable public ArrayList readArrayList(@Nullable ClassLoader loader) { return readArrayListInternal(loader, /* clazz */ null); @@ -3274,7 +3297,14 @@ public final class Parcel { * dataPosition(). Returns null if the previously written array was * null. The given class loader will be used to load any enclosed * Parcelables. + * + * @deprecated Use the type-safer version {@link #readArray(ClassLoader, Class)} starting from + * Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to use + * {@link #createTypedArray(Parcelable.Creator)} if possible (eg. if the items' class is + * final) since this is also more performant. Note that changing to the latter also + * requires changing the writes. */ + @Deprecated @Nullable public Object[] readArray(@Nullable ClassLoader loader) { return readArrayInternal(loader, /* clazz */ null); @@ -3300,7 +3330,14 @@ public final class Parcel { * dataPosition(). Returns null if the previously written list object was * null. The given class loader will be used to load any enclosed * Parcelables. + * + * @deprecated Use the type-safer version {@link #readSparseArray(ClassLoader, Class)} starting + * from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to + * use {@link #createTypedSparseArray(Parcelable.Creator)} if possible (eg. if the items' + * class is final) since this is also more performant. Note that changing to the latter + * also requires changing the writes. */ + @Deprecated @Nullable public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader) { return readSparseArrayInternal(loader, /* clazz */ null); @@ -4107,7 +4144,13 @@ public final class Parcel { * object has been written. * @throws BadParcelableException Throws BadParcelableException if there * was an error trying to instantiate the Parcelable. + * + * @deprecated Use the type-safer version {@link #readParcelable(ClassLoader, Class)} starting + * from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the format to + * use {@link Parcelable.Creator#createFromParcel(Parcel)} if possible since this is also + * more performant. Note that changing to the latter also requires changing the writes. */ + @Deprecated @Nullable public final <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader) { return readParcelableInternal(loader, /* clazz */ null); @@ -4176,7 +4219,11 @@ public final class Parcel { * read the {@link Parcelable.Creator}. * * @see #writeParcelableCreator + * + * @deprecated Use the type-safer version {@link #readParcelableCreator(ClassLoader, Class)} + * starting from Android {@link Build.VERSION_CODES#TIRAMISU}. */ + @Deprecated @Nullable public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) { return readParcelableCreatorInternal(loader, /* clazz */ null); @@ -4337,7 +4384,11 @@ public final class Parcel { * Read and return a new Serializable object from the parcel. * @return the Serializable object, or null if the Serializable name * wasn't found in the parcel. + * + * @deprecated Use the type-safer version {@link #readSerializable(ClassLoader, Class)} starting + * from Android {@link Build.VERSION_CODES#TIRAMISU}. */ + @Deprecated @Nullable public Serializable readSerializable() { return readSerializableInternal(/* loader */ null, /* clazz */ null); diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java index 4e8418bd60ec..ba5ed4360cd7 100644 --- a/core/java/android/os/ServiceManager.java +++ b/core/java/android/os/ServiceManager.java @@ -298,6 +298,17 @@ public final class ServiceManager { } /** + * Register callback for service registration notifications. + * + * @throws RemoteException for underlying error. + * @hide + */ + public static void registerForNotifications( + @NonNull String name, @NonNull IServiceCallback callback) throws RemoteException { + getIServiceManager().registerForNotifications(name, callback); + } + + /** * Return a list of all currently running services. * @return an array of all currently running services, or <code>null</code> in * case of an exception diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java index 3739040eef60..2dcf67483203 100644 --- a/core/java/android/os/ServiceManagerNative.java +++ b/core/java/android/os/ServiceManagerNative.java @@ -78,7 +78,7 @@ class ServiceManagerProxy implements IServiceManager { public void registerForNotifications(String name, IServiceCallback cb) throws RemoteException { - throw new RemoteException(); + mServiceManager.registerForNotifications(name, cb); } public void unregisterForNotifications(String name, IServiceCallback cb) diff --git a/core/java/android/os/StatsBootstrapAtom.aidl b/core/java/android/os/StatsBootstrapAtom.aidl new file mode 100644 index 000000000000..47500af8eebf --- /dev/null +++ b/core/java/android/os/StatsBootstrapAtom.aidl @@ -0,0 +1,34 @@ +/* + * Copyright 2021, 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.os; + +import android.os.StatsBootstrapAtomValue; + +/* + * Generic encapsulation of an atom for bootstrap processes to log. + * + * @hide + */ +parcelable StatsBootstrapAtom { + /* + * Atom ID. Must be between 1 - 10,000. + */ + int atomId; + /* + * Vector of fields in the order of the atom definition. + */ + StatsBootstrapAtomValue[] values; + }
\ No newline at end of file diff --git a/core/java/android/os/StatsBootstrapAtomValue.aidl b/core/java/android/os/StatsBootstrapAtomValue.aidl new file mode 100644 index 000000000000..a90dfa404ee9 --- /dev/null +++ b/core/java/android/os/StatsBootstrapAtomValue.aidl @@ -0,0 +1,29 @@ +/* + * Copyright 2021, 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.os; +/* + * Supported field types. + * + * @hide + */ +union StatsBootstrapAtomValue { + boolean boolValue; + int intValue; + long longValue; + float floatValue; + String stringValue; + byte[] bytesValue; +}
\ No newline at end of file diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index f3a8b5d79ea0..9f3a847e12eb 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -5365,5 +5365,14 @@ public final class Telephony { */ public static final String COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS = "d2d_sharing_contacts"; + + /** + * TelephonyProvider column name for NR Advanced calling + * Determines if the user has enabled VoNR settings for this subscription. + * + * @hide + */ + public static final String COLUMN_NR_ADVANCED_CALLING_ENABLED = + "nr_advanced_calling_enabled"; } } diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java index 12bcd8b0aa97..b5fe4f58dfb2 100644 --- a/core/java/android/util/Log.java +++ b/core/java/android/util/Log.java @@ -62,6 +62,10 @@ import java.net.UnknownHostException; * another buffer allocation and copy, and even more pressure on the gc. * That means that if your log message is filtered out, you might be doing * significant work and incurring significant overhead. + * + * <p>When calling the log methods that take a Throwable parameter, + * if any of the throwables in the cause chain is an <code>UnknownHostException</code>, + * then the stack trace is not logged. */ public final class Log { /** @hide */ @@ -341,6 +345,9 @@ public final class Log { /** * Handy function to get a loggable stack trace from a Throwable + + * <p>If any of the throwables in the cause chain is an <code>UnknownHostException</code>, + * this returns an empty string. * @param tr An exception to log */ @NonNull diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 55beae0f7b3d..00f4eb83bdaa 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -127,17 +127,15 @@ import java.util.function.Consumer; /** * The interface that apps use to talk to the window manager. - * </p><p> - * Each window manager instance is bound to a particular {@link Display}. - * To obtain a {@link WindowManager} for a different display, use - * {@link Context#createDisplayContext} to obtain a {@link Context} for that - * display, then use <code>Context.getSystemService(Context.WINDOW_SERVICE)</code> - * to get the WindowManager. - * </p><p> - * The simplest way to show a window on another display is to create a - * {@link Presentation}. The presentation will automatically obtain a - * {@link WindowManager} and {@link Context} for that display. - * </p> + * <p> + * Each window manager instance is bound to a {@link Display}. To obtain the + * <code>WindowManager</code> associated with a display, + * call {@link Context#createWindowContext(Display, int, Bundle)} to get the display's UI context, + * then call {@link Context#getSystemService(String)} or {@link Context#getSystemService(Class)} on + * the UI context. + * <p> + * The simplest way to show a window on a particular display is to create a {@link Presentation}, + * which automatically obtains a <code>WindowManager</code> and context for the display. */ @SystemService(Context.WINDOW_SERVICE) public interface WindowManager extends ViewManager { diff --git a/core/java/com/android/internal/net/OWNERS b/core/java/com/android/internal/net/OWNERS index 050cb5c2b44e..71f997bb57c5 100644 --- a/core/java/com/android/internal/net/OWNERS +++ b/core/java/com/android/internal/net/OWNERS @@ -1,9 +1,4 @@ set noparent +file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking -codewiz@google.com -ek@google.com -jchalard@google.com jsharkey@android.com -lorenzo@google.com -reminv@google.com -satk@google.com diff --git a/core/java/com/android/internal/os/ClassLoaderFactory.java b/core/java/com/android/internal/os/ClassLoaderFactory.java index 8b0411de5477..8f5e97d40530 100644 --- a/core/java/com/android/internal/os/ClassLoaderFactory.java +++ b/core/java/com/android/internal/os/ClassLoaderFactory.java @@ -24,6 +24,7 @@ import dalvik.system.DelegateLastClassLoader; import dalvik.system.DexClassLoader; import dalvik.system.PathClassLoader; +import java.util.ArrayList; import java.util.List; /** @@ -100,14 +101,25 @@ public class ClassLoaderFactory { } /** - * Same as {@code createClassLoader} below, but passes a null list of shared - * libraries. + * Same as {@code createClassLoader} below, but passes a null list of shared libraries. This + * method is used only to load platform classes (i.e. those in framework.jar or services.jar), + * and MUST NOT be used for loading untrusted classes, especially the app classes. For the + * latter case, use the below method which accepts list of shared libraries so that the classes + * don't have unlimited access to all shared libraries. */ public static ClassLoader createClassLoader(String dexPath, String librarySearchPath, String libraryPermittedPath, ClassLoader parent, int targetSdkVersion, boolean isNamespaceShared, String classLoaderName) { + // b/205164833: allow framework classes to have access to all public vendor libraries. + // This is because those classes are part of the platform and don't have an app manifest + // where required libraries can be specified using the <uses-native-library> tag. + // Note that this still does not give access to "private" vendor libraries. + List<String> nativeSharedLibraries = new ArrayList<>(); + nativeSharedLibraries.add("ALL"); + return createClassLoader(dexPath, librarySearchPath, libraryPermittedPath, - parent, targetSdkVersion, isNamespaceShared, classLoaderName, null, null, null); + parent, targetSdkVersion, isNamespaceShared, classLoaderName, null, + nativeSharedLibraries, null); } /** diff --git a/core/java/com/android/internal/view/OWNERS b/core/java/com/android/internal/view/OWNERS index eb2478f6550a..7a590d0ed079 100644 --- a/core/java/com/android/internal/view/OWNERS +++ b/core/java/com/android/internal/view/OWNERS @@ -15,7 +15,7 @@ per-file *Surface* = file:/services/core/java/com/android/server/wm/OWNERS # WindowManager per-file AppearanceRegion = file:/services/core/java/com/android/server/wm/OWNERS -per-file BaseIWIndow.java = file:/services/core/java/com/android/server/wm/OWNERS +per-file BaseIWindow.java = file:/services/core/java/com/android/server/wm/OWNERS per-file RotationPolicy.java = file:/services/core/java/com/android/server/wm/OWNERS per-file WindowManagerPolicyThread.java = file:/services/core/java/com/android/server/wm/OWNERS diff --git a/core/java/com/android/server/NetworkManagementSocketTagger.java b/core/java/com/android/server/NetworkManagementSocketTagger.java index 2959667e046f..26ff192521da 100644 --- a/core/java/com/android/server/NetworkManagementSocketTagger.java +++ b/core/java/com/android/server/NetworkManagementSocketTagger.java @@ -17,7 +17,6 @@ package com.android.server; import android.os.StrictMode; -import android.os.SystemProperties; import android.util.Log; import android.util.Slog; @@ -33,13 +32,6 @@ public final class NetworkManagementSocketTagger extends SocketTagger { private static final String TAG = "NetworkManagementSocketTagger"; private static final boolean LOGD = false; - /** - * {@link SystemProperties} key that indicates if {@code qtaguid} bandwidth - * controls have been enabled. - */ - // TODO: remove when always enabled, or once socket tagging silently fails. - public static final String PROP_QTAGUID_ENABLED = "net.qtaguid_enabled"; - private static ThreadLocal<SocketTags> threadSocketTags = new ThreadLocal<SocketTags>() { @Override protected SocketTags initialValue() { @@ -88,13 +80,11 @@ public final class NetworkManagementSocketTagger extends SocketTagger { private void tagSocketFd(FileDescriptor fd, int tag, int uid) { if (tag == -1 && uid == -1) return; - if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { - final int errno = native_tagSocketFd(fd, tag, uid); - if (errno < 0) { - Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", " - + tag + ", " + - + uid + ") failed with errno" + errno); - } + final int errno = native_tagSocketFd(fd, tag, uid); + if (errno < 0) { + Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", " + + tag + ", " + + uid + ") failed with errno" + errno); } } @@ -110,11 +100,9 @@ public final class NetworkManagementSocketTagger extends SocketTagger { final SocketTags options = threadSocketTags.get(); if (options.statsTag == -1 && options.statsUid == -1) return; - if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { - final int errno = native_untagSocketFd(fd); - if (errno < 0) { - Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno); - } + final int errno = native_untagSocketFd(fd); + if (errno < 0) { + Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno); } } @@ -124,21 +112,17 @@ public final class NetworkManagementSocketTagger extends SocketTagger { } public static void setKernelCounterSet(int uid, int counterSet) { - if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { - final int errno = native_setCounterSet(counterSet, uid); - if (errno < 0) { - Log.w(TAG, "setKernelCountSet(" + uid + ", " + counterSet + ") failed with errno " - + errno); - } + final int errno = native_setCounterSet(counterSet, uid); + if (errno < 0) { + Log.w(TAG, "setKernelCountSet(" + uid + ", " + counterSet + ") failed with errno " + + errno); } } public static void resetKernelUidStats(int uid) { - if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { - int errno = native_deleteTagData(0, uid); - if (errno < 0) { - Slog.w(TAG, "problem clearing counters for uid " + uid + " : errno " + errno); - } + int errno = native_deleteTagData(0, uid); + if (errno < 0) { + Slog.w(TAG, "problem clearing counters for uid " + uid + " : errno " + errno); } } diff --git a/core/java/com/android/server/net/OWNERS b/core/java/com/android/server/net/OWNERS index d3836d4c6c57..62c5737a2e8e 100644 --- a/core/java/com/android/server/net/OWNERS +++ b/core/java/com/android/server/net/OWNERS @@ -1,8 +1,2 @@ set noparent - -codewiz@google.com -jchalard@google.com -junyulai@google.com -lorenzo@google.com -reminv@google.com -satk@google.com +file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 97cac291630c..a13111143b0e 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -746,8 +746,9 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p } const bool checkJni = GetBoolProperty("dalvik.vm.checkjni", false); - ALOGV("CheckJNI is %s\n", checkJni ? "ON" : "OFF"); if (checkJni) { + ALOGD("CheckJNI is ON"); + /* extended JNI checking */ addOption("-Xcheck:jni"); diff --git a/core/jni/OWNERS b/core/jni/OWNERS index 78787e52dbaa..65d073fc1467 100644 --- a/core/jni/OWNERS +++ b/core/jni/OWNERS @@ -69,6 +69,9 @@ per-file AndroidRuntime.cpp = calin@google.com, ngeoffray@google.com, oth@google # Although marked "view" this is mostly graphics stuff per-file android_view_* = file:/graphics/java/android/graphics/OWNERS +# Verity +per-file com_android_internal_security_Verity* = ebiggers@google.com, victorhsieh@google.com + # VINTF per-file android_os_VintfObject* = file:platform/system/libvintf:/OWNERS per-file android_os_VintfRuntimeInfo* = file:platform/system/libvintf:/OWNERS diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 4c2b114c724a..5e0d9b32380c 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -34,6 +34,7 @@ #include <vector> #include <android-base/logging.h> +#include <android-base/properties.h> #include <bionic/malloc.h> #include <debuggerd/client.h> #include <log/log.h> @@ -859,7 +860,22 @@ static jlong android_os_Debug_getDmabufHeapPoolsSizeKb(JNIEnv* env, jobject claz return poolsSizeKb; } +static bool halSupportsGpuPrivateMemory() { + int productApiLevel = + android::base::GetIntProperty("ro.product.first_api_level", + android::base::GetIntProperty("ro.build.version.sdk", + __ANDROID_API_FUTURE__)); + int boardApiLevel = + android::base::GetIntProperty("ro.board.api_level", + android::base::GetIntProperty("ro.board.first_api_level", + __ANDROID_API_FUTURE__)); + + return std::min(productApiLevel, boardApiLevel) >= __ANDROID_API_S__; +} + static jlong android_os_Debug_getGpuPrivateMemoryKb(JNIEnv* env, jobject clazz) { + static bool gpuPrivateMemorySupported = halSupportsGpuPrivateMemory(); + struct memtrack_proc* p = memtrack_proc_new(); if (p == nullptr) { LOG(ERROR) << "getGpuPrivateMemoryKb: Failed to create memtrack_proc"; @@ -876,6 +892,12 @@ static jlong android_os_Debug_getGpuPrivateMemoryKb(JNIEnv* env, jobject clazz) ssize_t gpuPrivateMem = memtrack_proc_gl_pss(p); memtrack_proc_destroy(p); + + // Old HAL implementations may return 0 for GPU private memory if not supported + if (gpuPrivateMem == 0 && !gpuPrivateMemorySupported) { + return -1; + } + return gpuPrivateMem / 1024; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index d727a55615f2..261334b56215 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -6252,6 +6252,10 @@ android:permission="android.permission.BIND_JOB_SERVICE"> </service> + <service android:name="com.android.server.compos.IsolatedCompilationJobService" + android:permission="android.permission.BIND_JOB_SERVICE"> + </service> + <service android:name="com.android.server.PruneInstantAppsJobService" android:permission="android.permission.BIND_JOB_SERVICE" > </service> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index a350d14d92ab..be5063f3609a 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -64,16 +64,74 @@ <!-- Displayed when a carrier does not support call forwarding queries when roaming. --> <string name="mmiErrorWhileRoaming">Can not change call forwarding settings from your phone while you are roaming.</string> - <!-- Displayed when a phone feature such as call barring was activated. --> + <!-- Displayed when a phone feature such as call forwarding, call waiting, or call barring was + activated. + Used to build messages of the form: + <X> + <Y> + + Where <X> is the name of the service which was enabled. Can be one of: + {@link #BaMmi Call barring}, {@link #CfMmi Call forwarding}, + {@link #PwdMmi Password change}, {@link #CwMmi Call waiting}, + {@link #ClipMmi Incoming Caller ID}, {@link #ClirMmi Hide Outgoing Caller ID}, + {@link #ColpMmi Connected Line ID}, {@link #ColrMmi Connected Line ID Restriction}. + And <Y> is {@link #serviceEnabled} (this string). + --> <string name="serviceEnabled">Service was enabled.</string> <!-- Displayed in front of the list of a set of service classes - (voice, data, fax, etc.) that were enabled. --> + (voice, data, fax, etc.) that call waiting were enabled for. + Will be used with messages of the form: + <X> + <Y1> + ... + <Yn> + Where <X> is {@link #serviceEnabledFor} (this string) and <Y>..<Yn> can be: + {@link #serviceClassData}, {@link #serviceClassVoice}, {@link #serviceClassFAX}, + {@link #serviceClassSMS}, {@link #serviceClassDataAsync}, {@link #serviceClassDataSync}, + {@link #serviceClassPacket}, {@link #serviceClassPAD}. + --> <string name="serviceEnabledFor">Service was enabled for:</string> - <!-- Displayed when a phone feature such as call forwarding was deactivated. --> + <!-- Displayed when a phone feature such as call forwarding was deactivated. + Used to build messages of the form: + <X> + <Y> + + Where <X> is the name of the service which was disabled. Can be one of: + {@link #BaMmi Call barring}, {@link #CfMmi Call forwarding}, + {@link #PwdMmi Password change}, {@link #CwMmi Call waiting}, + {@link #ClipMmi Incoming Caller ID}, {@link #ClirMmi Hide Outgoing Caller ID}, + {@link #ColpMmi Connected Line ID}, {@link #ColrMmi Connected Line ID Restriction}. + And <Y> is {@link #serviceDisabled} (this string). + --> <string name="serviceDisabled">Service has been disabled.</string> - <!-- Displayed when a phone property such as a SIM password was registered. --> + <!-- Displayed when a phone property such as a SIM password was registered. Registration + entails setting up a service for use, where {@link #serviceEnabled} entails enabling a + previously registered service. + Used to build messages of the form: + <X> + <Y> + + Where <X> is the name of the service which was registered. Can be one of: + {@link #BaMmi Call barring}, {@link #CfMmi Call forwarding}, + {@link #PwdMmi Password change}, {@link #CwMmi Call waiting}, + {@link #ClipMmi Incoming Caller ID}, {@link #ClirMmi Hide Outgoing Caller ID}, + {@link #ColpMmi Connected Line ID}, {@link #ColrMmi Connected Line ID Restriction}. + And <Y> is {@link #serviceRegistered} (this string). --> <string name="serviceRegistered">Registration was successful.</string> - <!-- Displayed when a phone property such as a SIM password was erased. --> + <!-- Displayed when a phone property such as a SIM password was erased. + Erasure is the opposite of {@link #serviceRegistered} and entails removal of a service. + + Used to build messages of the form: + <X> + <Y> + + Where <X> is the name of the service which was registered. Can be one of: + {@link #BaMmi Call barring}, {@link #CfMmi Call forwarding}, + {@link #PwdMmi Password change}, {@link #CwMmi Call waiting}, + {@link #ClipMmi Incoming Caller ID}, {@link #ClirMmi Hide Outgoing Caller ID}, + {@link #ColpMmi Connected Line ID}, {@link #ColrMmi Connected Line ID Restriction}. + And <Y> is {@link #serviceErased} (this string). + --> <string name="serviceErased">Erasure was successful.</string> <!-- Displayed when a SIM password was entered incorrectly. --> <string name="passwordIncorrect">Incorrect password.</string> @@ -107,23 +165,32 @@ [CHAR LIMIT=10] --> <string name="meid">MEID</string> - <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. --> + <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="ClipMmi">Incoming Caller ID</string> - <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. --> + <!-- Displayed as the title for a success/failure report enabling/disabling caller ID. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="ClirMmi">Hide Outgoing Caller ID</string> - <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID. --> + <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="ColpMmi">Connected Line ID</string> - <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID restriction. --> + <!-- Displayed as the title for a success/failure report enabling/disabling connected line ID restriction. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="ColrMmi">Connected Line ID Restriction</string> - <!-- Displayed as the title for a success/failure report enabling/disabling call forwarding. --> + <!-- Displayed as the title for a success/failure report enabling/disabling call forwarding. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="CfMmi">Call forwarding</string> - <!-- Displayed as the title for a success/failure report enabling/disabling call waiting. --> + <!-- Displayed as the title for a success/failure report enabling/disabling call waiting. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="CwMmi">Call waiting</string> - <!-- Displayed as the title for a success/failure report enabling/disabling call barring. --> + <!-- Displayed as the title for a success/failure report enabling/disabling call barring. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="BaMmi">Call barring</string> - <!-- Displayed as the title for a success/failure report changing the SIM password. --> + <!-- Displayed as the title for a success/failure report changing the SIM password. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="PwdMmi">Password change</string> - <!-- Displayed as the title for a success/failure report changing the SIM PIN. --> + <!-- Displayed as the title for a success/failure report changing the SIM PIN. + See {@link #serviceEnabled}, {@link #serviceDisabled}. --> <string name="PinMmi">PIN change</string> <string name="CnipMmi">Calling number present</string> <string name="CnirMmi">Calling number restricted</string> @@ -198,21 +265,29 @@ <string name="peerTtyModeOff">Peer requested TTY Mode OFF</string> <!-- Mappings between TS 27.007 +CFCC/+CLCK "service classes" and human-readable strings--> <skip /> - <!-- Example: Service was enabled for: Voice, Data --> + <!-- Example: Service was enabled for: Voice, Data + See {@link #serviceEnabledFor}.--> <string name="serviceClassVoice">Voice</string> - <!-- Example: Service was enabled for: Voice, Data --> + <!-- Example: Service was enabled for: Voice, Data. + See {@link #serviceEnabledFor}. --> <string name="serviceClassData">Data</string> - <!-- Example: Service was enabled for: Voice, FAX --> + <!-- Example: Service was enabled for: Voice, FAX + See {@link #serviceEnabledFor}. --> <string name="serviceClassFAX">FAX</string> - <!-- Example: Service was enabled for: Voice, SMS --> + <!-- Example: Service was enabled for: Voice, SMS + See {@link #serviceEnabledFor}. --> <string name="serviceClassSMS">SMS</string> - <!-- Meaning: asynchronous data. Example: Service was enabled for: Voice, Async --> + <!-- Meaning: asynchronous data. Example: Service was enabled for: Voice, Async + See {@link #serviceEnabledFor}. --> <string name="serviceClassDataAsync">Async</string> - <!-- Meaning: synchronous data. Example: Service was enabled for: Voice, Async --> + <!-- Meaning: synchronous data. Example: Service was enabled for: Voice, Async + See {@link #serviceEnabledFor}. --> <string name="serviceClassDataSync">Sync</string> - <!-- Meaning: packet data. Example: Service was enabled for: Voice, Packet --> + <!-- Meaning: packet data. Example: Service was enabled for: Voice, Packet + See {@link #serviceEnabledFor}. --> <string name="serviceClassPacket">Packet</string> - <!-- Meaning: unknown. Example: Service was enabled for: Voice, PAD --> + <!-- Meaning: unknown. Example: Service was enabled for: Voice, PAD + See {@link #serviceEnabledFor}. --> <string name="serviceClassPAD">PAD</string> <!-- CDMA Roaming Indicator Strings (non ERI)--> <skip /> diff --git a/core/tests/coretests/OWNERS b/core/tests/coretests/OWNERS new file mode 100644 index 000000000000..0fb0c3021486 --- /dev/null +++ b/core/tests/coretests/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/services/core/java/com/android/server/am/OWNERS diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index cf072b488efc..e81adecc3bad 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -225,6 +225,8 @@ applications that come with the platform <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> <permission name="android.permission.UPDATE_APP_OPS_STATS"/> <permission name="android.permission.USE_RESERVED_DISK"/> + <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> + <permission name="android.permission.LOG_COMPAT_CHANGE" /> </privapp-permissions> <privapp-permissions package="com.android.providers.downloads"> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java index e6d088e6537d..2adf8ce8525f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java @@ -272,8 +272,10 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou final String innerPrefix = prefix + " "; final String childPrefix = innerPrefix + " "; pw.println(prefix + this); - pw.println(innerPrefix + "Root taskId=" + getRootTaskId() - + " winMode=" + mRootTaskInfo.getWindowingMode()); + if (mRootTaskInfo != null) { + pw.println(innerPrefix + "Root taskId=" + mRootTaskInfo.taskId + + " winMode=" + mRootTaskInfo.getWindowingMode()); + } if (mTaskInfo1 != null) { pw.println(innerPrefix + "1 taskId=" + mTaskInfo1.taskId + " winMode=" + mTaskInfo1.getWindowingMode()); diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index cf7039b9ed5c..c7f56961e498 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -32,7 +32,6 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.bluetooth.BluetoothCodecConfig; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothProfile; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -5796,112 +5795,25 @@ public class AudioManager { } } - /** - * Indicate Hearing Aid connection state change and eventually suppress - * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent. - * This operation is asynchronous but its execution will still be sequentially scheduled - * relative to calls to {@link #setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - * * BluetoothDevice, int, int, boolean, int)} and - * and {@link #handleBluetoothA2dpDeviceConfigChange(BluetoothDevice)}. - * @param device Bluetooth device connected/disconnected - * @param state new connection state (BluetoothProfile.STATE_xxx) - * @param musicDevice Default get system volume for the connecting device. - * (either {@link android.bluetooth.BluetoothProfile.hearingaid} or - * {@link android.bluetooth.BluetoothProfile.HEARING_AID}) - * @param suppressNoisyIntent if true the - * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent. - * {@hide} - */ - public void setBluetoothHearingAidDeviceConnectionState( - BluetoothDevice device, int state, boolean suppressNoisyIntent, - int musicDevice) { - final IAudioService service = getService(); - try { - service.setBluetoothHearingAidDeviceConnectionState(device, - state, suppressNoisyIntent, musicDevice); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - /** - * Indicate Le Audio output device connection state change and eventually suppress - * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent. - * @param device Bluetooth device connected/disconnected - * @param state new connection state (BluetoothProfile.STATE_xxx) - * @param suppressNoisyIntent if true the - * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent. - * {@hide} - */ - public void setBluetoothLeAudioOutDeviceConnectionState(BluetoothDevice device, int state, - boolean suppressNoisyIntent) { - final IAudioService service = getService(); - try { - service.setBluetoothLeAudioOutDeviceConnectionState(device, state, suppressNoisyIntent); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Indicate Le Audio input connection state change. - * @param device Bluetooth device connected/disconnected - * @param state new connection state (BluetoothProfile.STATE_xxx) - * {@hide} - */ - public void setBluetoothLeAudioInDeviceConnectionState(BluetoothDevice device, int state) { - final IAudioService service = getService(); - try { - service.setBluetoothLeAudioInDeviceConnectionState(device, state); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Indicate A2DP source or sink connection state change and eventually suppress - * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent. - * This operation is asynchronous but its execution will still be sequentially scheduled - * relative to calls to {@link #setBluetoothHearingAidDeviceConnectionState(BluetoothDevice, - * int, boolean, int)} and - * {@link #handleBluetoothA2dpDeviceConfigChange(BluetoothDevice)}. - * @param device Bluetooth device connected/disconnected - * @param state new connection state, {@link BluetoothProfile#STATE_CONNECTED} - * or {@link BluetoothProfile#STATE_DISCONNECTED} - * @param profile profile for the A2DP device - * @param a2dpVolume New volume for the connecting device. Does nothing if disconnecting. - * (either {@link android.bluetooth.BluetoothProfile.A2DP} or - * {@link android.bluetooth.BluetoothProfile.A2DP_SINK}) - * @param suppressNoisyIntent if true the - * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent. - * {@hide} - */ - public void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - BluetoothDevice device, int state, - int profile, boolean suppressNoisyIntent, int a2dpVolume) { - final IAudioService service = getService(); - try { - service.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device, - state, profile, suppressNoisyIntent, a2dpVolume); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Indicate A2DP device configuration has changed. - * This operation is asynchronous but its execution will still be sequentially scheduled - * relative to calls to - * {@link #setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice, int, int, - * boolean, int)} and - * {@link #setBluetoothHearingAidDeviceConnectionState(BluetoothDevice, int, boolean, int)} - * @param device Bluetooth device whose configuration has changed. + * Indicate Bluetooth profile connection state change. + * Configuration changes for A2DP are indicated by having the same <code>newDevice</code> and + * <code>previousDevice</code> + * This operation is asynchronous. + * + * @param newDevice Bluetooth device connected or null if there is no new devices + * @param previousDevice Bluetooth device disconnected or null if there is no disconnected + * devices + * @param info contain all info related to the device. {@link BtProfileConnectionInfo} * {@hide} */ - public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device) { + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK) + public void handleBluetoothActiveDeviceChanged(@Nullable BluetoothDevice newDevice, + @Nullable BluetoothDevice previousDevice, @NonNull BtProfileConnectionInfo info) { final IAudioService service = getService(); try { - service.handleBluetoothA2dpDeviceConfigChange(device); + service.handleBluetoothActiveDeviceChanged(newDevice, previousDevice, info); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/media/java/android/media/BtProfileConnectionInfo.aidl b/media/java/android/media/BtProfileConnectionInfo.aidl new file mode 100644 index 000000000000..047f06be0964 --- /dev/null +++ b/media/java/android/media/BtProfileConnectionInfo.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2021 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.media; + +parcelable BtProfileConnectionInfo; + diff --git a/media/java/android/media/BtProfileConnectionInfo.java b/media/java/android/media/BtProfileConnectionInfo.java new file mode 100644 index 000000000000..19ea2de6a434 --- /dev/null +++ b/media/java/android/media/BtProfileConnectionInfo.java @@ -0,0 +1,163 @@ +/* + * Copyright 2021 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.media; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.bluetooth.BluetoothProfile; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Contains information about Bluetooth profile connection state changed + * {@hide} + */ +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public final class BtProfileConnectionInfo implements Parcelable { + /** @hide */ + @IntDef({ + BluetoothProfile.A2DP, + BluetoothProfile.A2DP_SINK, // Can only be set by BtHelper + BluetoothProfile.HEADSET, // Can only be set by BtHelper + BluetoothProfile.HEARING_AID, + BluetoothProfile.LE_AUDIO, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BtProfile {} + + private final @BtProfile int mProfile; + private final boolean mSupprNoisy; + private final int mVolume; + private final boolean mIsLeOutput; + + private BtProfileConnectionInfo(@BtProfile int profile, boolean suppressNoisyIntent, int volume, + boolean isLeOutput) { + mProfile = profile; + mSupprNoisy = suppressNoisyIntent; + mVolume = volume; + mIsLeOutput = isLeOutput; + } + + /** + * Constructor used by BtHelper when a profile is connected + * {@hide} + */ + public BtProfileConnectionInfo(@BtProfile int profile) { + this(profile, false, -1, false); + } + + public static final @NonNull Parcelable.Creator<BtProfileConnectionInfo> CREATOR = + new Parcelable.Creator<BtProfileConnectionInfo>() { + @Override + public BtProfileConnectionInfo createFromParcel(Parcel source) { + return new BtProfileConnectionInfo(source.readInt(), source.readBoolean(), + source.readInt(), source.readBoolean()); + } + + @Override + public BtProfileConnectionInfo[] newArray(int size) { + return new BtProfileConnectionInfo[size]; + } + }; + + @Override + public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags) { + dest.writeInt(mProfile); + dest.writeBoolean(mSupprNoisy); + dest.writeInt(mVolume); + dest.writeBoolean(mIsLeOutput); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Constructor for A2dp info + * + * @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} + * intent will not be sent. + * + * @param volume of device -1 to ignore value + */ + public static @NonNull BtProfileConnectionInfo a2dpInfo(boolean suppressNoisyIntent, + int volume) { + return new BtProfileConnectionInfo(BluetoothProfile.A2DP, suppressNoisyIntent, volume, + false); + } + + /** + * Constructor for hearing aid info + * + * @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} + * intent will not be sent. + */ + public static @NonNull BtProfileConnectionInfo hearingAidInfo(boolean suppressNoisyIntent) { + return new BtProfileConnectionInfo(BluetoothProfile.HEARING_AID, suppressNoisyIntent, -1, + false); + } + + /** + * constructor for le audio info + * + * @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} + * intent will not be sent. + * + * @param isLeOutput if true mean the device is an output device, if false it's an input device + */ + public static @NonNull BtProfileConnectionInfo leAudio(boolean suppressNoisyIntent, + boolean isLeOutput) { + return new BtProfileConnectionInfo(BluetoothProfile.LE_AUDIO, suppressNoisyIntent, -1, + isLeOutput); + } + + /** + * @return The profile connection + */ + public @BtProfile int getProfile() { + return mProfile; + } + + /** + * @return {@code true} if {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be + * sent + */ + public boolean getSuppressNoisyIntent() { + return mSupprNoisy; + } + + /** + * Only for {@link BluetoothProfile.A2DP} profile + * @return the volume of the connection or -1 if the value is ignored + */ + public int getVolume() { + return mVolume; + } + + /** + * Only for {@link BluetoothProfile.LE_AUDIO} profile + * @return {@code true} is the LE device is an output device, {@code false} if it's an input + * device + */ + public boolean getIsLeOutput() { + return mIsLeOutput; + } +} diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index dd44fdf1e8c4..5ff56f9c680d 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -24,6 +24,7 @@ import android.media.AudioFocusInfo; import android.media.AudioPlaybackConfiguration; import android.media.AudioRecordingConfiguration; import android.media.AudioRoutesInfo; +import android.media.BtProfileConnectionInfo; import android.media.IAudioFocusDispatcher; import android.media.IAudioModeDispatcher; import android.media.IAudioRoutesObserver; @@ -207,8 +208,6 @@ interface IAudioService { void setWiredDeviceConnectionState(int type, int state, String address, String name, String caller); - void handleBluetoothA2dpDeviceConfigChange(in BluetoothDevice device); - @UnsupportedAppUsage AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer); @@ -268,16 +267,8 @@ interface IAudioService { oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio); - void setBluetoothHearingAidDeviceConnectionState(in BluetoothDevice device, - int state, boolean suppressNoisyIntent, int musicDevice); - - void setBluetoothLeAudioOutDeviceConnectionState(in BluetoothDevice device, int state, - boolean suppressNoisyIntent); - - void setBluetoothLeAudioInDeviceConnectionState(in BluetoothDevice device, int state); - - void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device, - int state, int profile, boolean suppressNoisyIntent, int a2dpVolume); + void handleBluetoothActiveDeviceChanged(in BluetoothDevice newDevice, + in BluetoothDevice previousDevice, in BtProfileConnectionInfo info); oneway void setFocusRequestResultFromExtPolicy(in AudioFocusInfo afi, int requestResult, in IAudioPolicyCallback pcb); diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 374cc7592faf..3c152fb68c0a 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -426,6 +426,12 @@ public final class MediaCodecInfo { /** @deprecated Use {@link #COLOR_Format32bitABGR8888}. */ public static final int COLOR_Format24BitABGR6666 = 43; + /** @hide + * P010 is a 4:2:0 YCbCr semiplanar format comprised of a WxH Y plane + * followed by a Wx(H/2) CbCr plane. Each sample is represented by a 16-bit + * little-endian value, with the lower 6 bits set to zero. */ + public static final int COLOR_FormatYUVP010 = 54; + /** @deprecated Use {@link #COLOR_FormatYUV420Flexible}. */ public static final int COLOR_TI_FormatYUV420PackedSemiPlanar = 0x7f000100; // COLOR_FormatSurface indicates that the data will be a GraphicBuffer metadata reference. diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp index ecd9cc1cd098..39b560b393a9 100644 --- a/media/jni/android_media_Utils.cpp +++ b/media/jni/android_media_Utils.cpp @@ -65,6 +65,7 @@ bool isPossiblyYUV(PixelFormat format) { case HAL_PIXEL_FORMAT_Y8: case HAL_PIXEL_FORMAT_Y16: case HAL_PIXEL_FORMAT_RAW16: + case HAL_PIXEL_FORMAT_RAW12: case HAL_PIXEL_FORMAT_RAW10: case HAL_PIXEL_FORMAT_RAW_OPAQUE: case HAL_PIXEL_FORMAT_BLOB: diff --git a/media/native/midi/amidi.cpp b/media/native/midi/amidi.cpp index 923377c8180e..f90796e415c0 100644 --- a/media/native/midi/amidi.cpp +++ b/media/native/midi/amidi.cpp @@ -325,8 +325,8 @@ public: } uint8_t readBuffer[AMIDI_PACKET_SIZE]; - ssize_t readCount = read(mPort->ufd, readBuffer, sizeof(readBuffer)); - if (readCount == EINTR || readCount < 1) { + ssize_t readCount = TEMP_FAILURE_RETRY(read(mPort->ufd, readBuffer, sizeof(readBuffer))); + if (readCount < 1) { return AMEDIA_ERROR_UNKNOWN; } @@ -407,7 +407,8 @@ ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPo ssize_t numTransferBytes = AMIDI_makeSendBuffer(writeBuffer, data + numSent, blockSize, timestamp); - ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, writeBuffer, numTransferBytes); + ssize_t numWritten = TEMP_FAILURE_RETRY(write(((AMIDI_Port*)inputPort)->ufd, writeBuffer, + numTransferBytes)); if (numWritten < 0) { break; // error so bail out. } @@ -430,7 +431,8 @@ media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPor uint8_t opCode = AMIDI_OPCODE_FLUSH; ssize_t numTransferBytes = 1; - ssize_t numWritten = write(((AMIDI_Port*)inputPort)->ufd, &opCode, numTransferBytes); + ssize_t numWritten = TEMP_FAILURE_RETRY(write(((AMIDI_Port*)inputPort)->ufd, &opCode, + numTransferBytes)); if (numWritten < numTransferBytes) { ALOGE("AMidiInputPort_flush Couldn't write MIDI flush. requested:%zd, written:%zd", diff --git a/omapi/aidl/Android.bp b/omapi/aidl/Android.bp new file mode 100644 index 000000000000..2b81200f0079 --- /dev/null +++ b/omapi/aidl/Android.bp @@ -0,0 +1,35 @@ +// Copyright 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +aidl_interface { + name: "android.se.omapi", + vendor_available: true, + srcs: ["android/se/omapi/*.aidl"], + stability: "vintf", + backend: { + java: { + sdk_version: "module_current", + }, + rust: { + enabled: true, + }, + ndk: { + separate_platform_variant: false, + }, + }, +} diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementChannel.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementChannel.aidl new file mode 100644 index 000000000000..725013a35cde --- /dev/null +++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementChannel.aidl @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2021, 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. + *//* + * Contributed by: Giesecke & Devrient GmbH. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.se.omapi; +/* @hide */ +@VintfStability +interface ISecureElementChannel { + void close(); + boolean isClosed(); + boolean isBasicChannel(); + byte[] getSelectResponse(); + byte[] transmit(in byte[] command); + boolean selectNext(); +} diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementListener.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementListener.aidl new file mode 100644 index 000000000000..77e1c53f47ac --- /dev/null +++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementListener.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *//* + * Contributed by: Giesecke & Devrient GmbH. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.se.omapi; +/* @hide */ +@VintfStability +interface ISecureElementListener { +} diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementReader.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementReader.aidl new file mode 100644 index 000000000000..2b10c473c902 --- /dev/null +++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementReader.aidl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *//* + * Contributed by: Giesecke & Devrient GmbH. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.se.omapi; +/* @hide */ +@VintfStability +interface ISecureElementReader { + boolean isSecureElementPresent(); + android.se.omapi.ISecureElementSession openSession(); + void closeSessions(); + boolean reset(); +} diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementService.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementService.aidl new file mode 100644 index 000000000000..ae6346278691 --- /dev/null +++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementService.aidl @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *//* + * Copyright (c) 2015-2017, The Linux Foundation. + *//* + * Contributed by: Giesecke & Devrient GmbH. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.se.omapi; +/* @hide */ +@VintfStability +interface ISecureElementService { + String[] getReaders(); + android.se.omapi.ISecureElementReader getReader(in String reader); + boolean[] isNFCEventAllowed(in String reader, in byte[] aid, in String[] packageNames); +} diff --git a/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementSession.aidl b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementSession.aidl new file mode 100644 index 000000000000..06287c551f5c --- /dev/null +++ b/omapi/aidl/aidl_api/android.se.omapi/current/android/se/omapi/ISecureElementSession.aidl @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *//* + * Copyright (c) 2015-2017, The Linux Foundation. + *//* + * Contributed by: Giesecke & Devrient GmbH. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.se.omapi; +/* @hide */ +@VintfStability +interface ISecureElementSession { + byte[] getAtr(); + void close(); + void closeChannels(); + boolean isClosed(); + android.se.omapi.ISecureElementChannel openBasicChannel(in byte[] aid, in byte p2, in android.se.omapi.ISecureElementListener listener); + android.se.omapi.ISecureElementChannel openLogicalChannel(in byte[] aid, in byte p2, in android.se.omapi.ISecureElementListener listener); +} diff --git a/core/java/android/se/omapi/ISecureElementChannel.aidl b/omapi/aidl/android/se/omapi/ISecureElementChannel.aidl index 4ae57ab829cb..bbd3c148caaf 100644 --- a/core/java/android/se/omapi/ISecureElementChannel.aidl +++ b/omapi/aidl/android/se/omapi/ISecureElementChannel.aidl @@ -22,6 +22,7 @@ package android.se.omapi; import android.se.omapi.ISecureElementSession; /** @hide */ +@VintfStability interface ISecureElementChannel { /** @@ -58,6 +59,9 @@ interface ISecureElementChannel { * Transmits the specified command APDU and returns the response APDU. * MANAGE channel commands are not supported. * Selection of applets is not supported in logical channels. + * + * @param command Command APDU, its structure is defined in ISO/IEC 7816-4 + * in Standard byte format */ byte[] transmit(in byte[] command); diff --git a/core/java/android/se/omapi/ISecureElementListener.aidl b/omapi/aidl/android/se/omapi/ISecureElementListener.aidl index e9dd18181c56..479dcd7d5acf 100644 --- a/core/java/android/se/omapi/ISecureElementListener.aidl +++ b/omapi/aidl/android/se/omapi/ISecureElementListener.aidl @@ -23,5 +23,6 @@ package android.se.omapi; * Interface to receive call-backs when the service is connected. * @hide */ +@VintfStability interface ISecureElementListener { } diff --git a/core/java/android/se/omapi/ISecureElementReader.aidl b/omapi/aidl/android/se/omapi/ISecureElementReader.aidl index 41244ab058e0..a6979face61f 100644 --- a/core/java/android/se/omapi/ISecureElementReader.aidl +++ b/omapi/aidl/android/se/omapi/ISecureElementReader.aidl @@ -22,6 +22,7 @@ package android.se.omapi; import android.se.omapi.ISecureElementSession; /** @hide */ +@VintfStability interface ISecureElementReader { /** @@ -34,7 +35,7 @@ interface ISecureElementReader { * Connects to a secure element in this reader. <br> * This method prepares (initialises) the Secure Element for communication * before the Session object is returned (e.g. powers the Secure Element by - * ICC ON if its not already on). There might be multiple sessions opened at + * ICC ON if it is not already on). There might be multiple sessions opened at * the same time on the same reader. The system ensures the interleaving of * APDUs between the respective sessions. * diff --git a/core/java/android/se/omapi/ISecureElementService.aidl b/omapi/aidl/android/se/omapi/ISecureElementService.aidl index 4fa799e78757..61ae4816de82 100644 --- a/core/java/android/se/omapi/ISecureElementService.aidl +++ b/omapi/aidl/android/se/omapi/ISecureElementService.aidl @@ -28,23 +28,31 @@ import android.se.omapi.ISecureElementReader; * SecureElement service interface. * @hide */ +@VintfStability interface ISecureElementService { /** * Returns the friendly names of available Secure Element readers. + * <ul> + * <li>If the reader is a SIM reader, then its name must be "SIM[Slot]".</li> + * <li>If the reader is a SD or micro SD reader, then its name must be "SD[Slot]"</li> + * <li>If the reader is a embedded SE reader, then its name must be "eSE[Slot]"</li> + * </ul> + * Slot is a decimal number without leading zeros. The Numbering must start with 1 + * (e.g. SIM1, SIM2, ... or SD1, SD2, ... or eSE1, eSE2, ...). */ String[] getReaders(); /** * Returns SecureElement Service reader object to the given name. */ - ISecureElementReader getReader(String reader); + ISecureElementReader getReader(in String reader); /** * Checks if the application defined by the package name is allowed to * receive NFC transaction events for the defined AID. */ - boolean[] isNFCEventAllowed(String reader, in byte[] aid, + boolean[] isNFCEventAllowed(in String reader, in byte[] aid, in String[] packageNames); } diff --git a/core/java/android/se/omapi/ISecureElementSession.aidl b/omapi/aidl/android/se/omapi/ISecureElementSession.aidl index 8ea599f2e866..129ecc4ddaa3 100644 --- a/core/java/android/se/omapi/ISecureElementSession.aidl +++ b/omapi/aidl/android/se/omapi/ISecureElementSession.aidl @@ -27,6 +27,7 @@ import android.se.omapi.ISecureElementReader; import android.se.omapi.ISecureElementListener; /** @hide */ +@VintfStability interface ISecureElementSession { /** @@ -45,7 +46,6 @@ interface ISecureElementSession { */ void closeChannels(); - /** * Tells if this session is closed. * @@ -59,15 +59,19 @@ interface ISecureElementSession { * applet if aid != null. * Logical channels cannot be opened with this connection. * Use interface method openLogicalChannel() to open a logical channel. + * Listener is passed to secure element service and used to monitor whether + * the client application that uses OMAPI is still alive or not. */ ISecureElementChannel openBasicChannel(in byte[] aid, in byte p2, - ISecureElementListener listener); + in ISecureElementListener listener); /** * Opens a connection using the next free logical channel of the card in the * specified reader. Selects the specified applet. * Selection of other applets with this connection is not supported. + * Listener is passed to secure element service and used to monitor whether + * the client application that uses OMAPI is still alive or not. */ ISecureElementChannel openLogicalChannel(in byte[] aid, in byte p2, - ISecureElementListener listener); + in ISecureElementListener listener); } diff --git a/omapi/aidl/vts/functional/AccessControlApp/Android.bp b/omapi/aidl/vts/functional/AccessControlApp/Android.bp new file mode 100644 index 000000000000..f03c3f6eb647 --- /dev/null +++ b/omapi/aidl/vts/functional/AccessControlApp/Android.bp @@ -0,0 +1,54 @@ +// +// Copyright (C) 2021 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_test { + name: "VtsHalOmapiSeAccessControlTestCases", + defaults: [ + "VtsHalTargetTestDefaults", + "use_libaidlvintf_gtest_helper_static", + ], + srcs: [ + "VtsHalOmapiSeAccessControlTestCases.cpp", + ], + shared_libs: [ + "libbase", + "liblog", + "libcutils", + "libhidlbase", + "libnativehelper", + "libutils", + "libbinder_ndk", + ], + static_libs: [ + "VtsHalHidlTargetTestBase", + "android.se.omapi-V1-ndk", + ], + cflags: [ + "-O0", + "-g", + "-Wall", + "-Werror", + ], + require_root: true, + test_suites: [ + "general-tests", + "vts", + ], +} diff --git a/omapi/aidl/vts/functional/AccessControlApp/VtsHalOmapiSeAccessControlTestCases.cpp b/omapi/aidl/vts/functional/AccessControlApp/VtsHalOmapiSeAccessControlTestCases.cpp new file mode 100644 index 000000000000..9ea65431417a --- /dev/null +++ b/omapi/aidl/vts/functional/AccessControlApp/VtsHalOmapiSeAccessControlTestCases.cpp @@ -0,0 +1,428 @@ +/* + * Copyright (C) 2021 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. + */ + +#include <aidl/android/se/omapi/BnSecureElementListener.h> +#include <aidl/android/se/omapi/ISecureElementChannel.h> +#include <aidl/android/se/omapi/ISecureElementListener.h> +#include <aidl/android/se/omapi/ISecureElementReader.h> +#include <aidl/android/se/omapi/ISecureElementService.h> +#include <aidl/android/se/omapi/ISecureElementSession.h> + +#include <VtsCoreUtil.h> +#include <aidl/Gtest.h> +#include <aidl/Vintf.h> +#include <android-base/logging.h> +#include <android/binder_manager.h> +#include <binder/IServiceManager.h> +#include <cutils/properties.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> +#include <utils/String16.h> + +using namespace std; +using namespace ::testing; +using namespace android; + +int main(int argc, char** argv) { + InitGoogleTest(&argc, argv); + int status = RUN_ALL_TESTS(); + return status; +} + +namespace { + +class OMAPISEAccessControlTest : public TestWithParam<std::string> { + protected: + + class SEListener : public ::aidl::android::se::omapi::BnSecureElementListener {}; + + /** + * Verifies TLV data + * + * @return true if the data is tlv formatted, false otherwise + */ + bool verifyBerTlvData(std::vector<uint8_t> tlv) { + if (tlv.size() == 0) { + LOG(ERROR) << "Invalid tlv, null"; + return false; + } + int i = 0; + if ((tlv[i++] & 0x1F) == 0x1F) { + // extra byte for TAG field + i++; + } + + int len = tlv[i++] & 0xFF; + if (len > 127) { + // more than 1 byte for length + int bytesLength = len - 128; + len = 0; + for (int j = bytesLength; j > 0; j--) { + len += (len << 8) + (tlv[i++] & 0xFF); + } + } + // Additional 2 bytes for the SW + return (tlv.size() == (i + len + 2)); + } + + void testSelectableAid( + std::vector<std::vector<uint8_t>> authorizedAids) { + for (auto aid : authorizedAids) { + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + auto seListener = ndk::SharedRefBase::make<SEListener>(); + + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + std::vector<uint8_t> selectResponse = {}; + ASSERT_NE(reader, nullptr) << "reader is null"; + + bool status = false; + auto res = reader->isSecureElementPresent(&status); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_TRUE(status); + + res = reader->openSession(&session); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(session, nullptr) << "Could not open session"; + + res = session->openLogicalChannel(aid, 0x00, seListener, &channel); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(channel, nullptr) << "Could not open channel"; + + res = channel->getSelectResponse(&selectResponse); + ASSERT_TRUE(res.isOk()) << "failed to get Select Response"; + ASSERT_GE(selectResponse.size(), 2); + + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + + ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00)); + ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90)); + ASSERT_TRUE( + verifyBerTlvData(selectResponse)) << "Select Response is not complete"; + } + } + } + } + + void testUnauthorisedAid( + std::vector<std::vector<uint8_t>> unAuthorizedAids) { + for (auto aid : unAuthorizedAids) { + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + auto seListener = ndk::SharedRefBase::make<SEListener>(); + + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + ASSERT_NE(reader, nullptr) << "reader is null"; + + bool status = false; + auto res = reader->isSecureElementPresent(&status); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_TRUE(status); + + res = reader->openSession(&session); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(session, nullptr) << "Could not open session"; + + res = session->openLogicalChannel(aid, 0x00, seListener, &channel); + + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + + if (!res.isOk()) { + ASSERT_EQ(res.getExceptionCode(), EX_SECURITY); + ASSERT_FALSE(res.isOk()) << "expected failed status for this test"; + } + } + } + } + } + + void testTransmitAPDU( + std::vector<uint8_t> aid, + std::vector<std::vector<uint8_t>> apdus) { + for (auto apdu : apdus) { + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + auto seListener = ndk::SharedRefBase::make<SEListener>(); + + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + ASSERT_NE(reader, nullptr) << "reader is null"; + bool status = false; + std::vector<uint8_t> selectResponse = {}; + std::vector<uint8_t> transmitResponse = {}; + auto res = reader->isSecureElementPresent(&status); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_TRUE(status); + + res = reader->openSession(&session); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(session, nullptr) << "Could not open session"; + + res = session->openLogicalChannel(aid, 0x00, seListener, &channel); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(channel, nullptr) << "Could not open channel"; + + res = channel->getSelectResponse(&selectResponse); + ASSERT_TRUE(res.isOk()) << "failed to get Select Response"; + ASSERT_GE(selectResponse.size(), 2); + ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00)); + ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90)); + ASSERT_TRUE( + verifyBerTlvData(selectResponse)) << "Select Response is not complete"; + + res = channel->transmit(apdu, &transmitResponse); + LOG(INFO) << "STATUS OF TRNSMIT: " << res.getExceptionCode() + << " Message: " << res.getMessage(); + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + ASSERT_TRUE(res.isOk()) << "failed to transmit"; + } + } + } + } + + void testUnauthorisedAPDU( + std::vector<uint8_t> aid, + std::vector<std::vector<uint8_t>> apdus) { + for (auto apdu : apdus) { + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + auto seListener = ndk::SharedRefBase::make<SEListener>(); + + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + ASSERT_NE(reader, nullptr) << "reader is null"; + bool status = false; + std::vector<uint8_t> selectResponse = {}; + std::vector<uint8_t> transmitResponse = {}; + auto res = reader->isSecureElementPresent(&status); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_TRUE(status); + + res = reader->openSession(&session); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(session, nullptr) << "Could not open session"; + + res = session->openLogicalChannel(aid, 0x00, seListener, &channel); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(channel, nullptr) << "Could not open channel"; + + res = channel->getSelectResponse(&selectResponse); + ASSERT_TRUE(res.isOk()) << "failed to get Select Response"; + ASSERT_GE(selectResponse.size(), 2); + ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00)); + ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90)); + ASSERT_TRUE( + verifyBerTlvData(selectResponse)) << "Select Response is not complete"; + + res = channel->transmit(apdu, &transmitResponse); + LOG(INFO) << "STATUS OF TRNSMIT: " << res.getExceptionCode() + << " Message: " << res.getMessage(); + + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + if (!res.isOk()) { + ASSERT_EQ(res.getExceptionCode(), EX_SECURITY); + ASSERT_FALSE(res.isOk()) << "expected failed status for this test"; + } + } + } + } + } + + bool supportOMAPIReaders() { + return (deviceSupportsFeature(FEATURE_SE_OMAPI_ESE.c_str())); + } + + void getFirstApiLevel(int32_t* outApiLevel) { + int32_t firstApiLevel = property_get_int32(FEATURE_SE_API_LEVEL.c_str(), -1); + if (firstApiLevel < 0) { + firstApiLevel = property_get_int32(FEATURE_SE_SDK_VERSION.c_str(), -1); + } + ASSERT_GT(firstApiLevel, 0); // first_api_level must exist + *outApiLevel = firstApiLevel; + return; + } + + bool supportsHardware() { + bool lowRamDevice = property_get_bool(FEATURE_SE_LOW_RAM.c_str(), true); + return !lowRamDevice || deviceSupportsFeature(FEATURE_SE_HARDWARE_WATCH.c_str()) || + deviceSupportsFeature(FEATURE_SE_OMAPI_SERVICE.c_str()); // android.se.omapi + } + + void SetUp() override { + ASSERT_TRUE(supportsHardware()); + int32_t apiLevel; + getFirstApiLevel(&apiLevel); + ASSERT_TRUE(apiLevel > 27); + ASSERT_TRUE(supportOMAPIReaders()); + LOG(INFO) << "get OMAPI service with name:" << GetParam(); + ::ndk::SpAIBinder ks2Binder(AServiceManager_getService(GetParam().c_str())); + mOmapiSeService = aidl::android::se::omapi::ISecureElementService::fromBinder(ks2Binder); + ASSERT_TRUE(mOmapiSeService); + + std::vector<std::string> readers = {}; + + if (mOmapiSeService != NULL) { + auto status = mOmapiSeService->getReaders(&readers); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + + for (auto readerName : readers) { + // Filter eSE readers only + if (readerName.find(ESE_READER_PREFIX, 0) != std::string::npos) { + std::shared_ptr<::aidl::android::se::omapi::ISecureElementReader> reader; + status = mOmapiSeService->getReader(readerName, &reader); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + + mVSReaders[readerName] = reader; + } + } + } + } + + void TearDown() override { + if (mOmapiSeService != nullptr) { + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + reader->closeSessions(); + } + } + } + } + + static inline std::string const ESE_READER_PREFIX = "eSE"; + static inline std::string const FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese"; + static inline std::string const FEATURE_SE_LOW_RAM = "ro.config.low_ram"; + static inline std::string const FEATURE_SE_HARDWARE_WATCH = "android.hardware.type.watch"; + static inline std::string const FEATURE_SE_OMAPI_SERVICE = "com.android.se"; + static inline std::string const FEATURE_SE_SDK_VERSION = "ro.build.version.sdk"; + static inline std::string const FEATURE_SE_API_LEVEL = "ro.product.first_api_level"; + + std::vector<uint8_t> AID_40 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x40}; + std::vector<uint8_t> AID_41 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x41}; + std::vector<uint8_t> AID_42 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x42}; + std::vector<uint8_t> AID_43 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x43}; + std::vector<uint8_t> AID_44 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x44}; + std::vector<uint8_t> AID_45 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x45}; + std::vector<uint8_t> AID_46 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x46}; + std::vector<uint8_t> AID_47 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x47}; + std::vector<uint8_t> AID_48 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x48}; + std::vector<uint8_t> AID_49 = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x49}; + std::vector<uint8_t> AID_4A = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4A}; + std::vector<uint8_t> AID_4B = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4B}; + std::vector<uint8_t> AID_4C = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4C}; + std::vector<uint8_t> AID_4D = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4D}; + std::vector<uint8_t> AID_4E = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4E}; + std::vector<uint8_t> AID_4F = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x4F}; + + std::vector<std::vector<uint8_t>> AUTHORIZED_AID = {AID_40, AID_41, AID_42, AID_44, AID_45, + AID_47, AID_48, AID_49, AID_4A, AID_4B, + AID_4C, AID_4D, AID_4E, AID_4F}; + std::vector<std::vector<uint8_t>> UNAUTHORIZED_AID = {AID_43, AID_46}; + + /* Authorized APDU for AID_40 */ + std::vector<std::vector<uint8_t>> AUTHORIZED_APDU_AID_40 = { + {0x00, 0x06, 0x00, 0x00}, + {0xA0, 0x06, 0x00, 0x00}, + }; + /* Unauthorized APDU for AID_40 */ + std::vector<std::vector<uint8_t>> UNAUTHORIZED_APDU_AID_40 = { + {0x00, 0x08, 0x00, 0x00, 0x00}, + {0x80, 0x06, 0x00, 0x00}, + {0xA0, 0x08, 0x00, 0x00, 0x00}, + {0x94, 0x06, 0x00, 0x00, 0x00}, + }; + + /* Authorized APDU for AID_41 */ + std::vector<std::vector<uint8_t>> AUTHORIZED_APDU_AID_41 = { + {0x94, 0x06, 0x00, 0x00}, + {0x94, 0x08, 0x00, 0x00, 0x00}, + {0x94, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00}, + {0x94, 0x0A, 0x00, 0x00, 0x01, 0xAA}}; + /* Unauthorized APDU for AID_41 */ + std::vector<std::vector<uint8_t>> UNAUTHORIZED_APDU_AID_41 = { + {0x00, 0x06, 0x00, 0x00}, + {0x80, 0x06, 0x00, 0x00}, + {0xA0, 0x06, 0x00, 0x00}, + {0x00, 0x08, 0x00, 0x00, 0x00}, + {0x00, 0x0A, 0x00, 0x00, 0x01, 0xAA}, + {0x80, 0x0A, 0x00, 0x00, 0x01, 0xAA}, + {0xA0, 0x0A, 0x00, 0x00, 0x01, 0xAA}, + {0x80, 0x08, 0x00, 0x00, 0x00}, + {0xA0, 0x08, 0x00, 0x00, 0x00}, + {0x00, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00}, + {0x80, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00}, + {0xA0, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00}, + }; + + std::shared_ptr<aidl::android::se::omapi::ISecureElementService> mOmapiSeService; + + std::map<std::string, std::shared_ptr<aidl::android::se::omapi::ISecureElementReader>> + mVSReaders = {}; +}; + +TEST_P(OMAPISEAccessControlTest, TestAuthorizedAID) { + testSelectableAid(AUTHORIZED_AID); +} + +TEST_P(OMAPISEAccessControlTest, TestUnauthorizedAID) { + testUnauthorisedAid(UNAUTHORIZED_AID); +} + +TEST_P(OMAPISEAccessControlTest, TestAuthorizedAPDUAID40) { + testTransmitAPDU(AID_40, AUTHORIZED_APDU_AID_40); +} + +TEST_P(OMAPISEAccessControlTest, TestUnauthorisedAPDUAID40) { + testUnauthorisedAPDU(AID_40, UNAUTHORIZED_APDU_AID_40); +} + +TEST_P(OMAPISEAccessControlTest, TestAuthorizedAPDUAID41) { + testTransmitAPDU(AID_41, AUTHORIZED_APDU_AID_41); +} + +TEST_P(OMAPISEAccessControlTest, TestUnauthorisedAPDUAID41) { + testUnauthorisedAPDU(AID_41, UNAUTHORIZED_APDU_AID_41); +} + +INSTANTIATE_TEST_SUITE_P(PerInstance, OMAPISEAccessControlTest, + testing::ValuesIn(::android::getAidlHalInstanceNames( + aidl::android::se::omapi::ISecureElementService::descriptor)), + android::hardware::PrintInstanceNameToString); +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OMAPISEAccessControlTest); + +} // namespace diff --git a/omapi/aidl/vts/functional/omapi/Android.bp b/omapi/aidl/vts/functional/omapi/Android.bp new file mode 100644 index 000000000000..c3ab8d13920c --- /dev/null +++ b/omapi/aidl/vts/functional/omapi/Android.bp @@ -0,0 +1,54 @@ +// +// Copyright (C) 2021 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_test { + name: "VtsHalOmapiSeServiceV1_TargetTest", + defaults: [ + "VtsHalTargetTestDefaults", + "use_libaidlvintf_gtest_helper_static", + ], + srcs: [ + "VtsHalOmapiSeServiceV1_TargetTest.cpp", + ], + shared_libs: [ + "libbase", + "liblog", + "libcutils", + "libhidlbase", + "libnativehelper", + "libutils", + "libbinder_ndk", + ], + static_libs: [ + "VtsHalHidlTargetTestBase", + "android.se.omapi-V1-ndk", + ], + cflags: [ + "-O0", + "-g", + "-Wall", + "-Werror", + ], + require_root: true, + test_suites: [ + "general-tests", + "vts", + ], +} diff --git a/omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.cpp b/omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.cpp new file mode 100644 index 000000000000..319cb7e70884 --- /dev/null +++ b/omapi/aidl/vts/functional/omapi/VtsHalOmapiSeServiceV1_TargetTest.cpp @@ -0,0 +1,609 @@ +/* + * Copyright (C) 2021 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. + */ + +#include <aidl/android/se/omapi/BnSecureElementListener.h> +#include <aidl/android/se/omapi/ISecureElementChannel.h> +#include <aidl/android/se/omapi/ISecureElementListener.h> +#include <aidl/android/se/omapi/ISecureElementReader.h> +#include <aidl/android/se/omapi/ISecureElementService.h> +#include <aidl/android/se/omapi/ISecureElementSession.h> + +#include <VtsCoreUtil.h> +#include <aidl/Gtest.h> +#include <aidl/Vintf.h> +#include <android-base/logging.h> +#include <android/binder_manager.h> +#include <binder/IServiceManager.h> +#include <cutils/properties.h> +#include <gtest/gtest.h> +#include <hidl/GtestPrinter.h> +#include <hidl/ServiceManagement.h> +#include <utils/String16.h> + +using namespace std; +using namespace ::testing; +using namespace android; + +int main(int argc, char** argv) { + InitGoogleTest(&argc, argv); + int status = RUN_ALL_TESTS(); + return status; +} + +namespace { + +class OMAPISEServiceHalTest : public TestWithParam<std::string> { + protected: + class SEListener : public ::aidl::android::se::omapi::BnSecureElementListener {}; + + void testSelectableAid( + std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> reader, + std::vector<uint8_t> aid, std::vector<uint8_t>& selectResponse) { + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>(); + + ASSERT_NE(reader, nullptr) << "reader is null"; + + bool status = false; + auto res = reader->isSecureElementPresent(&status); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_TRUE(status); + + res = reader->openSession(&session); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(session, nullptr) << "Could not open session"; + + res = session->openLogicalChannel(aid, 0x00, seListener, &channel); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(channel, nullptr) << "Could not open channel"; + + res = channel->getSelectResponse(&selectResponse); + ASSERT_TRUE(res.isOk()) << "failed to get Select Response"; + ASSERT_GE(selectResponse.size(), 2); + + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + + ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00)); + ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90)); + } + + void testNonSelectableAid( + std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> reader, + std::vector<uint8_t> aid) { + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>(); + + ASSERT_NE(reader, nullptr) << "reader is null"; + + bool status = false; + auto res = reader->isSecureElementPresent(&status); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_TRUE(status); + + res = reader->openSession(&session); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(session, nullptr) << "Could not open session"; + + res = session->openLogicalChannel(aid, 0x00, seListener, &channel); + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + + LOG(ERROR) << res.getMessage(); + ASSERT_FALSE(res.isOk()) << "expected to fail to open channel for this test"; + } + + /** + * Verifies TLV data + * + * @return true if the data is tlv formatted, false otherwise + */ + bool verifyBerTlvData(std::vector<uint8_t> tlv) { + if (tlv.size() == 0) { + LOG(ERROR) << "Invalid tlv, null"; + return false; + } + int i = 0; + if ((tlv[i++] & 0x1F) == 0x1F) { + // extra byte for TAG field + i++; + } + + int len = tlv[i++] & 0xFF; + if (len > 127) { + // more than 1 byte for length + int bytesLength = len - 128; + len = 0; + for (int j = bytesLength; j > 0; j--) { + len += (len << 8) + (tlv[i++] & 0xFF); + } + } + // Additional 2 bytes for the SW + return (tlv.size() == (i + len + 2)); + } + + void internalTransmitApdu( + std::shared_ptr<aidl::android::se::omapi::ISecureElementReader> reader, + std::vector<uint8_t> apdu, std::vector<uint8_t>& transmitResponse) { + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>(); + std::vector<uint8_t> selectResponse = {}; + + ASSERT_NE(reader, nullptr) << "reader is null"; + + bool status = false; + auto res = reader->isSecureElementPresent(&status); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_TRUE(status); + + res = reader->openSession(&session); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(session, nullptr) << "Could not open session"; + + res = session->openLogicalChannel(SELECTABLE_AID, 0x00, seListener, &channel); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(channel, nullptr) << "Could not open channel"; + + res = channel->getSelectResponse(&selectResponse); + ASSERT_TRUE(res.isOk()) << "failed to get Select Response"; + ASSERT_GE(selectResponse.size(), 2); + + res = channel->transmit(apdu, &transmitResponse); + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + LOG(INFO) << "STATUS OF TRNSMIT: " << res.getExceptionCode() + << " Message: " << res.getMessage(); + ASSERT_TRUE(res.isOk()) << "failed to transmit"; + } + + bool supportOMAPIReaders() { + return (deviceSupportsFeature(FEATURE_SE_OMAPI_ESE.c_str())); + } + + void SetUp() override { + LOG(INFO) << "get OMAPI service with name:" << GetParam(); + ::ndk::SpAIBinder ks2Binder(AServiceManager_getService(GetParam().c_str())); + mOmapiSeService = aidl::android::se::omapi::ISecureElementService::fromBinder(ks2Binder); + ASSERT_TRUE(mOmapiSeService); + + std::vector<std::string> readers = {}; + + if (omapiSecureService() != NULL) { + auto status = omapiSecureService()->getReaders(&readers); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + + for (auto readerName : readers) { + // Filter eSE readers only + if (readerName.find(ESE_READER_PREFIX, 0) != std::string::npos) { + std::shared_ptr<::aidl::android::se::omapi::ISecureElementReader> reader; + status = omapiSecureService()->getReader(readerName, &reader); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + + mVSReaders[readerName] = reader; + } + } + } + } + + void TearDown() override { + if (mOmapiSeService != nullptr) { + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + reader->closeSessions(); + } + } + } + } + + bool isDebuggableBuild() { + char value[PROPERTY_VALUE_MAX] = {0}; + property_get("ro.system.build.type", value, ""); + if (strcmp(value, "userdebug") == 0) { + return true; + } + if (strcmp(value, "eng") == 0) { + return true; + } + return false; + } + + std::shared_ptr<aidl::android::se::omapi::ISecureElementService> omapiSecureService() { + return mOmapiSeService; + } + + static inline std::string const ESE_READER_PREFIX = "eSE"; + static inline std::string const FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese"; + + std::vector<uint8_t> SELECTABLE_AID = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x31}; + std::vector<uint8_t> LONG_SELECT_RESPONSE_AID = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, + 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, + 0x43, 0x54, 0x53, 0x32}; + std::vector<uint8_t> NON_SELECTABLE_AID = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, + 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0xFF}; + + std::vector<std::vector<uint8_t>> ILLEGAL_COMMANDS_TRANSMIT = { + {0x00, 0x70, 0x00, 0x00}, + {0x00, 0x70, 0x80, 0x00}, + {0x00, 0xA4, 0x04, 0x04, 0x10, 0x4A, 0x53, 0x52, 0x31, 0x37, 0x37, + 0x54, 0x65, 0x73, 0x74, 0x65, 0x72, 0x20, 0x31, 0x2E, 0x30}}; + + /* OMAPI APDU Test case 1 and 3 */ + std::vector<std::vector<uint8_t>> NO_DATA_APDU = {{0x00, 0x06, 0x00, 0x00}, + {0x80, 0x06, 0x00, 0x00}, + {0xA0, 0x06, 0x00, 0x00}, + {0x94, 0x06, 0x00, 0x00}, + {0x00, 0x0A, 0x00, 0x00, 0x01, 0xAA}, + {0x80, 0x0A, 0x00, 0x00, 0x01, 0xAA}, + {0xA0, 0x0A, 0x00, 0x00, 0x01, 0xAA}, + {0x94, 0x0A, 0x00, 0x00, 0x01, 0xAA}}; + + /* OMAPI APDU Test case 2 and 4 */ + std::vector<std::vector<uint8_t>> DATA_APDU = {{0x00, 0x08, 0x00, 0x00, 0x00}, + {0x80, 0x08, 0x00, 0x00, 0x00}, + {0xA0, 0x08, 0x00, 0x00, 0x00}, + {0x94, 0x08, 0x00, 0x00, 0x00}, + {0x00, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00}, + {0x80, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00}, + {0xA0, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00}, + {0x94, 0x0C, 0x00, 0x00, 0x01, 0xAA, 0x00}}; + + /* Case 2 APDU command expects the P2 received in the SELECT command as 1-byte outgoing data */ + std::vector<uint8_t> CHECK_SELECT_P2_APDU = {0x00, 0xF4, 0x00, 0x00, 0x00}; + + /* OMAPI APDU Test case 1 and 3 */ + std::vector<std::vector<uint8_t>> SW_62xx_NO_DATA_APDU = {{0x00, 0xF3, 0x00, 0x06}, + {0x00, 0xF3, 0x00, 0x0A, 0x01, 0xAA}}; + + /* OMAPI APDU Test case 2 and 4 */ + std::vector<uint8_t> SW_62xx_DATA_APDU = {0x00, 0xF3, 0x00, 0x08, 0x00}; + std::vector<uint8_t> SW_62xx_VALIDATE_DATA_APDU = {0x00, 0xF3, 0x00, 0x0C, 0x01, 0xAA, 0x00}; + std::vector<std::vector<uint8_t>> SW_62xx = { + {0x62, 0x00}, {0x62, 0x81}, {0x62, 0x82}, {0x62, 0x83}, {0x62, 0x85}, {0x62, 0xF1}, + {0x62, 0xF2}, {0x63, 0xF1}, {0x63, 0xF2}, {0x63, 0xC2}, {0x62, 0x02}, {0x62, 0x80}, + {0x62, 0x84}, {0x62, 0x86}, {0x63, 0x00}, {0x63, 0x81}}; + + std::vector<std::vector<uint8_t>> SEGMENTED_RESP_APDU = { + // Get response Case2 61FF+61XX with answer length (P1P2) of 0x0800, 2048 bytes + {0x00, 0xC2, 0x08, 0x00, 0x00}, + // Get response Case4 61FF+61XX with answer length (P1P2) of 0x0800, 2048 bytes + {0x00, 0xC4, 0x08, 0x00, 0x02, 0x12, 0x34, 0x00}, + // Get response Case2 6100+61XX with answer length (P1P2) of 0x0800, 2048 bytes + {0x00, 0xC6, 0x08, 0x00, 0x00}, + // Get response Case4 6100+61XX with answer length (P1P2) of 0x0800, 2048 bytes + {0x00, 0xC8, 0x08, 0x00, 0x02, 0x12, 0x34, 0x00}, + // Test device buffer capacity 7FFF data + {0x00, 0xC2, 0x7F, 0xFF, 0x00}, + // Get response 6CFF+61XX with answer length (P1P2) of 0x0800, 2048 bytes + {0x00, 0xCF, 0x08, 0x00, 0x00}, + // Get response with another CLA with answer length (P1P2) of 0x0800, 2048 bytes + {0x94, 0xC2, 0x08, 0x00, 0x00}}; + long SERVICE_CONNECTION_TIME_OUT = 3000; + + std::shared_ptr<aidl::android::se::omapi::ISecureElementService> mOmapiSeService; + + std::map<std::string, std::shared_ptr<aidl::android::se::omapi::ISecureElementReader>> + mVSReaders = {}; +}; + +/** Tests getReaders API */ +TEST_P(OMAPISEServiceHalTest, TestGetReaders) { + std::vector<std::shared_ptr<aidl::android::se::omapi::ISecureElementReader>> eseReaders = + {}; + + for (const auto& [name, reader] : mVSReaders) { + bool status = false; + LOG(INFO) << "Name of the reader: " << name; + + if (reader) { + auto res = reader->isSecureElementPresent(&status); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + } + ASSERT_TRUE(status); + + if (name.find(ESE_READER_PREFIX) == std::string::npos) { + LOG(ERROR) << "Incorrect Reader name"; + FAIL(); + } + + if (name.find(ESE_READER_PREFIX, 0) != std::string::npos) { + eseReaders.push_back(reader); + } else { + LOG(INFO) << "Reader not supported: " << name; + FAIL(); + } + } + + if (deviceSupportsFeature(FEATURE_SE_OMAPI_ESE.c_str())) { + ASSERT_GE(eseReaders.size(), 1); + } else { + ASSERT_TRUE(eseReaders.size() == 0); + } +} + +/** Tests OpenBasicChannel API when aid is null */ +TEST_P(OMAPISEServiceHalTest, TestOpenBasicChannelNullAid) { + ASSERT_TRUE(supportOMAPIReaders() == true); + std::vector<uint8_t> aid = {}; + auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>(); + + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + bool result = false; + + auto status = reader->openSession(&session); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + if (!session) { + LOG(ERROR) << "Could not open session"; + FAIL(); + } + + status = session->openBasicChannel(aid, 0x00, seListener, &channel); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + + if (channel != nullptr) { + status = channel->isBasicChannel(&result); + ASSERT_TRUE(status.isOk()) << "Basic Channel cannot be opened"; + } + } + } +} + +/** Tests OpenBasicChannel API when aid is provided */ +TEST_P(OMAPISEServiceHalTest, TestOpenBasicChannelNonNullAid) { + ASSERT_TRUE(supportOMAPIReaders() == true); + auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>(); + + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + bool result = false; + + auto status = reader->openSession(&session); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + if (!session) { + LOG(ERROR) << "Could not open session"; + FAIL(); + } + + status = session->openBasicChannel(SELECTABLE_AID, 0x00, seListener, &channel); + ASSERT_TRUE(status.isOk()) << status.getMessage(); + + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + + if (channel != nullptr) { + status = channel->isBasicChannel(&result); + ASSERT_TRUE(status.isOk()) << "Basic Channel cannot be opened"; + } + } + } +} + +/** Tests Select API */ +TEST_P(OMAPISEServiceHalTest, TestSelectableAid) { + ASSERT_TRUE(supportOMAPIReaders() == true); + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + std::vector<uint8_t> selectResponse = {}; + testSelectableAid(reader, SELECTABLE_AID, selectResponse); + } + } +} + +/** Tests Select API */ +TEST_P(OMAPISEServiceHalTest, TestLongSelectResponse) { + ASSERT_TRUE(supportOMAPIReaders() == true); + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + std::vector<uint8_t> selectResponse = {}; + testSelectableAid(reader, LONG_SELECT_RESPONSE_AID, selectResponse); + ASSERT_TRUE(verifyBerTlvData(selectResponse)) << "Select Response is not complete"; + } + } +} + +/** Test to fail open channel with wrong aid */ +TEST_P(OMAPISEServiceHalTest, TestWrongAid) { + ASSERT_TRUE(supportOMAPIReaders() == true); + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + testNonSelectableAid(reader, NON_SELECTABLE_AID); + } + } +} + +/** Tests with invalid cmds in Transmit */ +TEST_P(OMAPISEServiceHalTest, TestSecurityExceptionInTransmit) { + ASSERT_TRUE(supportOMAPIReaders() == true); + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + std::shared_ptr<aidl::android::se::omapi::ISecureElementSession> session; + std::shared_ptr<aidl::android::se::omapi::ISecureElementChannel> channel; + auto seListener = ndk::SharedRefBase::make<::OMAPISEServiceHalTest::SEListener>(); + std::vector<uint8_t> selectResponse = {}; + + ASSERT_NE(reader, nullptr) << "reader is null"; + + bool status = false; + auto res = reader->isSecureElementPresent(&status); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_TRUE(status); + + res = reader->openSession(&session); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(session, nullptr) << "Could not open session"; + + res = session->openLogicalChannel(SELECTABLE_AID, 0x00, seListener, &channel); + ASSERT_TRUE(res.isOk()) << res.getMessage(); + ASSERT_NE(channel, nullptr) << "Could not open channel"; + + res = channel->getSelectResponse(&selectResponse); + ASSERT_TRUE(res.isOk()) << "failed to get Select Response"; + ASSERT_GE(selectResponse.size(), 2); + + ASSERT_EQ((selectResponse[selectResponse.size() - 1] & 0xFF), (0x00)); + ASSERT_EQ((selectResponse[selectResponse.size() - 2] & 0xFF), (0x90)); + + for (auto cmd : ILLEGAL_COMMANDS_TRANSMIT) { + std::vector<uint8_t> response = {}; + res = channel->transmit(cmd, &response); + ASSERT_EQ(res.getExceptionCode(), EX_SECURITY); + ASSERT_FALSE(res.isOk()) << "expected failed status for this test"; + } + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + } + } +} + +/** + * Tests Transmit API for all readers. + * + * Checks the return status and verifies the size of the + * response. + */ +TEST_P(OMAPISEServiceHalTest, TestTransmitApdu) { + ASSERT_TRUE(supportOMAPIReaders() == true); + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + for (auto apdu : NO_DATA_APDU) { + std::vector<uint8_t> response = {}; + internalTransmitApdu(reader, apdu, response); + ASSERT_GE(response.size(), 2); + ASSERT_EQ((response[response.size() - 1] & 0xFF), (0x00)); + ASSERT_EQ((response[response.size() - 2] & 0xFF), (0x90)); + } + + for (auto apdu : DATA_APDU) { + std::vector<uint8_t> response = {}; + internalTransmitApdu(reader, apdu, response); + /* 256 byte data and 2 bytes of status word */ + ASSERT_GE(response.size(), 258); + ASSERT_EQ((response[response.size() - 1] & 0xFF), (0x00)); + ASSERT_EQ((response[response.size() - 2] & 0xFF), (0x90)); + } + } + } +} + +/** + * Tests if underlying implementations returns the correct Status Word + * + * TO verify that : + * - the device does not modify the APDU sent to the Secure Element + * - the warning code is properly received by the application layer as SW answer + * - the verify that the application layer can fetch the additionnal data (when present) + */ +TEST_P(OMAPISEServiceHalTest, testStatusWordTransmit) { + ASSERT_TRUE(supportOMAPIReaders() == true); + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + for (auto apdu : SW_62xx_NO_DATA_APDU) { + for (uint8_t i = 0x00; i < SW_62xx.size(); i++) { + apdu[2] = i + 1; + std::vector<uint8_t> response = {}; + internalTransmitApdu(reader, apdu, response); + std::vector<uint8_t> SW = SW_62xx[i]; + ASSERT_GE(response.size(), 2); + ASSERT_EQ(response[response.size() - 1], SW[1]); + ASSERT_EQ(response[response.size() - 2], SW[0]); + } + } + + for (uint8_t i = 0x00; i < SW_62xx.size(); i++) { + std::vector<uint8_t> apdu = SW_62xx_DATA_APDU; + apdu[2] = i + 1; + std::vector<uint8_t> response = {}; + internalTransmitApdu(reader, apdu, response); + std::vector<uint8_t> SW = SW_62xx[i]; + ASSERT_GE(response.size(), 3); + ASSERT_EQ(response[response.size() - 1], SW[1]); + ASSERT_EQ(response[response.size() - 2], SW[0]); + } + + for (uint8_t i = 0x00; i < SW_62xx.size(); i++) { + std::vector<uint8_t> apdu = SW_62xx_VALIDATE_DATA_APDU; + apdu[2] = i + 1; + std::vector<uint8_t> response = {}; + internalTransmitApdu(reader, apdu, response); + ASSERT_GE(response.size(), apdu.size() + 2); + std::vector<uint8_t> responseSubstring((response.begin() + 0), + (response.begin() + apdu.size())); + // We should not care about which channel number is actually assigned. + responseSubstring[0] = apdu[0]; + ASSERT_TRUE((responseSubstring == apdu)); + std::vector<uint8_t> SW = SW_62xx[i]; + ASSERT_EQ(response[response.size() - 1], SW[1]); + ASSERT_EQ(response[response.size() - 2], SW[0]); + } + } + } +} + +/** Test if the responses are segmented by the underlying implementation */ +TEST_P(OMAPISEServiceHalTest, TestSegmentedResponseTransmit) { + ASSERT_TRUE(supportOMAPIReaders() == true); + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + for (auto apdu : SEGMENTED_RESP_APDU) { + std::vector<uint8_t> response = {}; + internalTransmitApdu(reader, apdu, response); + int expectedLength = (0x00 << 24) | (0x00 << 16) | (apdu[2] << 8) | apdu[3]; + ASSERT_EQ(response.size(), (expectedLength + 2)); + ASSERT_EQ((response[response.size() - 1] & 0xFF), (0x00)); + ASSERT_EQ((response[response.size() - 2] & 0xFF), (0x90)); + ASSERT_EQ((response[response.size() - 3] & 0xFF), (0xFF)); + } + } + } +} + +/** + * Tests the P2 value of the select command. + * + * Verifies that the default P2 value (0x00) is not modified by the underlying implementation. + */ +TEST_P(OMAPISEServiceHalTest, TestP2Value) { + ASSERT_TRUE(supportOMAPIReaders() == true); + if (mVSReaders.size() > 0) { + for (const auto& [name, reader] : mVSReaders) { + std::vector<uint8_t> response = {}; + internalTransmitApdu(reader, CHECK_SELECT_P2_APDU, response); + ASSERT_GE(response.size(), 3); + ASSERT_EQ((response[response.size() - 1] & 0xFF), (0x00)); + ASSERT_EQ((response[response.size() - 2] & 0xFF), (0x90)); + ASSERT_EQ((response[response.size() - 3] & 0xFF), (0x00)); + } + } +} + +INSTANTIATE_TEST_SUITE_P(PerInstance, OMAPISEServiceHalTest, + testing::ValuesIn(::android::getAidlHalInstanceNames( + aidl::android::se::omapi::ISecureElementService::descriptor)), + android::hardware::PrintInstanceNameToString); +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OMAPISEServiceHalTest); + +} // namespace diff --git a/omapi/java/Android.bp b/omapi/java/Android.bp new file mode 100644 index 000000000000..8d38da048d9b --- /dev/null +++ b/omapi/java/Android.bp @@ -0,0 +1,17 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +filegroup { + name: "framework-omapi-sources", + srcs: [ + "**/*.java", + "**/*.aidl", + ], + visibility: ["//frameworks/base"], +} diff --git a/core/java/android/se/OWNERS b/omapi/java/android/se/OWNERS index 5682fd3281f4..5682fd3281f4 100644 --- a/core/java/android/se/OWNERS +++ b/omapi/java/android/se/OWNERS diff --git a/core/java/android/se/omapi/Channel.java b/omapi/java/android/se/omapi/Channel.java index 90ce11ae0313..90ce11ae0313 100644 --- a/core/java/android/se/omapi/Channel.java +++ b/omapi/java/android/se/omapi/Channel.java diff --git a/core/java/android/se/omapi/OWNERS b/omapi/java/android/se/omapi/OWNERS index 5682fd3281f4..5682fd3281f4 100644 --- a/core/java/android/se/omapi/OWNERS +++ b/omapi/java/android/se/omapi/OWNERS diff --git a/core/java/android/se/omapi/Reader.java b/omapi/java/android/se/omapi/Reader.java index 90c934d189fa..3c2135d9bc9d 100644 --- a/core/java/android/se/omapi/Reader.java +++ b/omapi/java/android/se/omapi/Reader.java @@ -170,7 +170,9 @@ public final class Reader { try { closeSessions(); return mReader.reset(); - } catch (RemoteException ignore) {return false;} + } catch (RemoteException ignore) { + return false; + } } } } diff --git a/core/java/android/se/omapi/SEService.java b/omapi/java/android/se/omapi/SEService.java index 333af91ac872..f42ca364b6d9 100644 --- a/core/java/android/se/omapi/SEService.java +++ b/omapi/java/android/se/omapi/SEService.java @@ -230,20 +230,20 @@ public final class SEService { * is not exist. * @return A Reader object for this uicc slot. */ - public @NonNull Reader getUiccReader(int slotNumber) { - if (slotNumber < 1) { - throw new IllegalArgumentException("slotNumber should be larger than 0"); - } - loadReaders(); + public @NonNull Reader getUiccReader(int slotNumber) { + if (slotNumber < 1) { + throw new IllegalArgumentException("slotNumber should be larger than 0"); + } + loadReaders(); - String readerName = UICC_TERMINAL + slotNumber; - Reader reader = mReaders.get(readerName); + String readerName = UICC_TERMINAL + slotNumber; + Reader reader = mReaders.get(readerName); - if (reader == null) { + if (reader == null) { throw new IllegalArgumentException("Reader:" + readerName + " doesn't exist"); - } + } - return reader; + return reader; } /** diff --git a/core/java/android/se/omapi/Session.java b/omapi/java/android/se/omapi/Session.java index d5f8c82bf47e..d5f8c82bf47e 100644 --- a/core/java/android/se/omapi/Session.java +++ b/omapi/java/android/se/omapi/Session.java diff --git a/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp b/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp index 7ff9cedab5f7..b2c82c6fb846 100644 --- a/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp +++ b/packages/PrintSpooler/jni/com_android_printspooler_util_BitmapSerializeUtils.cpp @@ -30,11 +30,8 @@ static bool writeAllBytes(const int fd, void* buffer, const size_t byteCount) { char* writeBuffer = static_cast<char*>(buffer); size_t remainingBytes = byteCount; while (remainingBytes > 0) { - ssize_t writtenByteCount = write(fd, writeBuffer, remainingBytes); + ssize_t writtenByteCount = TEMP_FAILURE_RETRY(write(fd, writeBuffer, remainingBytes)); if (writtenByteCount == -1) { - if (errno == EINTR) { - continue; - } __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Error writing to buffer: %d", errno); return false; @@ -49,19 +46,17 @@ static bool readAllBytes(const int fd, void* buffer, const size_t byteCount) { char* readBuffer = static_cast<char*>(buffer); size_t remainingBytes = byteCount; while (remainingBytes > 0) { - ssize_t readByteCount = read(fd, readBuffer, remainingBytes); - - remainingBytes -= readByteCount; - readBuffer += readByteCount; - + ssize_t readByteCount = TEMP_FAILURE_RETRY(read(fd, readBuffer, remainingBytes)); if (readByteCount == -1) { - if (errno == EINTR) { - continue; - } __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "Error reading from buffer: %d", errno); return false; - } else if (readByteCount == 0 && remainingBytes > 0) { + } + + remainingBytes -= readByteCount; + readBuffer += readByteCount; + + if (readByteCount == 0 && remainingBytes > 0) { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "File closed before all bytes were read. %zu/%zu remaining", remainingBytes, byteCount); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java index 58d2185ea9e9..389892ed15e4 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java @@ -160,10 +160,12 @@ public class BluetoothEventManager { private void registerIntentReceiver(BroadcastReceiver receiver, IntentFilter filter) { if (mUserHandle == null) { // If userHandle has not been provided, simply call registerReceiver. - mContext.registerReceiver(receiver, filter, null, mReceiverHandler); + mContext.registerReceiver(receiver, filter, null, mReceiverHandler, + Context.RECEIVER_EXPORTED); } else { // userHandle was explicitly specified, so need to call multi-user aware API. - mContext.registerReceiverAsUser(receiver, mUserHandle, filter, null, mReceiverHandler); + mContext.registerReceiverAsUser(receiver, mUserHandle, filter, null, mReceiverHandler, + Context.RECEIVER_EXPORTED); } } @@ -305,7 +307,8 @@ public class BluetoothEventManager { CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); if (cachedDevice == null) { cachedDevice = mDeviceManager.addDevice(device); - Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice"); + Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice " + + cachedDevice.getDevice().getAnonymizedAddress()); } else if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED && !cachedDevice.getDevice().isConnected()) { // Dispatch device add callback to show bonded but diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 76b0b385dbda..97cc948e134b 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -575,6 +575,9 @@ <uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" /> <uses-permission android:name="android.permission.DEBUG_VIRTUAL_MACHINE" /> + <!-- Permission required for CTS test - SettingsMultiPaneDeepLinkTest --> + <uses-permission android:name="android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java index 15f77ffc08fd..8a213ec502e4 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java @@ -239,18 +239,20 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin if (mGhbmView != null && surface == null) { Log.e(TAG, "doIlluminate | surface must be non-null for GHBM"); } - mHbmProvider.enableHbm(mHbmType, surface, () -> { - if (mGhbmView != null) { - mGhbmView.drawIlluminationDot(mSensorRect); - } - if (onIlluminatedRunnable != null) { - // No framework API can reliably tell when a frame reaches the panel. A timeout - // is the safest solution. - postDelayed(onIlluminatedRunnable, mOnIlluminatedDelayMs); - } else { - Log.w(TAG, "doIlluminate | onIlluminatedRunnable is null"); - } - }); + if (mHbmProvider != null) { + mHbmProvider.enableHbm(mHbmType, surface, () -> { + if (mGhbmView != null) { + mGhbmView.drawIlluminationDot(mSensorRect); + } + if (onIlluminatedRunnable != null) { + // No framework API can reliably tell when a frame reaches the panel. A timeout + // is the safest solution. + postDelayed(onIlluminatedRunnable, mOnIlluminatedDelayMs); + } else { + Log.w(TAG, "doIlluminate | onIlluminatedRunnable is null"); + } + }); + } } @Override @@ -263,6 +265,8 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin mGhbmView.setGhbmIlluminationListener(null); mGhbmView.setVisibility(View.INVISIBLE); } - mHbmProvider.disableHbm(null /* onHbmDisabled */); + if (mHbmProvider != null) { + mHbmProvider.disableHbm(null /* onHbmDisabled */); + } } } diff --git a/services/core/java/com/android/server/AppFuseMountException.java b/services/core/java/com/android/server/AppFuseMountException.java new file mode 100644 index 000000000000..9a9379e4a1c7 --- /dev/null +++ b/services/core/java/com/android/server/AppFuseMountException.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2021 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; + +import android.os.Parcel; + +/** + * An exception that indicates there was an error with a + * app fuse mount operation. + */ +public class AppFuseMountException extends Exception { + public AppFuseMountException(String detailMessage) { + super(detailMessage); + } + + public AppFuseMountException(String detailMessage, Throwable throwable) { + super(detailMessage, throwable); + } + + /** + * Rethrow as a {@link RuntimeException} subclass that is handled by + * {@link Parcel#writeException(Exception)}. + */ + public IllegalArgumentException rethrowAsParcelableException() { + throw new IllegalStateException(getMessage(), this); + } +} diff --git a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java index 197321f1cb6a..263ff189a288 100644 --- a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java +++ b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java @@ -35,6 +35,7 @@ import com.android.internal.annotations.VisibleForTesting; * when Bluetooth is on and Bluetooth is in one of the following situations: * 1. Bluetooth A2DP is connected. * 2. Bluetooth Hearing Aid profile is connected. + * 3. Bluetooth LE Audio is connected */ class BluetoothAirplaneModeListener { private static final String TAG = "BluetoothAirplaneModeListener"; @@ -132,7 +133,7 @@ class BluetoothAirplaneModeListener { return false; } if (!mAirplaneHelper.isBluetoothOn() || !mAirplaneHelper.isAirplaneModeOn() - || !mAirplaneHelper.isA2dpOrHearingAidConnected()) { + || !mAirplaneHelper.isMediaProfileConnected()) { return false; } return true; diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index f62935ab1b13..8860a8164109 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -35,6 +35,7 @@ import android.app.BroadcastOptions; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothHearingAid; +import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProtoEnums; import android.bluetooth.IBluetooth; @@ -456,12 +457,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } } else if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action) - || BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(action)) { + || BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(action) + || BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED.equals(action)) { final int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED) && state == BluetoothProfile.STATE_DISCONNECTED - && !mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) { + && !mBluetoothModeChangeHelper.isMediaProfileConnected()) { Slog.i(TAG, "Device disconnected, reactivating pending flag changes"); onInitFlagsChanged(); } @@ -2291,7 +2293,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.d(TAG, "MESSAGE_INIT_FLAGS_CHANGED"); } mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED); - if (mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) { + if (mBluetoothModeChangeHelper.isMediaProfileConnected()) { Slog.i(TAG, "Delaying MESSAGE_INIT_FLAGS_CHANGED by " + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS + " ms due to existing connections"); diff --git a/services/core/java/com/android/server/BluetoothModeChangeHelper.java b/services/core/java/com/android/server/BluetoothModeChangeHelper.java index 3642e4dccf34..e5854c968207 100644 --- a/services/core/java/com/android/server/BluetoothModeChangeHelper.java +++ b/services/core/java/com/android/server/BluetoothModeChangeHelper.java @@ -20,6 +20,7 @@ import android.annotation.RequiresPermission; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothHearingAid; +import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile.ServiceListener; import android.content.Context; @@ -37,6 +38,7 @@ import com.android.internal.annotations.VisibleForTesting; public class BluetoothModeChangeHelper { private volatile BluetoothA2dp mA2dp; private volatile BluetoothHearingAid mHearingAid; + private volatile BluetoothLeAudio mLeAudio; private final BluetoothAdapter mAdapter; private final Context mContext; @@ -47,6 +49,7 @@ public class BluetoothModeChangeHelper { mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP); mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.HEARING_AID); + mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.LE_AUDIO); } private final ServiceListener mProfileServiceListener = new ServiceListener() { @@ -60,6 +63,9 @@ public class BluetoothModeChangeHelper { case BluetoothProfile.HEARING_AID: mHearingAid = (BluetoothHearingAid) proxy; break; + case BluetoothProfile.LE_AUDIO: + mLeAudio = (BluetoothLeAudio) proxy; + break; default: break; } @@ -75,6 +81,9 @@ public class BluetoothModeChangeHelper { case BluetoothProfile.HEARING_AID: mHearingAid = null; break; + case BluetoothProfile.LE_AUDIO: + mLeAudio = null; + break; default: break; } @@ -82,8 +91,8 @@ public class BluetoothModeChangeHelper { }; @VisibleForTesting - public boolean isA2dpOrHearingAidConnected() { - return isA2dpConnected() || isHearingAidConnected(); + public boolean isMediaProfileConnected() { + return isA2dpConnected() || isHearingAidConnected() || isLeAudioConnected(); } @VisibleForTesting @@ -142,4 +151,12 @@ public class BluetoothModeChangeHelper { } return hearingAid.getConnectedDevices().size() > 0; } + + private boolean isLeAudioConnected() { + final BluetoothLeAudio leAudio = mLeAudio; + if (leAudio == null) { + return false; + } + return leAudio.getConnectedDevices().size() > 0; + } } diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index c7e906876526..a2c2dbd407a5 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -39,8 +39,6 @@ import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.TAG_NONE; import static android.net.TrafficStats.UID_TETHERING; -import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED; - import android.annotation.NonNull; import android.app.ActivityManager; import android.content.Context; @@ -72,7 +70,6 @@ import android.os.ServiceManager; import android.os.ServiceSpecificException; import android.os.StrictMode; import android.os.SystemClock; -import android.os.SystemProperties; import android.os.Trace; import android.text.TextUtils; import android.util.Log; @@ -446,9 +443,6 @@ public class NetworkManagementService extends INetworkManagementService.Stub { // push any existing quota or UID rules synchronized (mQuotaLock) { - // Netd unconditionally enable bandwidth control - SystemProperties.set(PROP_QTAGUID_ENABLED, "1"); - mStrictEnabled = true; setDataSaverModeEnabled(mDataSaverMode); diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java index c9608a55170e..3e0208411c21 100644 --- a/services/core/java/com/android/server/NsdService.java +++ b/services/core/java/com/android/server/NsdService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2021 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. @@ -20,24 +20,27 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.database.ContentObserver; -import android.net.NetworkStack; import android.net.Uri; import android.net.nsd.INsdManager; +import android.net.nsd.INsdManagerCallback; +import android.net.nsd.INsdServiceConnector; import android.net.nsd.NsdManager; import android.net.nsd.NsdServiceInfo; import android.os.Handler; import android.os.HandlerThread; +import android.os.IBinder; import android.os.Message; -import android.os.Messenger; +import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.util.Base64; +import android.util.Log; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.AsyncChannel; import com.android.internal.util.DumpUtils; import com.android.internal.util.State; import com.android.internal.util.StateMachine; @@ -72,12 +75,11 @@ public class NsdService extends INsdManager.Stub { /** * Clients receiving asynchronous messages */ - private final HashMap<Messenger, ClientInfo> mClients = new HashMap<>(); + private final HashMap<NsdServiceConnector, ClientInfo> mClients = new HashMap<>(); /* A map from unique id to client info */ private final SparseArray<ClientInfo> mIdToClientInfoMap= new SparseArray<>(); - private final AsyncChannel mReplyChannel = new AsyncChannel(); private final long mCleanupDelayMs; private static final int INVALID_ID = 0; @@ -149,65 +151,66 @@ public class NsdService extends INsdManager.Stub { class DefaultState extends State { @Override public boolean processMessage(Message msg) { - ClientInfo cInfo = null; + final ClientInfo cInfo; + final int clientId = msg.arg2; switch (msg.what) { - case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: - if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { - AsyncChannel c = (AsyncChannel) msg.obj; - if (DBG) Slog.d(TAG, "New client listening to asynchronous messages"); - c.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED); - cInfo = new ClientInfo(c, msg.replyTo); - mClients.put(msg.replyTo, cInfo); - } else { - Slog.e(TAG, "Client connection failure, error=" + msg.arg1); + case NsdManager.REGISTER_CLIENT: + final Pair<NsdServiceConnector, INsdManagerCallback> arg = + (Pair<NsdServiceConnector, INsdManagerCallback>) msg.obj; + final INsdManagerCallback cb = arg.second; + try { + cb.asBinder().linkToDeath(arg.first, 0); + cInfo = new ClientInfo(cb); + mClients.put(arg.first, cInfo); + } catch (RemoteException e) { + Log.w(TAG, "Client " + clientId + " has already died"); } break; - case AsyncChannel.CMD_CHANNEL_DISCONNECTED: - switch (msg.arg1) { - case AsyncChannel.STATUS_SEND_UNSUCCESSFUL: - Slog.e(TAG, "Send failed, client connection lost"); - break; - case AsyncChannel.STATUS_REMOTE_DISCONNECTION: - if (DBG) Slog.d(TAG, "Client disconnected"); - break; - default: - if (DBG) Slog.d(TAG, "Client connection lost with reason: " + msg.arg1); - break; - } - - cInfo = mClients.get(msg.replyTo); + case NsdManager.UNREGISTER_CLIENT: + final NsdServiceConnector connector = (NsdServiceConnector) msg.obj; + cInfo = mClients.remove(connector); if (cInfo != null) { cInfo.expungeAllRequests(); - mClients.remove(msg.replyTo); if (cInfo.isLegacy()) { mLegacyClientCount -= 1; } } maybeScheduleStop(); break; - case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: - AsyncChannel ac = new AsyncChannel(); - ac.connect(mContext, getHandler(), msg.replyTo); - break; case NsdManager.DISCOVER_SERVICES: - replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + cInfo = getClientInfoForReply(msg); + if (cInfo != null) { + cInfo.onDiscoverServicesFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); + } break; case NsdManager.STOP_DISCOVERY: - replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + cInfo = getClientInfoForReply(msg); + if (cInfo != null) { + cInfo.onStopDiscoveryFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); + } break; case NsdManager.REGISTER_SERVICE: - replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + cInfo = getClientInfoForReply(msg); + if (cInfo != null) { + cInfo.onRegisterServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); + } break; case NsdManager.UNREGISTER_SERVICE: - replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + cInfo = getClientInfoForReply(msg); + if (cInfo != null) { + cInfo.onUnregisterServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); + } break; case NsdManager.RESOLVE_SERVICE: - replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + cInfo = getClientInfoForReply(msg); + if (cInfo != null) { + cInfo.onResolveServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); + } break; case NsdManager.DAEMON_CLEANUP: mDaemon.maybeStop(); @@ -215,7 +218,7 @@ public class NsdService extends INsdManager.Stub { // This event should be only sent by the legacy (target SDK < S) clients. // Mark the sending client as legacy. case NsdManager.DAEMON_STARTUP: - cInfo = mClients.get(msg.replyTo); + cInfo = getClientInfoForReply(msg); if (cInfo != null) { cancelStop(); cInfo.setLegacy(); @@ -230,6 +233,11 @@ public class NsdService extends INsdManager.Stub { } return HANDLED; } + + private ClientInfo getClientInfoForReply(Message msg) { + final ListenerArgs args = (ListenerArgs) msg.obj; + return mClients.get(args.connector); + } } class DisabledState extends State { @@ -289,122 +297,119 @@ public class NsdService extends INsdManager.Stub { @Override public boolean processMessage(Message msg) { - ClientInfo clientInfo; - NsdServiceInfo servInfo; - int id; + final ClientInfo clientInfo; + final int id; + final int clientId = msg.arg2; + final ListenerArgs args; switch (msg.what) { - case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: - return NOT_HANDLED; - case AsyncChannel.CMD_CHANNEL_DISCONNECTED: - return NOT_HANDLED; case NsdManager.DISABLE: //TODO: cleanup clients transitionTo(mDisabledState); break; case NsdManager.DISCOVER_SERVICES: if (DBG) Slog.d(TAG, "Discover services"); - servInfo = (NsdServiceInfo) msg.obj; - clientInfo = mClients.get(msg.replyTo); + args = (ListenerArgs) msg.obj; + clientInfo = mClients.get(args.connector); if (requestLimitReached(clientInfo)) { - replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, - NsdManager.FAILURE_MAX_LIMIT); + clientInfo.onDiscoverServicesFailed( + clientId, NsdManager.FAILURE_MAX_LIMIT); break; } maybeStartDaemon(); id = getUniqueId(); - if (discoverServices(id, servInfo.getServiceType())) { + if (discoverServices(id, args.serviceInfo.getServiceType())) { if (DBG) { Slog.d(TAG, "Discover " + msg.arg2 + " " + id + - servInfo.getServiceType()); + args.serviceInfo.getServiceType()); } - storeRequestMap(msg.arg2, id, clientInfo, msg.what); - replyToMessage(msg, NsdManager.DISCOVER_SERVICES_STARTED, servInfo); + storeRequestMap(clientId, id, clientInfo, msg.what); + clientInfo.onDiscoverServicesStarted(clientId, args.serviceInfo); } else { stopServiceDiscovery(id); - replyToMessage(msg, NsdManager.DISCOVER_SERVICES_FAILED, + clientInfo.onDiscoverServicesFailed(clientId, NsdManager.FAILURE_INTERNAL_ERROR); } break; case NsdManager.STOP_DISCOVERY: if (DBG) Slog.d(TAG, "Stop service discovery"); - clientInfo = mClients.get(msg.replyTo); + args = (ListenerArgs) msg.obj; + clientInfo = mClients.get(args.connector); try { - id = clientInfo.mClientIds.get(msg.arg2); + id = clientInfo.mClientIds.get(clientId); } catch (NullPointerException e) { - replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + clientInfo.onStopDiscoveryFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); break; } - removeRequestMap(msg.arg2, id, clientInfo); + removeRequestMap(clientId, id, clientInfo); if (stopServiceDiscovery(id)) { - replyToMessage(msg, NsdManager.STOP_DISCOVERY_SUCCEEDED); + clientInfo.onStopDiscoverySucceeded(clientId); } else { - replyToMessage(msg, NsdManager.STOP_DISCOVERY_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + clientInfo.onStopDiscoveryFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); } break; case NsdManager.REGISTER_SERVICE: if (DBG) Slog.d(TAG, "Register service"); - clientInfo = mClients.get(msg.replyTo); + args = (ListenerArgs) msg.obj; + clientInfo = mClients.get(args.connector); if (requestLimitReached(clientInfo)) { - replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, - NsdManager.FAILURE_MAX_LIMIT); + clientInfo.onRegisterServiceFailed( + clientId, NsdManager.FAILURE_MAX_LIMIT); break; } maybeStartDaemon(); id = getUniqueId(); - if (registerService(id, (NsdServiceInfo) msg.obj)) { - if (DBG) Slog.d(TAG, "Register " + msg.arg2 + " " + id); - storeRequestMap(msg.arg2, id, clientInfo, msg.what); + if (registerService(id, args.serviceInfo)) { + if (DBG) Slog.d(TAG, "Register " + clientId + " " + id); + storeRequestMap(clientId, id, clientInfo, msg.what); // Return success after mDns reports success } else { unregisterService(id); - replyToMessage(msg, NsdManager.REGISTER_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + clientInfo.onRegisterServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); } break; case NsdManager.UNREGISTER_SERVICE: if (DBG) Slog.d(TAG, "unregister service"); - clientInfo = mClients.get(msg.replyTo); - try { - id = clientInfo.mClientIds.get(msg.arg2); - } catch (NullPointerException e) { - replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + args = (ListenerArgs) msg.obj; + clientInfo = mClients.get(args.connector); + if (clientInfo == null) { + Slog.e(TAG, "Unknown connector in unregistration"); break; } - removeRequestMap(msg.arg2, id, clientInfo); + id = clientInfo.mClientIds.get(clientId); + removeRequestMap(clientId, id, clientInfo); if (unregisterService(id)) { - replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_SUCCEEDED); + clientInfo.onUnregisterServiceSucceeded(clientId); } else { - replyToMessage(msg, NsdManager.UNREGISTER_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + clientInfo.onUnregisterServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); } break; case NsdManager.RESOLVE_SERVICE: if (DBG) Slog.d(TAG, "Resolve service"); - servInfo = (NsdServiceInfo) msg.obj; - clientInfo = mClients.get(msg.replyTo); - + args = (ListenerArgs) msg.obj; + clientInfo = mClients.get(args.connector); if (clientInfo.mResolvedService != null) { - replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED, - NsdManager.FAILURE_ALREADY_ACTIVE); + clientInfo.onResolveServiceFailed( + clientId, NsdManager.FAILURE_ALREADY_ACTIVE); break; } maybeStartDaemon(); id = getUniqueId(); - if (resolveService(id, servInfo)) { + if (resolveService(id, args.serviceInfo)) { clientInfo.mResolvedService = new NsdServiceInfo(); - storeRequestMap(msg.arg2, id, clientInfo, msg.what); + storeRequestMap(clientId, id, clientInfo, msg.what); } else { - replyToMessage(msg, NsdManager.RESOLVE_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR); + clientInfo.onResolveServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); } break; case NsdManager.NATIVE_DAEMON_EVENT: @@ -449,30 +454,27 @@ public class NsdService extends INsdManager.Stub { case NativeResponseCode.SERVICE_FOUND: /* NNN uniqueId serviceName regType domain */ servInfo = new NsdServiceInfo(cooked[2], cooked[3]); - clientInfo.mChannel.sendMessage(NsdManager.SERVICE_FOUND, 0, - clientId, servInfo); + clientInfo.onServiceFound(clientId, servInfo); break; case NativeResponseCode.SERVICE_LOST: /* NNN uniqueId serviceName regType domain */ servInfo = new NsdServiceInfo(cooked[2], cooked[3]); - clientInfo.mChannel.sendMessage(NsdManager.SERVICE_LOST, 0, - clientId, servInfo); + clientInfo.onServiceLost(clientId, servInfo); break; case NativeResponseCode.SERVICE_DISCOVERY_FAILED: /* NNN uniqueId errorCode */ - clientInfo.mChannel.sendMessage(NsdManager.DISCOVER_SERVICES_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR, clientId); + clientInfo.onDiscoverServicesFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); break; case NativeResponseCode.SERVICE_REGISTERED: /* NNN regId serviceName regType */ servInfo = new NsdServiceInfo(cooked[2], null); - clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_SUCCEEDED, - id, clientId, servInfo); + clientInfo.onRegisterServiceSucceeded(clientId, servInfo); break; case NativeResponseCode.SERVICE_REGISTRATION_FAILED: /* NNN regId errorCode */ - clientInfo.mChannel.sendMessage(NsdManager.REGISTER_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR, clientId); + clientInfo.onRegisterServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); break; case NativeResponseCode.SERVICE_UPDATED: /* NNN regId */ @@ -511,8 +513,8 @@ public class NsdService extends INsdManager.Stub { if (getAddrInfo(id2, cooked[3])) { storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE); } else { - clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR, clientId); + clientInfo.onResolveServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); clientInfo.mResolvedService = null; } break; @@ -521,26 +523,26 @@ public class NsdService extends INsdManager.Stub { stopResolveService(id); removeRequestMap(clientId, id, clientInfo); clientInfo.mResolvedService = null; - clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR, clientId); + clientInfo.onResolveServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); break; case NativeResponseCode.SERVICE_GET_ADDR_FAILED: /* NNN resolveId errorCode */ stopGetAddrInfo(id); removeRequestMap(clientId, id, clientInfo); clientInfo.mResolvedService = null; - clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR, clientId); + clientInfo.onResolveServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); break; case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS: /* NNN resolveId hostname ttl addr */ try { clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4])); - clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_SUCCEEDED, - 0, clientId, clientInfo.mResolvedService); + clientInfo.onResolveServiceSucceeded( + clientId, clientInfo.mResolvedService); } catch (java.net.UnknownHostException e) { - clientInfo.mChannel.sendMessage(NsdManager.RESOLVE_SERVICE_FAILED, - NsdManager.FAILURE_INTERNAL_ERROR, clientId); + clientInfo.onResolveServiceFailed( + clientId, NsdManager.FAILURE_INTERNAL_ERROR); } stopGetAddrInfo(id); removeRequestMap(clientId, id, clientInfo); @@ -601,15 +603,71 @@ public class NsdService extends INsdManager.Stub { return service; } - public Messenger getMessenger() { + @Override + public INsdServiceConnector connect(INsdManagerCallback cb) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, "NsdService"); - return new Messenger(mNsdStateMachine.getHandler()); + final INsdServiceConnector connector = new NsdServiceConnector(); + mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( + NsdManager.REGISTER_CLIENT, new Pair<>(connector, cb))); + return connector; + } + + private static class ListenerArgs { + public final NsdServiceConnector connector; + public final NsdServiceInfo serviceInfo; + ListenerArgs(NsdServiceConnector connector, NsdServiceInfo serviceInfo) { + this.connector = connector; + this.serviceInfo = serviceInfo; + } } - public void setEnabled(boolean isEnabled) { - NetworkStack.checkNetworkStackPermission(mContext); - mNsdSettings.putEnabledStatus(isEnabled); - notifyEnabled(isEnabled); + private class NsdServiceConnector extends INsdServiceConnector.Stub + implements IBinder.DeathRecipient { + @Override + public void registerService(int listenerKey, NsdServiceInfo serviceInfo) { + mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( + NsdManager.REGISTER_SERVICE, 0, listenerKey, + new ListenerArgs(this, serviceInfo))); + } + + @Override + public void unregisterService(int listenerKey) { + mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( + NsdManager.UNREGISTER_SERVICE, 0, listenerKey, + new ListenerArgs(this, null))); + } + + @Override + public void discoverServices(int listenerKey, NsdServiceInfo serviceInfo) { + mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( + NsdManager.DISCOVER_SERVICES, 0, listenerKey, + new ListenerArgs(this, serviceInfo))); + } + + @Override + public void stopDiscovery(int listenerKey) { + mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( + NsdManager.STOP_DISCOVERY, 0, listenerKey, new ListenerArgs(this, null))); + } + + @Override + public void resolveService(int listenerKey, NsdServiceInfo serviceInfo) { + mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( + NsdManager.RESOLVE_SERVICE, 0, listenerKey, + new ListenerArgs(this, serviceInfo))); + } + + @Override + public void startDaemon() { + mNsdStateMachine.sendMessage(mNsdStateMachine.obtainMessage( + NsdManager.DAEMON_STARTUP, new ListenerArgs(this, null))); + } + + @Override + public void binderDied() { + mNsdStateMachine.sendMessage( + mNsdStateMachine.obtainMessage(NsdManager.UNREGISTER_CLIENT, this)); + } } private void notifyEnabled(boolean isEnabled) { @@ -832,43 +890,11 @@ public class NsdService extends INsdManager.Stub { mNsdStateMachine.dump(fd, pw, args); } - /* arg2 on the source message has an id that needs to be retained in replies - * see NsdManager for details */ - private Message obtainMessage(Message srcMsg) { - Message msg = Message.obtain(); - msg.arg2 = srcMsg.arg2; - return msg; - } - - private void replyToMessage(Message msg, int what) { - if (msg.replyTo == null) return; - Message dstMsg = obtainMessage(msg); - dstMsg.what = what; - mReplyChannel.replyToMessage(msg, dstMsg); - } - - private void replyToMessage(Message msg, int what, int arg1) { - if (msg.replyTo == null) return; - Message dstMsg = obtainMessage(msg); - dstMsg.what = what; - dstMsg.arg1 = arg1; - mReplyChannel.replyToMessage(msg, dstMsg); - } - - private void replyToMessage(Message msg, int what, Object obj) { - if (msg.replyTo == null) return; - Message dstMsg = obtainMessage(msg); - dstMsg.what = what; - dstMsg.obj = obj; - mReplyChannel.replyToMessage(msg, dstMsg); - } - /* Information tracked per client */ private class ClientInfo { private static final int MAX_LIMIT = 10; - private final AsyncChannel mChannel; - private final Messenger mMessenger; + private final INsdManagerCallback mCb; /* Remembers a resolved service until getaddrinfo completes */ private NsdServiceInfo mResolvedService; @@ -881,17 +907,14 @@ public class NsdService extends INsdManager.Stub { // The target SDK of this client < Build.VERSION_CODES.S private boolean mIsLegacy = false; - private ClientInfo(AsyncChannel c, Messenger m) { - mChannel = c; - mMessenger = m; - if (DBG) Slog.d(TAG, "New client, channel: " + c + " messenger: " + m); + private ClientInfo(INsdManagerCallback cb) { + mCb = cb; + if (DBG) Slog.d(TAG, "New client"); } @Override public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("mChannel ").append(mChannel).append("\n"); - sb.append("mMessenger ").append(mMessenger).append("\n"); sb.append("mResolvedService ").append(mResolvedService).append("\n"); sb.append("mIsLegacy ").append(mIsLegacy).append("\n"); for(int i = 0; i< mClientIds.size(); i++) { @@ -949,6 +972,102 @@ public class NsdService extends INsdManager.Stub { } return mClientIds.keyAt(idx); } + + void onDiscoverServicesStarted(int listenerKey, NsdServiceInfo info) { + try { + mCb.onDiscoverServicesStarted(listenerKey, info); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onDiscoverServicesStarted", e); + } + } + + void onDiscoverServicesFailed(int listenerKey, int error) { + try { + mCb.onDiscoverServicesFailed(listenerKey, error); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onDiscoverServicesFailed", e); + } + } + + void onServiceFound(int listenerKey, NsdServiceInfo info) { + try { + mCb.onServiceFound(listenerKey, info); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onServiceFound(", e); + } + } + + void onServiceLost(int listenerKey, NsdServiceInfo info) { + try { + mCb.onServiceLost(listenerKey, info); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onServiceLost(", e); + } + } + + void onStopDiscoveryFailed(int listenerKey, int error) { + try { + mCb.onStopDiscoveryFailed(listenerKey, error); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onStopDiscoveryFailed", e); + } + } + + void onStopDiscoverySucceeded(int listenerKey) { + try { + mCb.onStopDiscoverySucceeded(listenerKey); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onStopDiscoverySucceeded", e); + } + } + + void onRegisterServiceFailed(int listenerKey, int error) { + try { + mCb.onRegisterServiceFailed(listenerKey, error); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onRegisterServiceFailed", e); + } + } + + void onRegisterServiceSucceeded(int listenerKey, NsdServiceInfo info) { + try { + mCb.onRegisterServiceSucceeded(listenerKey, info); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onRegisterServiceSucceeded", e); + } + } + + void onUnregisterServiceFailed(int listenerKey, int error) { + try { + mCb.onUnregisterServiceFailed(listenerKey, error); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onUnregisterServiceFailed", e); + } + } + + void onUnregisterServiceSucceeded(int listenerKey) { + try { + mCb.onUnregisterServiceSucceeded(listenerKey); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onUnregisterServiceSucceeded", e); + } + } + + void onResolveServiceFailed(int listenerKey, int error) { + try { + mCb.onResolveServiceFailed(listenerKey, error); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onResolveServiceFailed", e); + } + } + + void onResolveServiceSucceeded(int listenerKey, NsdServiceInfo info) { + try { + mCb.onResolveServiceSucceeded(listenerKey, info); + } catch (RemoteException e) { + Log.e(TAG, "Error calling onResolveServiceSucceeded", e); + } + } } /** diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 69c29269b7a9..59bbb0d90076 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -3560,24 +3560,24 @@ class StorageManagerService extends IStorageManager.Stub } @Override - public ParcelFileDescriptor open() throws NativeDaemonConnectorException { + public ParcelFileDescriptor open() throws AppFuseMountException { try { final FileDescriptor fd = mVold.mountAppFuse(uid, mountId); mMounted = true; return new ParcelFileDescriptor(fd); } catch (Exception e) { - throw new NativeDaemonConnectorException("Failed to mount", e); + throw new AppFuseMountException("Failed to mount", e); } } @Override public ParcelFileDescriptor openFile(int mountId, int fileId, int flags) - throws NativeDaemonConnectorException { + throws AppFuseMountException { try { return new ParcelFileDescriptor( mVold.openAppFuseFile(uid, mountId, fileId, flags)); } catch (Exception e) { - throw new NativeDaemonConnectorException("Failed to open", e); + throw new AppFuseMountException("Failed to open", e); } } @@ -3617,7 +3617,7 @@ class StorageManagerService extends IStorageManager.Stub // It seems the thread of mAppFuseBridge has already been terminated. mAppFuseBridge = null; } - } catch (NativeDaemonConnectorException e) { + } catch (AppFuseMountException e) { throw e.rethrowAsParcelableException(); } } diff --git a/services/core/java/com/android/server/app/OWNERS b/services/core/java/com/android/server/app/OWNERS new file mode 100644 index 000000000000..aaebbfa8e253 --- /dev/null +++ b/services/core/java/com/android/server/app/OWNERS @@ -0,0 +1 @@ +per-file GameManager* = file:/GAME_MANAGER_OWNERS diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index c383f5120407..0b2311bc563a 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -17,12 +17,9 @@ package com.android.server.audio; import android.annotation.NonNull; import android.annotation.Nullable; -import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; -import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothProfile; -import android.bluetooth.BluetoothLeAudio; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -32,6 +29,7 @@ import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.media.AudioRoutesInfo; import android.media.AudioSystem; +import android.media.BtProfileConnectionInfo; import android.media.IAudioRoutesObserver; import android.media.ICapturePresetDevicesRoleDispatcher; import android.media.ICommunicationDeviceDispatcher; @@ -516,29 +514,82 @@ import java.util.concurrent.atomic.AtomicBoolean; } }; - /*package*/ static final class BtDeviceConnectionInfo { + /*package*/ static final class BtDeviceChangedData { + final @Nullable BluetoothDevice mNewDevice; + final @Nullable BluetoothDevice mPreviousDevice; + final @NonNull BtProfileConnectionInfo mInfo; + final @NonNull String mEventSource; + + BtDeviceChangedData(@Nullable BluetoothDevice newDevice, + @Nullable BluetoothDevice previousDevice, + @NonNull BtProfileConnectionInfo info, @NonNull String eventSource) { + mNewDevice = newDevice; + mPreviousDevice = previousDevice; + mInfo = info; + mEventSource = eventSource; + } + + @Override + public String toString() { + return "BtDeviceChangedData profile=" + BluetoothProfile.getProfileName( + mInfo.getProfile()) + + ", switch device: [" + mPreviousDevice + "] -> [" + mNewDevice + "]"; + } + } + + /*package*/ static final class BtDeviceInfo { final @NonNull BluetoothDevice mDevice; final @AudioService.BtProfileConnectionState int mState; - final int mProfile; + final @AudioService.BtProfile int mProfile; final boolean mSupprNoisy; final int mVolume; + final boolean mIsLeOutput; + final @NonNull String mEventSource; + final @AudioSystem.AudioFormatNativeEnumForBtCodec int mCodec; + final int mAudioSystemDevice; + final int mMusicDevice; - BtDeviceConnectionInfo(@NonNull BluetoothDevice device, - @AudioService.BtProfileConnectionState int state, - int profile, boolean suppressNoisyIntent, int vol) { + BtDeviceInfo(@NonNull BtDeviceChangedData d, @NonNull BluetoothDevice device, int state, + int audioDevice, @AudioSystem.AudioFormatNativeEnumForBtCodec int codec) { mDevice = device; mState = state; + mProfile = d.mInfo.getProfile(); + mSupprNoisy = d.mInfo.getSuppressNoisyIntent(); + mVolume = d.mInfo.getVolume(); + mIsLeOutput = d.mInfo.getIsLeOutput(); + mEventSource = d.mEventSource; + mAudioSystemDevice = audioDevice; + mMusicDevice = AudioSystem.DEVICE_NONE; + mCodec = codec; + } + + // constructor used by AudioDeviceBroker to search similar message + BtDeviceInfo(@NonNull BluetoothDevice device, int profile) { + mDevice = device; mProfile = profile; - mSupprNoisy = suppressNoisyIntent; - mVolume = vol; - } - - BtDeviceConnectionInfo(@NonNull BtDeviceConnectionInfo info) { - mDevice = info.mDevice; - mState = info.mState; - mProfile = info.mProfile; - mSupprNoisy = info.mSupprNoisy; - mVolume = info.mVolume; + mEventSource = ""; + mMusicDevice = AudioSystem.DEVICE_NONE; + mCodec = AudioSystem.AUDIO_FORMAT_DEFAULT; + mAudioSystemDevice = 0; + mState = 0; + mSupprNoisy = false; + mVolume = -1; + mIsLeOutput = false; + } + + // constructor used by AudioDeviceInventory when config change failed + BtDeviceInfo(@NonNull BluetoothDevice device, int profile, int state, int musicDevice, + int audioSystemDevice) { + mDevice = device; + mProfile = profile; + mEventSource = ""; + mMusicDevice = musicDevice; + mCodec = AudioSystem.AUDIO_FORMAT_DEFAULT; + mAudioSystemDevice = audioSystemDevice; + mState = state; + mSupprNoisy = false; + mVolume = -1; + mIsLeOutput = false; } // redefine equality op so we can match messages intended for this device @@ -550,16 +601,52 @@ import java.util.concurrent.atomic.AtomicBoolean; if (this == o) { return true; } - if (o instanceof BtDeviceConnectionInfo) { - return mDevice.equals(((BtDeviceConnectionInfo) o).mDevice); + if (o instanceof BtDeviceInfo) { + return mProfile == ((BtDeviceInfo) o).mProfile + && mDevice.equals(((BtDeviceInfo) o).mDevice); } return false; } + } - @Override - public String toString() { - return "BtDeviceConnectionInfo dev=" + mDevice.toString(); + BtDeviceInfo createBtDeviceInfo(@NonNull BtDeviceChangedData d, @NonNull BluetoothDevice device, + int state) { + int audioDevice; + int codec = AudioSystem.AUDIO_FORMAT_DEFAULT; + switch (d.mInfo.getProfile()) { + case BluetoothProfile.A2DP_SINK: + audioDevice = AudioSystem.DEVICE_IN_BLUETOOTH_A2DP; + break; + case BluetoothProfile.A2DP: + audioDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP; + synchronized (mDeviceStateLock) { + codec = mBtHelper.getA2dpCodec(device); + } + break; + case BluetoothProfile.HEARING_AID: + audioDevice = AudioSystem.DEVICE_OUT_HEARING_AID; + break; + case BluetoothProfile.LE_AUDIO: + if (d.mInfo.getIsLeOutput()) { + audioDevice = AudioSystem.DEVICE_OUT_BLE_HEADSET; + } else { + audioDevice = AudioSystem.DEVICE_IN_BLE_HEADSET; + } + break; + default: throw new IllegalArgumentException("Invalid profile " + d.mInfo.getProfile()); } + return new BtDeviceInfo(d, device, state, audioDevice, codec); + } + + private void btMediaMetricRecord(@NonNull BluetoothDevice device, String state, + @NonNull BtDeviceChangedData data) { + final String name = TextUtils.emptyIfNull(device.getName()); + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR + + "queueOnBluetoothActiveDeviceChanged") + .set(MediaMetrics.Property.STATE, state) + .set(MediaMetrics.Property.STATUS, data.mInfo.getProfile()) + .set(MediaMetrics.Property.NAME, name) + .record(); } /** @@ -567,116 +654,37 @@ import java.util.concurrent.atomic.AtomicBoolean; * not just a simple message post * @param info struct with the (dis)connection information */ - /*package*/ void queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - @NonNull BtDeviceConnectionInfo info) { - final String name = TextUtils.emptyIfNull(info.mDevice.getName()); - new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR - + "postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent") - .set(MediaMetrics.Property.STATE, info.mState == BluetoothProfile.STATE_CONNECTED - ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) - .set(MediaMetrics.Property.INDEX, info.mVolume) - .set(MediaMetrics.Property.NAME, name) - .record(); - - // operations of removing and posting messages related to A2DP device state change must be - // mutually exclusive - synchronized (mDeviceStateLock) { - // when receiving a request to change the connection state of a device, this last - // request is the source of truth, so cancel all previous requests that are already in - // the handler - removeScheduledA2dpEvents(info.mDevice); - - sendLMsgNoDelay( - info.mState == BluetoothProfile.STATE_CONNECTED - ? MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION - : MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION, - SENDMSG_QUEUE, info); - } - } - - /** remove all previously scheduled connection and state change events for the given device */ - @GuardedBy("mDeviceStateLock") - private void removeScheduledA2dpEvents(@NonNull BluetoothDevice device) { - mBrokerHandler.removeEqualMessages(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, device); - - final BtDeviceConnectionInfo connectionInfoToRemove = new BtDeviceConnectionInfo(device, - // the next parameters of the constructor will be ignored when finding the message - // to remove as the equality of the message's object is tested on the device itself - // (see BtDeviceConnectionInfo.equals() method override) - BluetoothProfile.STATE_CONNECTED, 0, false, -1); - mBrokerHandler.removeEqualMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION, - connectionInfoToRemove); - mBrokerHandler.removeEqualMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION, - connectionInfoToRemove); - - final BtHelper.BluetoothA2dpDeviceInfo devInfoToRemove = - new BtHelper.BluetoothA2dpDeviceInfo(device); - mBrokerHandler.removeEqualMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED, - devInfoToRemove); - mBrokerHandler.removeEqualMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED, - devInfoToRemove); - mBrokerHandler.removeEqualMessages(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, - devInfoToRemove); - } - - private static final class HearingAidDeviceConnectionInfo { - final @NonNull BluetoothDevice mDevice; - final @AudioService.BtProfileConnectionState int mState; - final boolean mSupprNoisy; - final int mMusicDevice; - final @NonNull String mEventSource; - - HearingAidDeviceConnectionInfo(@NonNull BluetoothDevice device, - @AudioService.BtProfileConnectionState int state, - boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) { - mDevice = device; - mState = state; - mSupprNoisy = suppressNoisyIntent; - mMusicDevice = musicDevice; - mEventSource = eventSource; - } - } - - /*package*/ void postBluetoothHearingAidDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, - boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) { - final HearingAidDeviceConnectionInfo info = new HearingAidDeviceConnectionInfo( - device, state, suppressNoisyIntent, musicDevice, eventSource); - sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info); - } - - private static final class LeAudioDeviceConnectionInfo { - final @NonNull BluetoothDevice mDevice; - final @AudioService.BtProfileConnectionState int mState; - final boolean mSupprNoisy; - final @NonNull String mEventSource; - - LeAudioDeviceConnectionInfo(@NonNull BluetoothDevice device, - @AudioService.BtProfileConnectionState int state, - boolean suppressNoisyIntent, @NonNull String eventSource) { - mDevice = device; - mState = state; - mSupprNoisy = suppressNoisyIntent; - mEventSource = eventSource; + /*package*/ void queueOnBluetoothActiveDeviceChanged(@NonNull BtDeviceChangedData data) { + if (data.mInfo.getProfile() == BluetoothProfile.A2DP && data.mPreviousDevice != null + && data.mPreviousDevice.equals(data.mNewDevice)) { + final String name = TextUtils.emptyIfNull(data.mNewDevice.getName()); + new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR + + "queueOnBluetoothActiveDeviceChanged_update") + .set(MediaMetrics.Property.NAME, name) + .set(MediaMetrics.Property.STATUS, data.mInfo.getProfile()) + .record(); + synchronized (mDeviceStateLock) { + postBluetoothA2dpDeviceConfigChange(data.mNewDevice); + } + } else { + synchronized (mDeviceStateLock) { + if (data.mPreviousDevice != null) { + btMediaMetricRecord(data.mPreviousDevice, MediaMetrics.Value.DISCONNECTED, + data); + sendLMsgNoDelay(MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT, SENDMSG_QUEUE, + createBtDeviceInfo(data, data.mPreviousDevice, + BluetoothProfile.STATE_DISCONNECTED)); + } + if (data.mNewDevice != null) { + btMediaMetricRecord(data.mNewDevice, MediaMetrics.Value.CONNECTED, data); + sendLMsgNoDelay(MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT, SENDMSG_QUEUE, + createBtDeviceInfo(data, data.mNewDevice, + BluetoothProfile.STATE_CONNECTED)); + } + } } } - /*package*/ void postBluetoothLeAudioOutDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, - boolean suppressNoisyIntent, @NonNull String eventSource) { - final LeAudioDeviceConnectionInfo info = new LeAudioDeviceConnectionInfo( - device, state, suppressNoisyIntent, eventSource); - sendLMsgNoDelay(MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info); - } - - /*package*/ void postBluetoothLeAudioInDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, - @NonNull String eventSource) { - final LeAudioDeviceConnectionInfo info = new LeAudioDeviceConnectionInfo( - device, state, false, eventSource); - sendLMsgNoDelay(MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info); - } - /** * Current Bluetooth SCO audio active state indicated by BtHelper via setBluetoothScoOn(). */ @@ -926,19 +934,8 @@ import java.util.concurrent.atomic.AtomicBoolean; } @GuardedBy("mDeviceStateLock") - /*package*/ void postA2dpSinkConnection(@AudioService.BtProfileConnectionState int state, - @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) { - sendILMsg(state == BluetoothA2dp.STATE_CONNECTED - ? MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED - : MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED, - SENDMSG_QUEUE, - state, btDeviceInfo, delay); - } - - /*package*/ void postA2dpSourceConnection(@AudioService.BtProfileConnectionState int state, - @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) { - sendILMsg(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, - state, btDeviceInfo, delay); + /*package*/ void postBluetoothActiveDevice(BtDeviceInfo info, int delay) { + sendLMsg(MSG_L_SET_BT_ACTIVE_DEVICE, SENDMSG_QUEUE, info, delay); } /*package*/ void postSetWiredDeviceConnectionState( @@ -946,72 +943,12 @@ import java.util.concurrent.atomic.AtomicBoolean; sendLMsg(MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE, SENDMSG_QUEUE, connectionState, delay); } - /*package*/ void postSetHearingAidConnectionState( - @AudioService.BtProfileConnectionState int state, - @NonNull BluetoothDevice device, int delay) { - sendILMsg(MSG_IL_SET_HEARING_AID_CONNECTION_STATE, SENDMSG_QUEUE, - state, - device, - delay); - } - - /*package*/ void postSetLeAudioOutConnectionState( - @AudioService.BtProfileConnectionState int state, - @NonNull BluetoothDevice device, int delay) { - sendILMsg(MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE, SENDMSG_QUEUE, - state, - device, - delay); - } - - /*package*/ void postSetLeAudioInConnectionState( - @AudioService.BtProfileConnectionState int state, - @NonNull BluetoothDevice device) { - sendILMsgNoDelay(MSG_IL_SET_LE_AUDIO_IN_CONNECTION_STATE, SENDMSG_QUEUE, - state, - device); - } - - /*package*/ void postDisconnectA2dp() { - sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE); - } - - /*package*/ void postDisconnectA2dpSink() { - sendMsgNoDelay(MSG_DISCONNECT_A2DP_SINK, SENDMSG_QUEUE); - } - - /*package*/ void postDisconnectHearingAid() { - sendMsgNoDelay(MSG_DISCONNECT_BT_HEARING_AID, SENDMSG_QUEUE); - } - - /*package*/ void postDisconnectLeAudio() { - sendMsgNoDelay(MSG_DISCONNECT_BT_LE_AUDIO, SENDMSG_QUEUE); - } - - /*package*/ void postDisconnectHeadset() { - sendMsgNoDelay(MSG_DISCONNECT_BT_HEADSET, SENDMSG_QUEUE); - } - - /*package*/ void postBtA2dpProfileConnected(BluetoothA2dp a2dpProfile) { - sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP, SENDMSG_QUEUE, a2dpProfile); - } - - /*package*/ void postBtA2dpSinkProfileConnected(BluetoothProfile profile) { - sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK, SENDMSG_QUEUE, profile); - } - - /*package*/ void postBtHeasetProfileConnected(BluetoothHeadset headsetProfile) { - sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET, SENDMSG_QUEUE, headsetProfile); - } - - /*package*/ void postBtHearingAidProfileConnected(BluetoothHearingAid hearingAidProfile) { - sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID, SENDMSG_QUEUE, - hearingAidProfile); + /*package*/ void postBtProfileDisconnected(int profile) { + sendIMsgNoDelay(MSG_I_BT_SERVICE_DISCONNECTED_PROFILE, SENDMSG_QUEUE, profile); } - /*package*/ void postBtLeAudioProfileConnected(BluetoothLeAudio leAudioProfile) { - sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_LE_AUDIO, SENDMSG_QUEUE, - leAudioProfile); + /*package*/ void postBtProfileConnected(int profile, BluetoothProfile proxy) { + sendILMsgNoDelay(MSG_IL_BT_SERVICE_CONNECTED_PROFILE, SENDMSG_QUEUE, profile, proxy); } /*package*/ void postCommunicationRouteClientDied(CommunicationRouteClient client) { @@ -1069,13 +1006,6 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - /*package*/ void postSetA2dpSourceConnectionState(@BluetoothProfile.BtProfileState int state, - @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) { - final int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0; - sendILMsgNoDelay(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, state, - btDeviceInfo); - } - /*package*/ void handleFailureToConnectToBtHeadsetService(int delay) { sendMsg(MSG_BT_HEADSET_CNCT_FAILED, SENDMSG_REPLACE, delay); } @@ -1088,19 +1018,10 @@ import java.util.concurrent.atomic.AtomicBoolean; sendMsgNoDelay(fromA2dp ? MSG_REPORT_NEW_ROUTES_A2DP : MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP); } - /*package*/ void postA2dpActiveDeviceChange( - @NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) { - sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo); - } - // must be called synchronized on mConnectedDevices - /*package*/ boolean hasScheduledA2dpSinkConnectionState(BluetoothDevice btDevice) { - final BtHelper.BluetoothA2dpDeviceInfo devInfoToCheck = - new BtHelper.BluetoothA2dpDeviceInfo(btDevice); - return (mBrokerHandler.hasEqualMessages( - MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED, devInfoToCheck) - || mBrokerHandler.hasEqualMessages( - MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED, devInfoToCheck)); + /*package*/ boolean hasScheduledA2dpConnection(BluetoothDevice btDevice) { + final BtDeviceInfo devInfoToCheck = new BtDeviceInfo(btDevice, BluetoothProfile.A2DP); + return mBrokerHandler.hasEqualMessages(MSG_L_SET_BT_ACTIVE_DEVICE, devInfoToCheck); } /*package*/ void setA2dpTimeout(String address, int a2dpCodec, int delayMs) { @@ -1124,12 +1045,6 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - /*package*/ int getA2dpCodec(@NonNull BluetoothDevice device) { - synchronized (mDeviceStateLock) { - return mBtHelper.getA2dpCodec(device); - } - } - /*package*/ void broadcastStickyIntentToCurrentProfileGroup(Intent intent) { mSystemServer.broadcastStickyIntentToCurrentProfileGroup(intent); } @@ -1285,39 +1200,11 @@ import java.util.concurrent.atomic.AtomicBoolean; mDeviceInventory.onReportNewRoutes(); } break; - case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED: - case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED: - synchronized (mDeviceStateLock) { - mDeviceInventory.onSetA2dpSinkConnectionState( - (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1); - } - break; - case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE: - synchronized (mDeviceStateLock) { - mDeviceInventory.onSetA2dpSourceConnectionState( - (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1); - } - break; - case MSG_IL_SET_HEARING_AID_CONNECTION_STATE: + case MSG_L_SET_BT_ACTIVE_DEVICE: synchronized (mDeviceStateLock) { - mDeviceInventory.onSetHearingAidConnectionState( - (BluetoothDevice) msg.obj, msg.arg1, + mDeviceInventory.onSetBtActiveDevice((BtDeviceInfo) msg.obj, mAudioService.getBluetoothContextualVolumeStream()); } - break; - case MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE: - synchronized (mDeviceStateLock) { - mDeviceInventory.onSetLeAudioOutConnectionState( - (BluetoothDevice) msg.obj, msg.arg1, - mAudioService.getBluetoothContextualVolumeStream()); - } - break; - case MSG_IL_SET_LE_AUDIO_IN_CONNECTION_STATE: - synchronized (mDeviceStateLock) { - mDeviceInventory.onSetLeAudioInConnectionState( - (BluetoothDevice) msg.obj, msg.arg1); - } - break; case MSG_BT_HEADSET_CNCT_FAILED: synchronized (mSetModeLock) { synchronized (mDeviceStateLock) { @@ -1332,14 +1219,10 @@ import java.util.concurrent.atomic.AtomicBoolean; } break; case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: - final int a2dpCodec; final BluetoothDevice btDevice = (BluetoothDevice) msg.obj; synchronized (mDeviceStateLock) { - a2dpCodec = mBtHelper.getA2dpCodec(btDevice); - // TODO: name of method being called on AudioDeviceInventory is currently - // misleading (config change vs active device change), to be - // reconciliated once the BT side has been updated. - mDeviceInventory.onBluetoothA2dpActiveDeviceChange( + final int a2dpCodec = mBtHelper.getA2dpCodec(btDevice); + mDeviceInventory.onBluetoothA2dpDeviceConfigChange( new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec), BtHelper.EVENT_DEVICE_CONFIG_CHANGE); } @@ -1392,96 +1275,47 @@ import java.util.concurrent.atomic.AtomicBoolean; mDeviceInventory.onToggleHdmi(); } break; - case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE: - synchronized (mDeviceStateLock) { - mDeviceInventory.onBluetoothA2dpActiveDeviceChange( - (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, - BtHelper.EVENT_ACTIVE_DEVICE_CHANGE); - } - break; - case MSG_DISCONNECT_A2DP: - synchronized (mDeviceStateLock) { - mDeviceInventory.disconnectA2dp(); - } - break; - case MSG_DISCONNECT_A2DP_SINK: - synchronized (mDeviceStateLock) { - mDeviceInventory.disconnectA2dpSink(); - } - break; - case MSG_DISCONNECT_BT_HEARING_AID: - synchronized (mDeviceStateLock) { - mDeviceInventory.disconnectHearingAid(); - } - break; - case MSG_DISCONNECT_BT_HEADSET: - synchronized (mSetModeLock) { + case MSG_I_BT_SERVICE_DISCONNECTED_PROFILE: + if (msg.arg1 != BluetoothProfile.HEADSET) { synchronized (mDeviceStateLock) { - mBtHelper.disconnectHeadset(); + mDeviceInventory.onBtProfileDisconnected(msg.arg1); + } + } else { + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.disconnectHeadset(); + } } } break; - case MSG_DISCONNECT_BT_LE_AUDIO: - synchronized(mDeviceStateLock) { - mDeviceInventory.disconnectLeAudio(); - } - break; - case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP: - synchronized (mDeviceStateLock) { - mBtHelper.onA2dpProfileConnected((BluetoothA2dp) msg.obj); - } - break; - case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK: - synchronized (mDeviceStateLock) { - mBtHelper.onA2dpSinkProfileConnected((BluetoothProfile) msg.obj); - } - break; - case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID: - synchronized (mDeviceStateLock) { - mBtHelper.onHearingAidProfileConnected((BluetoothHearingAid) msg.obj); - } - break; - - case MSG_L_BT_SERVICE_CONNECTED_PROFILE_LE_AUDIO: - synchronized(mDeviceStateLock) { - mBtHelper.onLeAudioProfileConnected((BluetoothLeAudio) msg.obj); - } - break; - case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET: - synchronized (mSetModeLock) { + case MSG_IL_BT_SERVICE_CONNECTED_PROFILE: + if (msg.arg1 != BluetoothProfile.HEADSET) { synchronized (mDeviceStateLock) { - mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj); + mBtHelper.onBtProfileConnected(msg.arg1, (BluetoothProfile) msg.obj); + } + } else { + synchronized (mSetModeLock) { + synchronized (mDeviceStateLock) { + mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj); + } } } break; - case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION: - case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION: { - final BtDeviceConnectionInfo info = (BtDeviceConnectionInfo) msg.obj; + case MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT: { + final BtDeviceInfo info = (BtDeviceInfo) msg.obj; + if (info.mDevice == null) break; AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( - "msg: setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent " + "msg: onBluetoothActiveDeviceChange " + " state=" + info.mState // only querying address as this is the only readily available // field on the device + " addr=" + info.mDevice.getAddress() - + " prof=" + info.mProfile + " supprNoisy=" + info.mSupprNoisy - + " vol=" + info.mVolume)).printLog(TAG)); - synchronized (mDeviceStateLock) { - mDeviceInventory.setBluetoothA2dpDeviceConnectionState( - info.mDevice, info.mState, info.mProfile, info.mSupprNoisy, - AudioSystem.DEVICE_NONE, info.mVolume); - } - } break; - case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: { - final HearingAidDeviceConnectionInfo info = - (HearingAidDeviceConnectionInfo) msg.obj; - AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( - "msg: setHearingAidDeviceConnectionState state=" + info.mState - + " addr=" + info.mDevice.getAddress() + + " prof=" + info.mProfile + " supprNoisy=" + info.mSupprNoisy - + " src=" + info.mEventSource)).printLog(TAG)); + + " src=" + info.mEventSource + )).printLog(TAG)); synchronized (mDeviceStateLock) { - mDeviceInventory.setBluetoothHearingAidDeviceConnectionState( - info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice); + mDeviceInventory.setBluetoothActiveDevice(info); } } break; case MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY: { @@ -1524,31 +1358,6 @@ import java.util.concurrent.atomic.AtomicBoolean; final int capturePreset = msg.arg1; mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset); } break; - case MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT: { - final LeAudioDeviceConnectionInfo info = - (LeAudioDeviceConnectionInfo) msg.obj; - AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( - "setLeAudioDeviceOutConnectionState state=" + info.mState - + " addr=" + info.mDevice.getAddress() - + " supprNoisy=" + info.mSupprNoisy - + " src=" + info.mEventSource)).printLog(TAG)); - synchronized (mDeviceStateLock) { - mDeviceInventory.setBluetoothLeAudioOutDeviceConnectionState( - info.mDevice, info.mState, info.mSupprNoisy); - } - } break; - case MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT: { - final LeAudioDeviceConnectionInfo info = - (LeAudioDeviceConnectionInfo) msg.obj; - AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( - "setLeAudioDeviceInConnectionState state=" + info.mState - + " addr=" + info.mDevice.getAddress() - + " src=" + info.mEventSource)).printLog(TAG)); - synchronized (mDeviceStateLock) { - mDeviceInventory.setBluetoothLeAudioInDeviceConnectionState(info.mDevice, - info.mState); - } - } break; default: Log.wtf(TAG, "Invalid message " + msg.what); } @@ -1579,8 +1388,7 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final int MSG_IIL_SET_FORCE_USE = 4; private static final int MSG_IIL_SET_FORCE_BT_A2DP_USE = 5; private static final int MSG_TOGGLE_HDMI = 6; - private static final int MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE = 7; - private static final int MSG_IL_SET_HEARING_AID_CONNECTION_STATE = 8; + private static final int MSG_L_SET_BT_ACTIVE_DEVICE = 7; private static final int MSG_BT_HEADSET_CNCT_FAILED = 9; private static final int MSG_IL_BTA2DP_TIMEOUT = 10; @@ -1593,25 +1401,11 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final int MSG_I_SET_AVRCP_ABSOLUTE_VOLUME = 15; private static final int MSG_I_SET_MODE_OWNER_PID = 16; - // process active A2DP device change, obj is BtHelper.BluetoothA2dpDeviceInfo - private static final int MSG_L_A2DP_ACTIVE_DEVICE_CHANGE = 18; - - private static final int MSG_DISCONNECT_A2DP = 19; - private static final int MSG_DISCONNECT_A2DP_SINK = 20; - private static final int MSG_DISCONNECT_BT_HEARING_AID = 21; - private static final int MSG_DISCONNECT_BT_HEADSET = 22; - private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP = 23; - private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK = 24; - private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID = 25; - private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET = 26; - - // process change of state, obj is BtHelper.BluetoothA2dpDeviceInfo - private static final int MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED = 27; - private static final int MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED = 28; + private static final int MSG_I_BT_SERVICE_DISCONNECTED_PROFILE = 22; + private static final int MSG_IL_BT_SERVICE_CONNECTED_PROFILE = 23; // process external command to (dis)connect an A2DP device, obj is BtDeviceConnectionInfo - private static final int MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION = 29; - private static final int MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION = 30; + private static final int MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT = 29; // process external command to (dis)connect a hearing aid device private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 31; @@ -1630,33 +1424,21 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final int MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY = 40; private static final int MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY = 41; - private static final int MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE = 42; - private static final int MSG_IL_SET_LE_AUDIO_IN_CONNECTION_STATE = 43; - private static final int MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT = 44; - private static final int MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT = 45; + private static final int MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT = 45; + // // process set volume for Le Audio, obj is BleVolumeInfo private static final int MSG_II_SET_LE_AUDIO_OUT_VOLUME = 46; - private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_LE_AUDIO = 47; - private static final int MSG_DISCONNECT_BT_LE_AUDIO = 48; - private static boolean isMessageHandledUnderWakelock(int msgId) { switch(msgId) { case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE: - case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED: - case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED: - case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE: - case MSG_IL_SET_HEARING_AID_CONNECTION_STATE: + case MSG_L_SET_BT_ACTIVE_DEVICE: case MSG_IL_BTA2DP_TIMEOUT: case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: case MSG_TOGGLE_HDMI: - case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE: - case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION: - case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION: + case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT: case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: case MSG_CHECK_MUTE_MUSIC: - case MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT: - case MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT: return true; default: return false; @@ -1739,14 +1521,10 @@ import java.util.concurrent.atomic.AtomicBoolean; long time = SystemClock.uptimeMillis() + delay; switch (msg) { - case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE: - case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED: - case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED: - case MSG_IL_SET_HEARING_AID_CONNECTION_STATE: + case MSG_L_SET_BT_ACTIVE_DEVICE: case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE: case MSG_IL_BTA2DP_TIMEOUT: case MSG_L_A2DP_DEVICE_CONFIG_CHANGE: - case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE: if (sLastDeviceConnectMsgTime >= time) { // add a little delay to make sure messages are ordered as expected time = sLastDeviceConnectMsgTime + 30; @@ -1765,14 +1543,9 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final Set<Integer> MESSAGES_MUTE_MUSIC; static { MESSAGES_MUTE_MUSIC = new HashSet<>(); - MESSAGES_MUTE_MUSIC.add(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED); - MESSAGES_MUTE_MUSIC.add(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED); - MESSAGES_MUTE_MUSIC.add(MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE); + MESSAGES_MUTE_MUSIC.add(MSG_L_SET_BT_ACTIVE_DEVICE); MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONFIG_CHANGE); - MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE); - MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION); - MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION); - MESSAGES_MUTE_MUSIC.add(MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT); + MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT); MESSAGES_MUTE_MUSIC.add(MSG_IIL_SET_FORCE_BT_A2DP_USE); MESSAGES_MUTE_MUSIC.add(MSG_REPORT_NEW_ROUTES_A2DP); } diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 6c3c736aeb93..0a114b924063 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -16,11 +16,8 @@ package com.android.server.audio; import android.annotation.NonNull; -import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHearingAid; -import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothProfile; import android.content.Intent; import android.media.AudioDeviceAttributes; @@ -286,186 +283,102 @@ public class AudioDeviceInventory { } } - // only public for mocking/spying @GuardedBy("AudioDeviceBroker.mDeviceStateLock") - @VisibleForTesting - public void onSetA2dpSinkConnectionState(@NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, - @AudioService.BtProfileConnectionState int state) { - final BluetoothDevice btDevice = btInfo.getBtDevice(); - int a2dpVolume = btInfo.getVolume(); - if (AudioService.DEBUG_DEVICES) { - Log.d(TAG, "onSetA2dpSinkConnectionState btDevice=" + btDevice + " state=" - + state + " vol=" + a2dpVolume); - } - String address = btDevice.getAddress(); - if (address == null) { - address = ""; - } - if (!BluetoothAdapter.checkBluetoothAddress(address)) { - address = ""; - } - - final @AudioSystem.AudioFormatNativeEnumForBtCodec int a2dpCodec = btInfo.getCodec(); - - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "A2DP sink connected: device addr=" + address + " state=" + state - + " codec=" + AudioSystem.audioFormatToString(a2dpCodec) - + " vol=" + a2dpVolume)); - - new MediaMetrics.Item(mMetricsId + "a2dp") - .set(MediaMetrics.Property.ADDRESS, address) - .set(MediaMetrics.Property.ENCODING, AudioSystem.audioFormatToString(a2dpCodec)) - .set(MediaMetrics.Property.EVENT, "onSetA2dpSinkConnectionState") - .set(MediaMetrics.Property.INDEX, a2dpVolume) - .set(MediaMetrics.Property.STATE, - state == BluetoothProfile.STATE_CONNECTED - ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) - .record(); - - synchronized (mDevicesLock) { - final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - btDevice.getAddress()); - final DeviceInfo di = mConnectedDevices.get(key); - boolean isConnected = di != null; - - if (isConnected) { - if (state == BluetoothProfile.STATE_CONNECTED) { - // device is already connected, but we are receiving a connection again, - // it could be for a codec change - if (a2dpCodec != di.mDeviceCodecFormat) { - mDeviceBroker.postBluetoothA2dpDeviceConfigChange(btDevice); - } - } else { - makeA2dpDeviceUnavailableNow(address, di.mDeviceCodecFormat); - } - } else if (state == BluetoothProfile.STATE_CONNECTED) { - // device is not already connected - if (a2dpVolume != -1) { - mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC, - // convert index to internal representation in VolumeStreamState - a2dpVolume * 10, - AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "onSetA2dpSinkConnectionState"); - } - makeA2dpDeviceAvailable(address, BtHelper.getName(btDevice), - "onSetA2dpSinkConnectionState", a2dpCodec); - } - } - } - - /*package*/ void onSetA2dpSourceConnectionState( - @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int state) { - final BluetoothDevice btDevice = btInfo.getBtDevice(); + void onSetBtActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo btInfo, int streamType) { if (AudioService.DEBUG_DEVICES) { - Log.d(TAG, "onSetA2dpSourceConnectionState btDevice=" + btDevice + " state=" - + state); + Log.d(TAG, "onSetBtActiveDevice" + + " btDevice=" + btInfo.mDevice + + " profile=" + BluetoothProfile.getProfileName(btInfo.mProfile) + + " state=" + BluetoothProfile.getConnectionStateName(btInfo.mState)); } - String address = btDevice.getAddress(); + String address = btInfo.mDevice.getAddress(); if (!BluetoothAdapter.checkBluetoothAddress(address)) { address = ""; } - synchronized (mDevicesLock) { - final String key = DeviceInfo.makeDeviceListKey( - AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address); - final DeviceInfo di = mConnectedDevices.get(key); - boolean isConnected = di != null; - - new MediaMetrics.Item(mMetricsId + "onSetA2dpSourceConnectionState") - .set(MediaMetrics.Property.ADDRESS, address) - .set(MediaMetrics.Property.DEVICE, - AudioSystem.getDeviceName(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP)) - .set(MediaMetrics.Property.STATE, - state == BluetoothProfile.STATE_CONNECTED - ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) - .record(); - - if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { - makeA2dpSrcUnavailable(address); - } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { - makeA2dpSrcAvailable(address); - } - } - } - - /*package*/ void onSetHearingAidConnectionState(BluetoothDevice btDevice, - @AudioService.BtProfileConnectionState int state, int streamType) { - String address = btDevice.getAddress(); - if (!BluetoothAdapter.checkBluetoothAddress(address)) { - address = ""; - } - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "onSetHearingAidConnectionState addr=" + address)); + AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent("BT connected:" + + " addr=" + address + + " profile=" + btInfo.mProfile + + " state=" + btInfo.mState + + " codec=" + AudioSystem.audioFormatToString(btInfo.mCodec))); - new MediaMetrics.Item(mMetricsId + "onSetHearingAidConnectionState") - .set(MediaMetrics.Property.ADDRESS, address) + new MediaMetrics.Item(mMetricsId + "onSetBtActiveDevice") + .set(MediaMetrics.Property.STATUS, btInfo.mProfile) .set(MediaMetrics.Property.DEVICE, - AudioSystem.getDeviceName(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP)) - .set(MediaMetrics.Property.STATE, - state == BluetoothProfile.STATE_CONNECTED - ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) + AudioSystem.getDeviceName(btInfo.mAudioSystemDevice)) + .set(MediaMetrics.Property.ADDRESS, address) + .set(MediaMetrics.Property.ENCODING, + AudioSystem.audioFormatToString(btInfo.mCodec)) + .set(MediaMetrics.Property.EVENT, "onSetBtActiveDevice") .set(MediaMetrics.Property.STREAM_TYPE, AudioSystem.streamToString(streamType)) + .set(MediaMetrics.Property.STATE, + btInfo.mState == BluetoothProfile.STATE_CONNECTED + ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) .record(); synchronized (mDevicesLock) { - final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, - btDevice.getAddress()); + final String key = DeviceInfo.makeDeviceListKey(btInfo.mAudioSystemDevice, address); final DeviceInfo di = mConnectedDevices.get(key); - boolean isConnected = di != null; - if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { - makeHearingAidDeviceUnavailable(address); - } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { - makeHearingAidDeviceAvailable(address, BtHelper.getName(btDevice), streamType, - "onSetHearingAidConnectionState"); - } - } - } + final boolean isConnected = di != null; - /*package*/ void onSetLeAudioConnectionState(BluetoothDevice btDevice, - @AudioService.BtProfileConnectionState int state, int streamType, int device) { - String address = btDevice.getAddress(); - if (!BluetoothAdapter.checkBluetoothAddress(address)) { - address = ""; - } - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "onSetLeAudioConnectionState addr=" + address)); + final boolean switchToUnavailable = isConnected + && btInfo.mState != BluetoothProfile.STATE_CONNECTED; + final boolean switchToAvailable = !isConnected + && btInfo.mState == BluetoothProfile.STATE_CONNECTED; - synchronized (mDevicesLock) { - DeviceInfo di = null; - boolean isConnected = false; - - String key = DeviceInfo.makeDeviceListKey(device, btDevice.getAddress()); - di = mConnectedDevices.get(key); - isConnected = di != null; - - if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { - makeLeAudioDeviceUnavailable(address, device); - } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { - makeLeAudioDeviceAvailable(address, BtHelper.getName(btDevice), streamType, - device, "onSetLeAudioConnectionState"); + switch (btInfo.mProfile) { + case BluetoothProfile.A2DP_SINK: + if (switchToUnavailable) { + makeA2dpSrcUnavailable(address); + } else if (switchToAvailable) { + makeA2dpSrcAvailable(address); + } + break; + case BluetoothProfile.A2DP: + if (switchToUnavailable) { + makeA2dpDeviceUnavailableNow(address, di.mDeviceCodecFormat); + } else if (switchToAvailable) { + // device is not already connected + if (btInfo.mVolume != -1) { + mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC, + // convert index to internal representation in VolumeStreamState + btInfo.mVolume * 10, btInfo.mAudioSystemDevice, + "onSetBtActiveDevice"); + } + makeA2dpDeviceAvailable(address, BtHelper.getName(btInfo.mDevice), + "onSetBtActiveDevice", btInfo.mCodec); + } + break; + case BluetoothProfile.HEARING_AID: + if (switchToUnavailable) { + makeHearingAidDeviceUnavailable(address); + } else if (switchToAvailable) { + makeHearingAidDeviceAvailable(address, BtHelper.getName(btInfo.mDevice), + streamType, "onSetBtActiveDevice"); + } + break; + case BluetoothProfile.LE_AUDIO: + if (switchToUnavailable) { + makeLeAudioDeviceUnavailable(address, btInfo.mAudioSystemDevice); + } else if (switchToAvailable) { + makeLeAudioDeviceAvailable(address, BtHelper.getName(btInfo.mDevice), + streamType, btInfo.mAudioSystemDevice, "onSetBtActiveDevice"); + } + break; + default: throw new IllegalArgumentException("Invalid profile " + + BluetoothProfile.getProfileName(btInfo.mProfile)); } } } - /*package*/ void onSetLeAudioOutConnectionState(BluetoothDevice btDevice, - @AudioService.BtProfileConnectionState int state, int streamType) { - // TODO: b/198610537 clarify DEVICE_OUT_BLE_HEADSET vs DEVICE_OUT_BLE_SPEAKER criteria - onSetLeAudioConnectionState(btDevice, state, streamType, - AudioSystem.DEVICE_OUT_BLE_HEADSET); - } - - /*package*/ void onSetLeAudioInConnectionState(BluetoothDevice btDevice, - @AudioService.BtProfileConnectionState int state) { - onSetLeAudioConnectionState(btDevice, state, AudioSystem.STREAM_DEFAULT, - AudioSystem.DEVICE_IN_BLE_HEADSET); - } @GuardedBy("AudioDeviceBroker.mDeviceStateLock") - /*package*/ void onBluetoothA2dpActiveDeviceChange( + /*package*/ void onBluetoothA2dpDeviceConfigChange( @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) { MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId - + "onBluetoothA2dpActiveDeviceChange") + + "onBluetoothA2dpDeviceConfigChange") .set(MediaMetrics.Property.EVENT, BtHelper.a2dpDeviceEventToString(event)); final BluetoothDevice btDevice = btInfo.getBtDevice(); @@ -474,7 +387,7 @@ public class AudioDeviceInventory { return; } if (AudioService.DEBUG_DEVICES) { - Log.d(TAG, "onBluetoothA2dpActiveDeviceChange btDevice=" + btDevice); + Log.d(TAG, "onBluetoothA2dpDeviceConfigChange btDevice=" + btDevice); } int a2dpVolume = btInfo.getVolume(); @AudioSystem.AudioFormatNativeEnumForBtCodec final int a2dpCodec = btInfo.getCodec(); @@ -484,11 +397,11 @@ public class AudioDeviceInventory { address = ""; } AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "onBluetoothA2dpActiveDeviceChange addr=" + address + "onBluetoothA2dpDeviceConfigChange addr=" + address + " event=" + BtHelper.a2dpDeviceEventToString(event))); synchronized (mDevicesLock) { - if (mDeviceBroker.hasScheduledA2dpSinkConnectionState(btDevice)) { + if (mDeviceBroker.hasScheduledA2dpConnection(btDevice)) { AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( "A2dp config change ignored (scheduled connection change)") .printLog(TAG)); @@ -500,7 +413,7 @@ public class AudioDeviceInventory { AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address); final DeviceInfo di = mConnectedDevices.get(key); if (di == null) { - Log.e(TAG, "invalid null DeviceInfo in onBluetoothA2dpActiveDeviceChange"); + Log.e(TAG, "invalid null DeviceInfo in onBluetoothA2dpDeviceConfigChange"); mmi.set(MediaMetrics.Property.EARLY_RETURN, "null DeviceInfo").record(); return; } @@ -518,7 +431,7 @@ public class AudioDeviceInventory { // convert index to internal representation in VolumeStreamState a2dpVolume * 10, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - "onBluetoothA2dpActiveDeviceChange"); + "onBluetoothA2dpDeviceConfigChange"); } } else if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) { if (di.mDeviceCodecFormat != a2dpCodec) { @@ -539,10 +452,9 @@ public class AudioDeviceInventory { int musicDevice = mDeviceBroker.getDeviceForStream(AudioSystem.STREAM_MUSIC); // force A2DP device disconnection in case of error so that AudioService state is // consistent with audio policy manager state - setBluetoothA2dpDeviceConnectionState( - btDevice, BluetoothA2dp.STATE_DISCONNECTED, BluetoothProfile.A2DP, - false /* suppressNoisyIntent */, musicDevice, - -1 /* a2dpVolume */); + setBluetoothActiveDevice(new AudioDeviceBroker.BtDeviceInfo(btDevice, + BluetoothProfile.A2DP, BluetoothProfile.STATE_DISCONNECTED, + musicDevice, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)); } else { AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( "APM handleDeviceConfigChange success for A2DP device addr=" + address @@ -828,7 +740,7 @@ public class AudioDeviceInventory { } - /*package*/ void disconnectA2dp() { + private void disconnectA2dp() { synchronized (mDevicesLock) { final ArraySet<String> toRemove = new ArraySet<>(); // Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices @@ -850,7 +762,7 @@ public class AudioDeviceInventory { } } - /*package*/ void disconnectA2dpSink() { + private void disconnectA2dpSink() { synchronized (mDevicesLock) { final ArraySet<String> toRemove = new ArraySet<>(); // Disconnect ALL DEVICE_IN_BLUETOOTH_A2DP devices @@ -865,7 +777,7 @@ public class AudioDeviceInventory { } } - /*package*/ void disconnectHearingAid() { + private void disconnectHearingAid() { synchronized (mDevicesLock) { final ArraySet<String> toRemove = new ArraySet<>(); // Disconnect ALL DEVICE_OUT_HEARING_AID devices @@ -887,6 +799,28 @@ public class AudioDeviceInventory { } } + /*package*/ synchronized void onBtProfileDisconnected(int profile) { + switch (profile) { + case BluetoothProfile.A2DP: + disconnectA2dp(); + break; + case BluetoothProfile.A2DP_SINK: + disconnectA2dpSink(); + break; + case BluetoothProfile.HEARING_AID: + disconnectHearingAid(); + break; + case BluetoothProfile.LE_AUDIO: + disconnectLeAudio(); + break; + default: + // Not a valid profile to disconnect + Log.e(TAG, "onBtProfileDisconnected: Not a valid profile to disconnect " + + BluetoothProfile.getProfileName(profile)); + break; + } + } + /*package*/ void disconnectLeAudio() { synchronized (mDevicesLock) { final ArraySet<String> toRemove = new ArraySet<>(); @@ -934,46 +868,39 @@ public class AudioDeviceInventory { // only public for mocking/spying @GuardedBy("AudioDeviceBroker.mDeviceStateLock") @VisibleForTesting - public void setBluetoothA2dpDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, - int profile, boolean suppressNoisyIntent, int musicDevice, int a2dpVolume) { + public int setBluetoothActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo info) { int delay; - if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) { - throw new IllegalArgumentException("invalid profile " + profile); - } synchronized (mDevicesLock) { - if (profile == BluetoothProfile.A2DP && !suppressNoisyIntent) { + if (!info.mSupprNoisy + && ((info.mProfile == BluetoothProfile.LE_AUDIO && info.mIsLeOutput) + || info.mProfile == BluetoothProfile.HEARING_AID + || info.mProfile == BluetoothProfile.A2DP)) { @AudioService.ConnectionState int asState = - (state == BluetoothA2dp.STATE_CONNECTED) + (info.mState == BluetoothProfile.STATE_CONNECTED) ? AudioService.CONNECTION_STATE_CONNECTED : AudioService.CONNECTION_STATE_DISCONNECTED; - delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, - asState, musicDevice); + delay = checkSendBecomingNoisyIntentInt(info.mAudioSystemDevice, asState, + info.mMusicDevice); } else { delay = 0; } - final int a2dpCodec = mDeviceBroker.getA2dpCodec(device); - if (AudioService.DEBUG_DEVICES) { - Log.i(TAG, "setBluetoothA2dpDeviceConnectionState device: " + device - + " state: " + state + " delay(ms): " + delay - + " codec:" + Integer.toHexString(a2dpCodec) - + " suppressNoisyIntent: " + suppressNoisyIntent); + Log.i(TAG, "setBluetoothActiveDevice device: " + info.mDevice + + " profile: " + BluetoothProfile.getProfileName(info.mProfile) + + " state: " + BluetoothProfile.getConnectionStateName(info.mState) + + " delay(ms): " + delay + + " codec:" + Integer.toHexString(info.mCodec) + + " suppressNoisyIntent: " + info.mSupprNoisy); } - - final BtHelper.BluetoothA2dpDeviceInfo a2dpDeviceInfo = - new BtHelper.BluetoothA2dpDeviceInfo(device, a2dpVolume, a2dpCodec); - if (profile == BluetoothProfile.A2DP) { - mDeviceBroker.postA2dpSinkConnection(state, - a2dpDeviceInfo, - delay); - } else { //profile == BluetoothProfile.A2DP_SINK - mDeviceBroker.postA2dpSourceConnection(state, - a2dpDeviceInfo, - delay); + mDeviceBroker.postBluetoothActiveDevice(info, delay); + if (info.mProfile == BluetoothProfile.HEARING_AID + && info.mState == BluetoothProfile.STATE_CONNECTED) { + mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE, + "HEARING_AID set to CONNECTED"); } } + return delay; } /*package*/ int setWiredDeviceConnectionState(int type, @AudioService.ConnectionState int state, @@ -987,50 +914,6 @@ public class AudioDeviceInventory { } } - /*package*/ int setBluetoothHearingAidDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, - boolean suppressNoisyIntent, int musicDevice) { - int delay; - synchronized (mDevicesLock) { - if (!suppressNoisyIntent) { - int intState = (state == BluetoothHearingAid.STATE_CONNECTED) ? 1 : 0; - delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_HEARING_AID, - intState, musicDevice); - } else { - delay = 0; - } - mDeviceBroker.postSetHearingAidConnectionState(state, device, delay); - if (state == BluetoothHearingAid.STATE_CONNECTED) { - mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE, - "HEARING_AID set to CONNECTED"); - } - return delay; - } - } - - /*package*/ int setBluetoothLeAudioOutDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, - boolean suppressNoisyIntent) { - synchronized (mDevicesLock) { - /* Active device become null and it's previous device is not connected anymore */ - int delay = 0; - if (!suppressNoisyIntent) { - int intState = (state == BluetoothLeAudio.STATE_CONNECTED) ? 1 : 0; - delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_BLE_HEADSET, - intState, AudioSystem.DEVICE_NONE); - } - mDeviceBroker.postSetLeAudioOutConnectionState(state, device, delay); - return delay; - } - } - - /*package*/ void setBluetoothLeAudioInDeviceConnectionState( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state) { - synchronized (mDevicesLock) { - mDeviceBroker.postSetLeAudioInConnectionState(state, device); - } - } - //------------------------------------------------------------------- // Internal utilities diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index d75f21c4ef85..51784bbce2e0 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -83,6 +83,7 @@ import android.media.AudioPlaybackConfiguration; import android.media.AudioRecordingConfiguration; import android.media.AudioRoutesInfo; import android.media.AudioSystem; +import android.media.BtProfileConnectionInfo; import android.media.IAudioFocusDispatcher; import android.media.IAudioModeDispatcher; import android.media.IAudioRoutesObserver; @@ -309,8 +310,8 @@ public class AudioService extends IAudioService.Stub private static final int MSG_UPDATE_A11Y_SERVICE_UIDS = 35; private static final int MSG_UPDATE_AUDIO_MODE = 36; private static final int MSG_RECORDING_CONFIG_CHANGE = 37; - private static final int MSG_SET_A2DP_DEV_CONNECTION_STATE = 38; - private static final int MSG_A2DP_DEV_CONFIG_CHANGE = 39; + private static final int MSG_BT_DEV_CHANGED = 38; + private static final int MSG_DISPATCH_AUDIO_MODE = 40; // start of messages handled under wakelock @@ -6266,77 +6267,44 @@ public class AudioService extends IAudioService.Stub public @interface BtProfileConnectionState {} /** - * See AudioManager.setBluetoothHearingAidDeviceConnectionState() - */ - public void setBluetoothHearingAidDeviceConnectionState( - @NonNull BluetoothDevice device, @BtProfileConnectionState int state, - boolean suppressNoisyIntent, int musicDevice) - { - if (device == null) { - throw new IllegalArgumentException("Illegal null device"); - } - if (state != BluetoothProfile.STATE_CONNECTED - && state != BluetoothProfile.STATE_DISCONNECTED) { - throw new IllegalArgumentException("Illegal BluetoothProfile state for device " - + " (dis)connection, got " + state); - } - mDeviceBroker.postBluetoothHearingAidDeviceConnectionState( - device, state, suppressNoisyIntent, musicDevice, "AudioService"); - } - - private void setBluetoothLeAudioDeviceConnectionState(@NonNull BluetoothDevice device, - @BtProfileConnectionState int state) { - if (device == null) { - throw new IllegalArgumentException("Illegal null device"); - } - if (state != BluetoothProfile.STATE_CONNECTED - && state != BluetoothProfile.STATE_DISCONNECTED) { - throw new IllegalArgumentException("Illegal BluetoothProfile state for device " - + " (dis)connection, got " + state); - } - } - - /** - * See AudioManager.setBluetoothLeAudioOutDeviceConnectionState() + * @hide + * The profiles that can be used with AudioService.handleBluetoothActiveDeviceChanged() */ - public void setBluetoothLeAudioOutDeviceConnectionState( - @NonNull BluetoothDevice device, @BtProfileConnectionState int state, - boolean suppressNoisyIntent) { - setBluetoothLeAudioDeviceConnectionState(device, state); - mDeviceBroker.postBluetoothLeAudioOutDeviceConnectionState(device, state, - suppressNoisyIntent, "AudioService"); - } + @IntDef({ + BluetoothProfile.HEARING_AID, + BluetoothProfile.A2DP, + BluetoothProfile.A2DP_SINK, + BluetoothProfile.LE_AUDIO, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BtProfile {} - /** - * See AudioManager.setBluetoothLeAudioInDeviceConnectionState() - */ - public void setBluetoothLeAudioInDeviceConnectionState( - @NonNull BluetoothDevice device, @BtProfileConnectionState int state) { - setBluetoothLeAudioDeviceConnectionState(device, state); - mDeviceBroker.postBluetoothLeAudioInDeviceConnectionState(device, state, "AudioService"); - } /** - * See AudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent() + * See AudioManager.handleBluetoothActiveDeviceChanged(...) */ - public void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - @NonNull BluetoothDevice device, @BtProfileConnectionState int state, - int profile, boolean suppressNoisyIntent, int a2dpVolume) { - if (device == null) { - throw new IllegalArgumentException("Illegal null device"); + public void handleBluetoothActiveDeviceChanged(BluetoothDevice newDevice, + BluetoothDevice previousDevice, @NonNull BtProfileConnectionInfo info) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BLUETOOTH_STACK) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Bluetooth is the only caller allowed"); } - if (state != BluetoothProfile.STATE_CONNECTED - && state != BluetoothProfile.STATE_DISCONNECTED) { - throw new IllegalArgumentException("Illegal BluetoothProfile state for device " - + " (dis)connection, got " + state); + if (info == null) { + throw new IllegalArgumentException("Illegal null BtProfileConnectionInfo for device " + + previousDevice + " -> " + newDevice); } - - AudioDeviceBroker.BtDeviceConnectionInfo info = - new AudioDeviceBroker.BtDeviceConnectionInfo(device, state, - profile, suppressNoisyIntent, a2dpVolume); - sendMsg(mAudioHandler, MSG_SET_A2DP_DEV_CONNECTION_STATE, SENDMSG_QUEUE, - 0 /*arg1*/, 0 /*arg2*/, - /*obj*/ info, 0 /*delay*/); + final int profile = info.getProfile(); + if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK + && profile != BluetoothProfile.LE_AUDIO + && profile != BluetoothProfile.HEARING_AID) { + throw new IllegalArgumentException("Illegal BluetoothProfile profile for device " + + previousDevice + " -> " + newDevice + ". Got: " + profile); + } + AudioDeviceBroker.BtDeviceChangedData data = + new AudioDeviceBroker.BtDeviceChangedData(newDevice, previousDevice, info, + "AudioService"); + sendMsg(mAudioHandler, MSG_BT_DEV_CHANGED, SENDMSG_QUEUE, 0, 0, + /*obj*/ data, /*delay*/ 0); } /** only public for mocking/spying, do not call outside of AudioService */ @@ -6345,19 +6313,6 @@ public class AudioService extends IAudioService.Stub mStreamStates[AudioSystem.STREAM_MUSIC].muteInternally(mute); } - /** - * See AudioManager.handleBluetoothA2dpDeviceConfigChange() - * @param device - */ - public void handleBluetoothA2dpDeviceConfigChange(BluetoothDevice device) - { - if (device == null) { - throw new IllegalArgumentException("Illegal null device"); - } - sendMsg(mAudioHandler, MSG_A2DP_DEV_CONFIG_CHANGE, SENDMSG_QUEUE, 0, 0, - /*obj*/ device, /*delay*/ 0); - } - private static final Set<Integer> DEVICE_MEDIA_UNMUTED_ON_PLUG_SET; static { DEVICE_MEDIA_UNMUTED_ON_PLUG_SET = new HashSet<>(); @@ -7709,13 +7664,9 @@ public class AudioService extends IAudioService.Stub } break; - case MSG_SET_A2DP_DEV_CONNECTION_STATE: - mDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - (AudioDeviceBroker.BtDeviceConnectionInfo) msg.obj); - break; - - case MSG_A2DP_DEV_CONFIG_CHANGE: - mDeviceBroker.postBluetoothA2dpDeviceConfigChange((BluetoothDevice) msg.obj); + case MSG_BT_DEV_CHANGED: + mDeviceBroker.queueOnBluetoothActiveDeviceChanged( + (AudioDeviceBroker.BtDeviceChangedData) msg.obj); break; case MSG_DISPATCH_AUDIO_MODE: diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index c924fde23f9d..9273a5d5cf9c 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -31,6 +31,7 @@ import android.content.Intent; import android.media.AudioDeviceAttributes; import android.media.AudioManager; import android.media.AudioSystem; +import android.media.BtProfileConnectionInfo; import android.os.Binder; import android.os.UserHandle; import android.provider.Settings; @@ -451,11 +452,11 @@ public class BtHelper { } /*package*/ synchronized void disconnectAllBluetoothProfiles() { - mDeviceBroker.postDisconnectA2dp(); - mDeviceBroker.postDisconnectA2dpSink(); - mDeviceBroker.postDisconnectHeadset(); - mDeviceBroker.postDisconnectHearingAid(); - mDeviceBroker.postDisconnectLeAudio(); + mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.A2DP); + mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.A2DP_SINK); + mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.HEADSET); + mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.HEARING_AID); + mDeviceBroker.postBtProfileDisconnected(BluetoothProfile.LE_AUDIO); } // @GuardedBy("AudioDeviceBroker.mSetModeLock") @@ -474,63 +475,32 @@ public class BtHelper { mBluetoothHeadset = null; } - /*package*/ synchronized void onA2dpProfileConnected(BluetoothA2dp a2dp) { - mA2dp = a2dp; - final List<BluetoothDevice> deviceList = mA2dp.getConnectedDevices(); - if (deviceList.isEmpty()) { + /*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) { + if (profile == BluetoothProfile.HEADSET) { + onHeadsetProfileConnected((BluetoothHeadset) proxy); return; } - final BluetoothDevice btDevice = deviceList.get(0); - // the device is guaranteed CONNECTED - mDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - new AudioDeviceBroker.BtDeviceConnectionInfo(btDevice, - BluetoothA2dp.STATE_CONNECTED, BluetoothProfile.A2DP_SINK, - true, -1)); - } - - /*package*/ synchronized void onA2dpSinkProfileConnected(BluetoothProfile profile) { - final List<BluetoothDevice> deviceList = profile.getConnectedDevices(); - if (deviceList.isEmpty()) { - return; + if (profile == BluetoothProfile.A2DP) { + mA2dp = (BluetoothA2dp) proxy; + } else if (profile == BluetoothProfile.LE_AUDIO) { + mLeAudio = (BluetoothLeAudio) proxy; } - final BluetoothDevice btDevice = deviceList.get(0); - final @BluetoothProfile.BtProfileState int state = - profile.getConnectionState(btDevice); - mDeviceBroker.postSetA2dpSourceConnectionState( - state, new BluetoothA2dpDeviceInfo(btDevice)); - } - - /*package*/ synchronized void onHearingAidProfileConnected(BluetoothHearingAid hearingAid) { - mHearingAid = hearingAid; - final List<BluetoothDevice> deviceList = mHearingAid.getConnectedDevices(); + final List<BluetoothDevice> deviceList = proxy.getConnectedDevices(); if (deviceList.isEmpty()) { return; } final BluetoothDevice btDevice = deviceList.get(0); final @BluetoothProfile.BtProfileState int state = - mHearingAid.getConnectionState(btDevice); - mDeviceBroker.postBluetoothHearingAidDeviceConnectionState( - btDevice, state, - /*suppressNoisyIntent*/ false, - /*musicDevice*/ android.media.AudioSystem.DEVICE_NONE, - /*eventSource*/ "mBluetoothProfileServiceListener"); - } - - /*package*/ synchronized void onLeAudioProfileConnected(BluetoothLeAudio leAudio) { - mLeAudio = leAudio; - final List<BluetoothDevice> deviceList = mLeAudio.getConnectedDevices(); - if (deviceList.isEmpty()) { - return; + proxy.getConnectionState(btDevice); + if (state == BluetoothProfile.STATE_CONNECTED) { + mDeviceBroker.queueOnBluetoothActiveDeviceChanged( + new AudioDeviceBroker.BtDeviceChangedData(btDevice, null, + new BtProfileConnectionInfo(profile), "mBluetoothProfileServiceListener")); + } else { + mDeviceBroker.queueOnBluetoothActiveDeviceChanged( + new AudioDeviceBroker.BtDeviceChangedData(null, btDevice, + new BtProfileConnectionInfo(profile), "mBluetoothProfileServiceListener")); } - - final BluetoothDevice btDevice = deviceList.get(0); - final @BluetoothProfile.BtProfileState int state = - mLeAudio.getConnectionState(btDevice); - mDeviceBroker.postBluetoothLeAudioOutDeviceConnectionState( - btDevice, state, - /*suppressNoisyIntent*/ false, - /*musicDevice android.media.AudioSystem.DEVICE_NONE,*/ - /*eventSource*/ "mBluetoothProfileServiceListener"); } // @GuardedBy("AudioDeviceBroker.mSetModeLock") @@ -677,36 +647,16 @@ public class BtHelper { public void onServiceConnected(int profile, BluetoothProfile proxy) { switch(profile) { case BluetoothProfile.A2DP: - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "BT profile service: connecting A2DP profile")); - mDeviceBroker.postBtA2dpProfileConnected((BluetoothA2dp) proxy); - break; - case BluetoothProfile.A2DP_SINK: - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "BT profile service: connecting A2DP_SINK profile")); - mDeviceBroker.postBtA2dpSinkProfileConnected(proxy); - break; - case BluetoothProfile.HEADSET: - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "BT profile service: connecting HEADSET profile")); - mDeviceBroker.postBtHeasetProfileConnected((BluetoothHeadset) proxy); - break; - case BluetoothProfile.HEARING_AID: - AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "BT profile service: connecting HEARING_AID profile")); - mDeviceBroker.postBtHearingAidProfileConnected( - (BluetoothHearingAid) proxy); - break; - case BluetoothProfile.LE_AUDIO: AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( - "BT profile service: connecting LE_AUDIO profile")); - mDeviceBroker.postBtLeAudioProfileConnected( - (BluetoothLeAudio) proxy); + "BT profile service: connecting " + + BluetoothProfile.getProfileName(profile) + " profile")); + mDeviceBroker.postBtProfileConnected(profile, proxy); break; + default: break; } @@ -715,22 +665,11 @@ public class BtHelper { switch (profile) { case BluetoothProfile.A2DP: - mDeviceBroker.postDisconnectA2dp(); - break; - case BluetoothProfile.A2DP_SINK: - mDeviceBroker.postDisconnectA2dpSink(); - break; - case BluetoothProfile.HEADSET: - mDeviceBroker.postDisconnectHeadset(); - break; - case BluetoothProfile.HEARING_AID: - mDeviceBroker.postDisconnectHearingAid(); - break; case BluetoothProfile.LE_AUDIO: - mDeviceBroker.postDisconnectLeAudio(); + mDeviceBroker.postBtProfileDisconnected(profile); break; default: diff --git a/services/core/java/com/android/server/connectivity/OWNERS b/services/core/java/com/android/server/connectivity/OWNERS index 7311eee32a4c..62c5737a2e8e 100644 --- a/services/core/java/com/android/server/connectivity/OWNERS +++ b/services/core/java/com/android/server/connectivity/OWNERS @@ -1,8 +1,2 @@ set noparent - -codewiz@google.com -ek@google.com -jchalard@google.com -lorenzo@google.com -reminv@google.com -satk@google.com +file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking diff --git a/services/core/java/com/android/server/health/HealthRegCallbackAidl.java b/services/core/java/com/android/server/health/HealthRegCallbackAidl.java new file mode 100644 index 000000000000..629011a86bb4 --- /dev/null +++ b/services/core/java/com/android/server/health/HealthRegCallbackAidl.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2021 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.health; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.health.HealthInfo; +import android.hardware.health.IHealth; +import android.hardware.health.IHealthInfoCallback; +import android.os.RemoteException; +import android.os.Trace; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; + +/** + * On service registration, {@link #onRegistration} is called, which registers {@code this}, an + * {@link IHealthInfoCallback}, to the health service. + * + * <p>When the health service has updates to health info via {@link IHealthInfoCallback}, {@link + * HealthInfoCallback#update} is called. + * + * <p>AIDL variant of {@link HealthHalCallbackHidl}. + * + * @hide + */ +// It is made public so Mockito can access this class. It should have been package private if not +// for testing. +@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) +public class HealthRegCallbackAidl { + private static final String TAG = "HealthRegCallbackAidl"; + private final HealthInfoCallback mServiceInfoCallback; + private final IHealthInfoCallback mHalInfoCallback = new HalInfoCallback(); + + HealthRegCallbackAidl(@Nullable HealthInfoCallback healthInfoCallback) { + mServiceInfoCallback = healthInfoCallback; + } + + /** + * Called when the service manager sees {@code newService} replacing {@code oldService}. + * This unregisters the health info callback from the old service (ignoring errors), then + * registers the health info callback to the new service. + * + * @param oldService the old IHealth service + * @param newService the new IHealth service + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public void onRegistration(@Nullable IHealth oldService, @NonNull IHealth newService) { + if (mServiceInfoCallback == null) return; + + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "HealthUnregisterCallbackAidl"); + try { + unregisterCallback(oldService, mHalInfoCallback); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "HealthRegisterCallbackAidl"); + try { + registerCallback(newService, mHalInfoCallback); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + } + + private static void unregisterCallback(@Nullable IHealth oldService, IHealthInfoCallback cb) { + if (oldService == null) return; + try { + oldService.unregisterCallback(cb); + } catch (RemoteException e) { + // Ignore errors. The service might have died. + Slog.w( + TAG, + "health: cannot unregister previous callback (transaction error): " + + e.getMessage()); + } + } + + private static void registerCallback(@NonNull IHealth newService, IHealthInfoCallback cb) { + try { + newService.registerCallback(cb); + } catch (RemoteException e) { + Slog.e( + TAG, + "health: cannot register callback, framework may cease to" + + " receive updates on health / battery info!", + e); + return; + } + // registerCallback does NOT guarantee that update is called immediately, so request a + // manual update here. + try { + newService.update(); + } catch (RemoteException e) { + Slog.e(TAG, "health: cannot update after registering health info callback", e); + } + } + + private class HalInfoCallback extends IHealthInfoCallback.Stub { + @Override + public void healthInfoChanged(HealthInfo healthInfo) throws RemoteException { + mServiceInfoCallback.update(healthInfo); + } + } +} diff --git a/services/core/java/com/android/server/health/HealthServiceWrapper.java b/services/core/java/com/android/server/health/HealthServiceWrapper.java index 9b97554ee259..25d1a885bc18 100644 --- a/services/core/java/com/android/server/health/HealthServiceWrapper.java +++ b/services/core/java/com/android/server/health/HealthServiceWrapper.java @@ -81,6 +81,8 @@ public abstract class HealthServiceWrapper { public static HealthServiceWrapper create(@Nullable HealthInfoCallback healthInfoCallback) throws RemoteException, NoSuchElementException { return create( + healthInfoCallback == null ? null : new HealthRegCallbackAidl(healthInfoCallback), + new HealthServiceWrapperAidl.ServiceManagerStub() {}, healthInfoCallback == null ? null : new HealthHalCallbackHidl(healthInfoCallback), new HealthServiceWrapperHidl.IServiceManagerSupplier() {}, new HealthServiceWrapperHidl.IHealthSupplier() {}); @@ -89,6 +91,9 @@ public abstract class HealthServiceWrapper { /** * Create a new HealthServiceWrapper instance for testing. * + * @param aidlRegCallback callback for AIDL service registration, or {@code null} if the client + * does not care about AIDL service registration notifications + * @param aidlServiceManager Stub for AIDL ServiceManager * @param hidlRegCallback callback for HIDL service registration, or {@code null} if the client * does not care about HIDL service registration notifications * @param hidlServiceManagerSupplier supplier of HIDL service manager @@ -97,10 +102,17 @@ public abstract class HealthServiceWrapper { */ @VisibleForTesting static @NonNull HealthServiceWrapper create( + @Nullable HealthRegCallbackAidl aidlRegCallback, + @NonNull HealthServiceWrapperAidl.ServiceManagerStub aidlServiceManager, @Nullable HealthServiceWrapperHidl.Callback hidlRegCallback, @NonNull HealthServiceWrapperHidl.IServiceManagerSupplier hidlServiceManagerSupplier, @NonNull HealthServiceWrapperHidl.IHealthSupplier hidlHealthSupplier) throws RemoteException, NoSuchElementException { + try { + return new HealthServiceWrapperAidl(aidlRegCallback, aidlServiceManager); + } catch (NoSuchElementException e) { + // Ignore, try HIDL + } return new HealthServiceWrapperHidl( hidlRegCallback, hidlServiceManagerSupplier, hidlHealthSupplier); } diff --git a/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java new file mode 100644 index 000000000000..4f2ed68974fa --- /dev/null +++ b/services/core/java/com/android/server/health/HealthServiceWrapperAidl.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2021 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.health; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.health.HealthInfo; +import android.hardware.health.IHealth; +import android.os.BatteryManager; +import android.os.BatteryProperty; +import android.os.Binder; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.IServiceCallback; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.ServiceSpecificException; +import android.os.Trace; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Implement {@link HealthServiceWrapper} backed by the AIDL HAL. + * + * @hide + */ +class HealthServiceWrapperAidl extends HealthServiceWrapper { + private static final String TAG = "HealthServiceWrapperAidl"; + @VisibleForTesting static final String SERVICE_NAME = IHealth.DESCRIPTOR + "/default"; + private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceBinder"); + private final AtomicReference<IHealth> mLastService = new AtomicReference<>(); + private final IServiceCallback mServiceCallback = new ServiceCallback(); + private final HealthRegCallbackAidl mRegCallback; + + /** Stub interface into {@link ServiceManager} for testing. */ + interface ServiceManagerStub { + default @Nullable IHealth waitForDeclaredService(@NonNull String name) { + return IHealth.Stub.asInterface(ServiceManager.waitForDeclaredService(name)); + } + + default void registerForNotifications( + @NonNull String name, @NonNull IServiceCallback callback) throws RemoteException { + ServiceManager.registerForNotifications(name, callback); + } + } + + HealthServiceWrapperAidl( + @Nullable HealthRegCallbackAidl regCallback, @NonNull ServiceManagerStub serviceManager) + throws RemoteException, NoSuchElementException { + + traceBegin("HealthInitGetServiceAidl"); + IHealth newService; + try { + newService = serviceManager.waitForDeclaredService(SERVICE_NAME); + } finally { + traceEnd(); + } + if (newService == null) { + throw new NoSuchElementException( + "IHealth service instance isn't available. Perhaps no permission?"); + } + mLastService.set(newService); + mRegCallback = regCallback; + if (mRegCallback != null) { + mRegCallback.onRegistration(null /* oldService */, newService); + } + + traceBegin("HealthInitRegisterNotificationAidl"); + mHandlerThread.start(); + try { + serviceManager.registerForNotifications(SERVICE_NAME, mServiceCallback); + } finally { + traceEnd(); + } + Slog.i(TAG, "health: HealthServiceWrapper listening to AIDL HAL"); + } + + @Override + @VisibleForTesting + public HandlerThread getHandlerThread() { + return mHandlerThread; + } + + @Override + public int getProperty(int id, BatteryProperty prop) throws RemoteException { + traceBegin("HealthGetPropertyAidl"); + try { + return getPropertyInternal(id, prop); + } finally { + traceEnd(); + } + } + + private int getPropertyInternal(int id, BatteryProperty prop) throws RemoteException { + IHealth service = mLastService.get(); + if (service == null) throw new RemoteException("no health service"); + try { + switch (id) { + case BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER: + prop.setLong(service.getChargeCounterUah()); + break; + case BatteryManager.BATTERY_PROPERTY_CURRENT_NOW: + prop.setLong(service.getCurrentNowMicroamps()); + break; + case BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE: + prop.setLong(service.getCurrentAverageMicroamps()); + break; + case BatteryManager.BATTERY_PROPERTY_CAPACITY: + prop.setLong(service.getCapacity()); + break; + case BatteryManager.BATTERY_PROPERTY_STATUS: + prop.setLong(service.getChargeStatus()); + break; + case BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER: + prop.setLong(service.getEnergyCounterNwh()); + break; + } + } catch (UnsupportedOperationException e) { + // Leave prop untouched. + return -1; + } catch (ServiceSpecificException e) { + // Leave prop untouched. + return -2; + } + // throws RemoteException as-is. BatteryManager wraps it into a RuntimeException + // and throw it to apps. + + // If no error, return 0. + return 0; + } + + @Override + public void scheduleUpdate() throws RemoteException { + getHandlerThread() + .getThreadHandler() + .post( + () -> { + traceBegin("HealthScheduleUpdate"); + try { + IHealth service = mLastService.get(); + if (service == null) { + Slog.e(TAG, "no health service"); + return; + } + service.update(); + } catch (RemoteException | ServiceSpecificException ex) { + Slog.e(TAG, "Cannot call update on health AIDL HAL", ex); + } finally { + traceEnd(); + } + }); + } + + @Override + public HealthInfo getHealthInfo() throws RemoteException { + IHealth service = mLastService.get(); + if (service == null) return null; + try { + return service.getHealthInfo(); + } catch (UnsupportedOperationException | ServiceSpecificException ex) { + return null; + } + } + + private static void traceBegin(String name) { + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, name); + } + + private static void traceEnd() { + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + + private class ServiceCallback extends IServiceCallback.Stub { + @Override + public void onRegistration(String name, @NonNull final IBinder newBinder) + throws RemoteException { + if (!SERVICE_NAME.equals(name)) return; + // This runnable only runs on mHandlerThread and ordering is ensured, hence + // no locking is needed inside the runnable. + getHandlerThread() + .getThreadHandler() + .post( + () -> { + IHealth newService = + IHealth.Stub.asInterface(Binder.allowBlocking(newBinder)); + IHealth oldService = mLastService.getAndSet(newService); + IBinder oldBinder = + oldService != null ? oldService.asBinder() : null; + if (Objects.equals(newBinder, oldBinder)) return; + + Slog.i(TAG, "New health AIDL HAL service registered"); + mRegCallback.onRegistration(oldService, newService); + }); + } + } +} diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java index 73de0f814325..ffc1aed4c672 100644 --- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java +++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java @@ -51,6 +51,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; class BluetoothRouteProvider { private static final String TAG = "BTRouteProvider"; @@ -174,8 +175,9 @@ class BluetoothRouteProvider { private void buildBluetoothRoutes() { mBluetoothRoutes.clear(); - if (mBluetoothAdapter.getBondedDevices() != null) { - for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) { + Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices(); + if (bondedDevices != null) { + for (BluetoothDevice device : bondedDevices) { if (device.isConnected()) { BluetoothRouteInfo newBtRoute = createBluetoothRoute(device); if (newBtRoute.connectedProfiles.size() > 0) { diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java index 431b00914f02..e6433db11d7b 100644 --- a/services/core/java/com/android/server/net/NetworkStatsFactory.java +++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java @@ -159,7 +159,7 @@ public class NetworkStatsFactory { } public NetworkStatsFactory() { - this(new File("/proc/"), new File("/sys/fs/bpf/map_netd_app_uid_stats_map").exists()); + this(new File("/proc/"), true); } @VisibleForTesting diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 097b0711eff7..f4b72a15d0e3 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -215,8 +215,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private final PowerManager.WakeLock mWakeLock; - private final boolean mUseBpfTrafficStats; - private final ContentObserver mContentObserver; private final ContentResolver mContentResolver; @@ -438,7 +436,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mStatsObservers = Objects.requireNonNull(statsObservers, "missing NetworkStatsObservers"); mSystemDir = Objects.requireNonNull(systemDir, "missing systemDir"); mBaseDir = Objects.requireNonNull(baseDir, "missing baseDir"); - mUseBpfTrafficStats = new File("/sys/fs/bpf/map_netd_app_uid_stats_map").exists(); mDeps = Objects.requireNonNull(deps, "missing Dependencies"); final HandlerThread handlerThread = mDeps.makeHandlerThread(); @@ -1084,13 +1081,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub { if (callingUid != android.os.Process.SYSTEM_UID && callingUid != uid) { return UNSUPPORTED; } - return nativeGetUidStat(uid, type, checkBpfStatsEnable()); + return nativeGetUidStat(uid, type); } @Override public long getIfaceStats(@NonNull String iface, int type) { Objects.requireNonNull(iface); - long nativeIfaceStats = nativeGetIfaceStat(iface, type, checkBpfStatsEnable()); + long nativeIfaceStats = nativeGetIfaceStat(iface, type); if (nativeIfaceStats == -1) { return nativeIfaceStats; } else { @@ -1104,7 +1101,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public long getTotalStats(int type) { - long nativeTotalStats = nativeGetTotalStat(type, checkBpfStatsEnable()); + long nativeTotalStats = nativeGetTotalStat(type); if (nativeTotalStats == -1) { return nativeTotalStats; } else { @@ -1137,10 +1134,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - private boolean checkBpfStatsEnable() { - return mUseBpfTrafficStats; - } - /** * Update {@link NetworkStatsRecorder} and {@link #mGlobalAlertBytes} to * reflect current {@link #mPersistThreshold} value. Always defers to @@ -2249,7 +2242,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - private static native long nativeGetTotalStat(int type, boolean useBpfStats); - private static native long nativeGetIfaceStat(String iface, int type, boolean useBpfStats); - private static native long nativeGetUidStat(int uid, int type, boolean useBpfStats); + private static native long nativeGetTotalStat(int type); + private static native long nativeGetIfaceStat(String iface, int type); + private static native long nativeGetUidStat(int uid, int type); } diff --git a/services/core/java/com/android/server/net/OWNERS b/services/core/java/com/android/server/net/OWNERS index a15fc3eef539..9c96d46f15b8 100644 --- a/services/core/java/com/android/server/net/OWNERS +++ b/services/core/java/com/android/server/net/OWNERS @@ -1,6 +1,5 @@ set noparent - -include platform/packages/modules/Connectivity:/OWNERS +file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking jsharkey@android.com sudheersai@google.com diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 1a0a885d88b4..8e18508adc90 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -3538,9 +3538,9 @@ public class PackageManagerService extends IPackageManager.Stub list.addAll(mApexManager.getFactoryPackages()); } else { list.addAll(mApexManager.getActivePackages()); - } - if (listUninstalled) { - list.addAll(mApexManager.getInactivePackages()); + if (listUninstalled) { + list.addAll(mApexManager.getInactivePackages()); + } } } return new ParceledListSlice<>(list); @@ -21917,8 +21917,10 @@ public class PackageManagerService extends IPackageManager.Stub ApexManager.ActiveApexInfo apexInfo) { for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) { ScanPartition sp = SYSTEM_PARTITIONS.get(i); - if (apexInfo.preInstalledApexPath.getAbsolutePath().startsWith( - sp.getFolder().getAbsolutePath() + File.separator)) { + if (apexInfo.preInstalledApexPath.getAbsolutePath().equals( + sp.getFolder().getAbsolutePath()) + || apexInfo.preInstalledApexPath.getAbsolutePath().startsWith( + sp.getFolder().getAbsolutePath() + File.separator)) { return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX); } } diff --git a/services/core/java/com/android/server/stats/bootstrap/StatsBootstrapAtomService.java b/services/core/java/com/android/server/stats/bootstrap/StatsBootstrapAtomService.java new file mode 100644 index 000000000000..0d420a535415 --- /dev/null +++ b/services/core/java/com/android/server/stats/bootstrap/StatsBootstrapAtomService.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2021 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.stats.bootstrap; + +import android.content.Context; +import android.os.IStatsBootstrapAtomService; +import android.os.StatsBootstrapAtom; +import android.os.StatsBootstrapAtomValue; +import android.util.Slog; +import android.util.StatsEvent; +import android.util.StatsLog; + +import com.android.server.SystemService; + +/** + * Proxy service for logging pushed atoms to statsd + * + * @hide + */ +public class StatsBootstrapAtomService extends IStatsBootstrapAtomService.Stub { + + private static final String TAG = "StatsBootstrapAtomService"; + private static final boolean DEBUG = false; + + @Override + public void reportBootstrapAtom(StatsBootstrapAtom atom) { + if (atom.atomId < 1 || atom.atomId >= 10000) { + Slog.e(TAG, "Atom ID " + atom.atomId + " is not a valid atom ID"); + return; + } + StatsEvent.Builder builder = StatsEvent.newBuilder().setAtomId(atom.atomId); + for (StatsBootstrapAtomValue value : atom.values) { + switch (value.getTag()) { + case StatsBootstrapAtomValue.boolValue: + builder.writeBoolean(value.getBoolValue()); + break; + case StatsBootstrapAtomValue.intValue: + builder.writeInt(value.getIntValue()); + break; + case StatsBootstrapAtomValue.longValue: + builder.writeLong(value.getLongValue()); + break; + case StatsBootstrapAtomValue.floatValue: + builder.writeFloat(value.getFloatValue()); + break; + case StatsBootstrapAtomValue.stringValue: + builder.writeString(value.getStringValue()); + break; + case StatsBootstrapAtomValue.bytesValue: + builder.writeByteArray(value.getBytesValue()); + break; + default: + Slog.e(TAG, "Unexpected value type " + value.getTag() + + " when logging atom " + atom.atomId); + return; + + } + } + StatsLog.write(builder.usePooledBuffer().build()); + } + + /** + * Lifecycle and related code + */ + public static final class Lifecycle extends SystemService { + private StatsBootstrapAtomService mStatsBootstrapAtomService; + + public Lifecycle(Context context) { + super(context); + } + + @Override + public void onStart() { + mStatsBootstrapAtomService = new StatsBootstrapAtomService(); + try { + publishBinderService(Context.STATS_BOOTSTRAP_ATOM_SERVICE, + mStatsBootstrapAtomService); + if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_BOOTSTRAP_ATOM_SERVICE); + } catch (Exception e) { + Slog.e(TAG, "Failed to publishBinderService", e); + } + } + } + +} diff --git a/services/core/java/com/android/server/storage/AppFuseBridge.java b/services/core/java/com/android/server/storage/AppFuseBridge.java index b00540fd2a52..292314840efb 100644 --- a/services/core/java/com/android/server/storage/AppFuseBridge.java +++ b/services/core/java/com/android/server/storage/AppFuseBridge.java @@ -24,7 +24,7 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.FuseUnavailableMountException; import com.android.internal.util.Preconditions; -import com.android.server.NativeDaemonConnectorException; +import com.android.server.AppFuseMountException; import libcore.io.IoUtils; import java.util.concurrent.CountDownLatch; @@ -55,7 +55,7 @@ public class AppFuseBridge implements Runnable { } public ParcelFileDescriptor addBridge(MountScope mountScope) - throws FuseUnavailableMountException, NativeDaemonConnectorException { + throws FuseUnavailableMountException, AppFuseMountException { /* ** Dead Lock between Java lock (AppFuseBridge.java) and Native lock (FuseBridgeLoop.cc) ** @@ -112,7 +112,7 @@ public class AppFuseBridge implements Runnable { try { int flags = FileUtils.translateModePfdToPosix(mode); return scope.openFile(mountId, fileId, flags); - } catch (NativeDaemonConnectorException error) { + } catch (AppFuseMountException error) { throw new FuseUnavailableMountException(mountId); } } @@ -160,9 +160,9 @@ public class AppFuseBridge implements Runnable { return mMountResult; } - public abstract ParcelFileDescriptor open() throws NativeDaemonConnectorException; + public abstract ParcelFileDescriptor open() throws AppFuseMountException; public abstract ParcelFileDescriptor openFile(int mountId, int fileId, int flags) - throws NativeDaemonConnectorException; + throws AppFuseMountException; } private native long native_new(); diff --git a/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java b/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java index 4eb1b99183f1..60068cb9a766 100644 --- a/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java +++ b/services/core/java/com/android/server/timezonedetector/ReferenceWithHistory.java @@ -25,6 +25,7 @@ import android.util.IndentingPrintWriter; import java.time.Duration; import java.util.ArrayDeque; +import java.util.Iterator; /** * A class that behaves like the following definition, except it stores the history of values set @@ -112,9 +113,11 @@ public final class ReferenceWithHistory<V> { if (mValues == null) { ipw.println("{Empty}"); } else { - int i = mSetCount; - for (TimestampedValue<V> valueHolder : mValues) { - ipw.print(--i); + int i = mSetCount - mValues.size(); + Iterator<TimestampedValue<V>> reverseIterator = mValues.descendingIterator(); + while (reverseIterator.hasNext()) { + TimestampedValue<V> valueHolder = reverseIterator.next(); + ipw.print(i++); ipw.print("@"); ipw.print(Duration.ofMillis(valueHolder.getReferenceTimeMillis()).toString()); ipw.print(": "); diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 28947083854b..2f54f302af6c 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -315,6 +315,7 @@ public final class TvInputManagerService extends SystemService { PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, userId); List<TvInputInfo> inputList = new ArrayList<>(); + List<ComponentName> hardwareComponents = new ArrayList<>(); for (ResolveInfo ri : services) { ServiceInfo si = ri.serviceInfo; if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) { @@ -325,6 +326,7 @@ public final class TvInputManagerService extends SystemService { ComponentName component = new ComponentName(si.packageName, si.name); if (hasHardwarePermission(pm, component)) { + hardwareComponents.add(component); ServiceState serviceState = userState.serviceStateMap.get(component); if (serviceState == null) { // New hardware input found. Create a new ServiceState and connect to the @@ -397,6 +399,15 @@ public final class TvInputManagerService extends SystemService { } } + // Clean up ServiceState corresponding to the removed hardware inputs + Iterator<ServiceState> it = userState.serviceStateMap.values().iterator(); + while (it.hasNext()) { + ServiceState serviceState = it.next(); + if (serviceState.isHardware && !hardwareComponents.contains(serviceState.component)) { + it.remove(); + } + } + userState.inputMap.clear(); userState.inputMap = inputMap; } @@ -2974,32 +2985,47 @@ public final class TvInputManagerService extends SystemService { public void addHardwareInput(int deviceId, TvInputInfo inputInfo) { ensureHardwarePermission(); ensureValidInput(inputInfo); - synchronized (mLock) { - mTvInputHardwareManager.addHardwareInput(deviceId, inputInfo); - addHardwareInputLocked(inputInfo); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + mTvInputHardwareManager.addHardwareInput(deviceId, inputInfo); + addHardwareInputLocked(inputInfo); + } + } finally { + Binder.restoreCallingIdentity(identity); } } public void addHdmiInput(int id, TvInputInfo inputInfo) { ensureHardwarePermission(); ensureValidInput(inputInfo); - synchronized (mLock) { - mTvInputHardwareManager.addHdmiInput(id, inputInfo); - addHardwareInputLocked(inputInfo); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + mTvInputHardwareManager.addHdmiInput(id, inputInfo); + addHardwareInputLocked(inputInfo); + } + } finally { + Binder.restoreCallingIdentity(identity); } } public void removeHardwareInput(String inputId) { ensureHardwarePermission(); - synchronized (mLock) { - ServiceState serviceState = getServiceStateLocked(mComponent, mUserId); - boolean removed = serviceState.hardwareInputMap.remove(inputId) != null; - if (removed) { - buildTvInputListLocked(mUserId, null); - mTvInputHardwareManager.removeHardwareInput(inputId); - } else { - Slog.e(TAG, "failed to remove input " + inputId); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + ServiceState serviceState = getServiceStateLocked(mComponent, mUserId); + boolean removed = serviceState.hardwareInputMap.remove(inputId) != null; + if (removed) { + buildTvInputListLocked(mUserId, null); + mTvInputHardwareManager.removeHardwareInput(inputId); + } else { + Slog.e(TAG, "failed to remove input " + inputId); + } } + } finally { + Binder.restoreCallingIdentity(identity); } } } diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp index 10b248a70e7e..5178132e4a2e 100644 --- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp +++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp @@ -38,9 +38,6 @@ using android::bpf::bpfGetIfaceStats; namespace android { -static const char* QTAGUID_IFACE_STATS = "/proc/net/xt_qtaguid/iface_stat_fmt"; -static const char* QTAGUID_UID_STATS = "/proc/net/xt_qtaguid/stats"; - // NOTE: keep these in sync with TrafficStats.java static const uint64_t UNKNOWN = -1; @@ -72,102 +69,17 @@ static uint64_t getStatsType(Stats* stats, StatsType type) { } } -static int parseIfaceStats(const char* iface, Stats* stats) { - FILE *fp = fopen(QTAGUID_IFACE_STATS, "r"); - if (fp == NULL) { - return -1; - } - - char buffer[384]; - char cur_iface[32]; - bool foundTcp = false; - uint64_t rxBytes, rxPackets, txBytes, txPackets, tcpRxPackets, tcpTxPackets; - - while (fgets(buffer, sizeof(buffer), fp) != NULL) { - int matched = sscanf(buffer, "%31s %" SCNu64 " %" SCNu64 " %" SCNu64 - " %" SCNu64 " " "%*u %" SCNu64 " %*u %*u %*u %*u " - "%*u %" SCNu64 " %*u %*u %*u %*u", cur_iface, &rxBytes, - &rxPackets, &txBytes, &txPackets, &tcpRxPackets, &tcpTxPackets); - if (matched >= 5) { - if (matched == 7) { - foundTcp = true; - } - if (!iface || !strcmp(iface, cur_iface)) { - stats->rxBytes += rxBytes; - stats->rxPackets += rxPackets; - stats->txBytes += txBytes; - stats->txPackets += txPackets; - if (matched == 7) { - stats->tcpRxPackets += tcpRxPackets; - stats->tcpTxPackets += tcpTxPackets; - } - } - } - } - - if (!foundTcp) { - stats->tcpRxPackets = UNKNOWN; - stats->tcpTxPackets = UNKNOWN; - } - - if (fclose(fp) != 0) { - return -1; - } - return 0; -} - -static int parseUidStats(const uint32_t uid, Stats* stats) { - FILE *fp = fopen(QTAGUID_UID_STATS, "r"); - if (fp == NULL) { - return -1; - } - - char buffer[384]; - char iface[32]; - uint32_t idx, cur_uid, set; - uint64_t tag, rxBytes, rxPackets, txBytes, txPackets; - - while (fgets(buffer, sizeof(buffer), fp) != NULL) { - if (sscanf(buffer, - "%" SCNu32 " %31s 0x%" SCNx64 " %u %u %" SCNu64 " %" SCNu64 - " %" SCNu64 " %" SCNu64 "", - &idx, iface, &tag, &cur_uid, &set, &rxBytes, &rxPackets, - &txBytes, &txPackets) == 9) { - if (uid == cur_uid && tag == 0L) { - stats->rxBytes += rxBytes; - stats->rxPackets += rxPackets; - stats->txBytes += txBytes; - stats->txPackets += txPackets; - } - } - } - - if (fclose(fp) != 0) { - return -1; - } - return 0; -} - -static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type, jboolean useBpfStats) { +static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type) { Stats stats = {}; - if (useBpfStats) { - if (bpfGetIfaceStats(NULL, &stats) == 0) { - return getStatsType(&stats, (StatsType) type); - } else { - return UNKNOWN; - } - } - - if (parseIfaceStats(NULL, &stats) == 0) { + if (bpfGetIfaceStats(NULL, &stats) == 0) { return getStatsType(&stats, (StatsType) type); } else { return UNKNOWN; } } -static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type, - jboolean useBpfStats) { +static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type) { ScopedUtfChars iface8(env, iface); if (iface8.c_str() == NULL) { return UNKNOWN; @@ -175,33 +87,17 @@ static jlong getIfaceStat(JNIEnv* env, jclass clazz, jstring iface, jint type, Stats stats = {}; - if (useBpfStats) { - if (bpfGetIfaceStats(iface8.c_str(), &stats) == 0) { - return getStatsType(&stats, (StatsType) type); - } else { - return UNKNOWN; - } - } - - if (parseIfaceStats(iface8.c_str(), &stats) == 0) { + if (bpfGetIfaceStats(iface8.c_str(), &stats) == 0) { return getStatsType(&stats, (StatsType) type); } else { return UNKNOWN; } } -static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type, jboolean useBpfStats) { +static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type) { Stats stats = {}; - if (useBpfStats) { - if (bpfGetUidStats(uid, &stats) == 0) { - return getStatsType(&stats, (StatsType) type); - } else { - return UNKNOWN; - } - } - - if (parseUidStats(uid, &stats) == 0) { + if (bpfGetUidStats(uid, &stats) == 0) { return getStatsType(&stats, (StatsType) type); } else { return UNKNOWN; @@ -209,9 +105,9 @@ static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type, jboolean } static const JNINativeMethod gMethods[] = { - {"nativeGetTotalStat", "(IZ)J", (void*) getTotalStat}, - {"nativeGetIfaceStat", "(Ljava/lang/String;IZ)J", (void*) getIfaceStat}, - {"nativeGetUidStat", "(IIZ)J", (void*) getUidStat}, + {"nativeGetTotalStat", "(I)J", (void*)getTotalStat}, + {"nativeGetIfaceStat", "(Ljava/lang/String;I)J", (void*)getIfaceStat}, + {"nativeGetUidStat", "(II)J", (void*)getUidStat}, }; int register_android_server_net_NetworkStatsService(JNIEnv* env) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index aca7cc9070b1..1f96c6674a12 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -264,6 +264,8 @@ public final class SystemServer implements Dumpable { "com.android.server.stats.StatsCompanion$Lifecycle"; private static final String STATS_PULL_ATOM_SERVICE_CLASS = "com.android.server.stats.pull.StatsPullAtomService"; + private static final String STATS_BOOTSTRAP_ATOM_SERVICE_LIFECYCLE_CLASS = + "com.android.server.stats.bootstrap.StatsBootstrapAtomService$Lifecycle"; private static final String USB_SERVICE_CLASS = "com.android.server.usb.UsbService$Lifecycle"; private static final String MIDI_SERVICE_CLASS = @@ -364,6 +366,8 @@ public final class SystemServer implements Dumpable { "com.android.server.blob.BlobStoreManagerService"; private static final String APP_SEARCH_MANAGER_SERVICE_CLASS = "com.android.server.appsearch.AppSearchManagerService"; + private static final String ISOLATED_COMPILATION_SERVICE_CLASS = + "com.android.server.compos.IsolatedCompilationService"; private static final String ROLLBACK_MANAGER_SERVICE_CLASS = "com.android.server.rollback.RollbackManagerService"; private static final String ALARM_MANAGER_SERVICE_CLASS = @@ -2487,6 +2491,11 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(STATS_PULL_ATOM_SERVICE_CLASS); t.traceEnd(); + // Log atoms to statsd from bootstrap processes. + t.traceBegin("StatsBootstrapAtomService"); + mSystemServiceManager.startService(STATS_BOOTSTRAP_ATOM_SERVICE_LIFECYCLE_CLASS); + t.traceEnd(); + // Incidentd and dumpstated helper t.traceBegin("StartIncidentCompanionService"); mSystemServiceManager.startService(IncidentCompanionService.class); @@ -2651,6 +2660,12 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(APP_SEARCH_MANAGER_SERVICE_CLASS); t.traceEnd(); + if (SystemProperties.getBoolean("ro.config.isolated_compilation_enabled", false)) { + t.traceBegin("IsolatedCompilationService"); + mSystemServiceManager.startService(ISOLATED_COMPILATION_SERVICE_CLASS); + t.traceEnd(); + } + t.traceBegin("StartMediaCommunicationService"); mSystemServiceManager.startService(MEDIA_COMMUNICATION_SERVICE_CLASS); t.traceEnd(); diff --git a/services/net/OWNERS b/services/net/OWNERS index d3836d4c6c57..62c5737a2e8e 100644 --- a/services/net/OWNERS +++ b/services/net/OWNERS @@ -1,8 +1,2 @@ set noparent - -codewiz@google.com -jchalard@google.com -junyulai@google.com -lorenzo@google.com -reminv@google.com -satk@google.com +file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking diff --git a/services/people/java/com/android/server/people/data/CallLogQueryHelper.java b/services/people/java/com/android/server/people/data/CallLogQueryHelper.java index 45e0aac24ca7..ff901af3defa 100644 --- a/services/people/java/com/android/server/people/data/CallLogQueryHelper.java +++ b/services/people/java/com/android/server/people/data/CallLogQueryHelper.java @@ -93,6 +93,9 @@ class CallLogQueryHelper { hasResults = true; } } + } catch (SecurityException ex) { + Slog.e(TAG, "Query call log failed: " + ex); + return false; } return hasResults; } diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java index 4fb801e188d0..62a16f7f24fd 100644 --- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java @@ -35,6 +35,7 @@ import android.provider.DeviceConfig; import android.util.Log; import com.android.internal.R; +import com.android.internal.os.BackgroundThread; import com.android.server.IoThread; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -90,10 +91,12 @@ public final class ProfcollectForwardingService extends SystemService { if (mIProfcollect == null) { return; } - if (serviceHasSupportedTraceProvider()) { - registerObservers(); - } - ProfcollectBGJobService.schedule(getContext()); + BackgroundThread.get().getThreadHandler().post(() -> { + if (serviceHasSupportedTraceProvider()) { + registerObservers(); + ProfcollectBGJobService.schedule(getContext()); + } + }); } } @@ -146,7 +149,7 @@ public final class ProfcollectForwardingService extends SystemService { connectNativeService(); break; default: - throw new AssertionError("Unknown message: " + message.toString()); + throw new AssertionError("Unknown message: " + message); } } } @@ -190,11 +193,14 @@ public final class ProfcollectForwardingService extends SystemService { Log.d(LOG_TAG, "Starting background process job"); } - try { - sSelfService.mIProfcollect.process(false); - } catch (RemoteException e) { - Log.e(LOG_TAG, e.getMessage()); - } + BackgroundThread.get().getThreadHandler().post( + () -> { + try { + sSelfService.mIProfcollect.process(); + } catch (RemoteException e) { + Log.e(LOG_TAG, e.getMessage()); + } + }); return true; } @@ -230,14 +236,16 @@ public final class ProfcollectForwardingService extends SystemService { "applaunch_trace_freq", 2); int randomNum = ThreadLocalRandom.current().nextInt(100); if (randomNum < traceFrequency) { - try { - if (DEBUG) { - Log.d(LOG_TAG, "Tracing on app launch event: " + packageName); - } - mIProfcollect.trace_once("applaunch"); - } catch (RemoteException e) { - Log.e(LOG_TAG, e.getMessage()); + if (DEBUG) { + Log.d(LOG_TAG, "Tracing on app launch event: " + packageName); } + BackgroundThread.get().getThreadHandler().post(() -> { + try { + mIProfcollect.trace_once("applaunch"); + } catch (RemoteException e) { + Log.e(LOG_TAG, e.getMessage()); + } + }); } } @@ -301,7 +309,7 @@ public final class ProfcollectForwardingService extends SystemService { } Context context = getContext(); - new Thread(() -> { + BackgroundThread.get().getThreadHandler().post(() -> { try { // Prepare profile report String reportName = mIProfcollect.report() + ".zip"; @@ -321,6 +329,6 @@ public final class ProfcollectForwardingService extends SystemService { } catch (RemoteException e) { Log.e(LOG_TAG, e.getMessage()); } - }).start(); + }); } } diff --git a/services/tests/mockingservicestests/OWNERS b/services/tests/mockingservicestests/OWNERS new file mode 100644 index 000000000000..0fb0c3021486 --- /dev/null +++ b/services/tests/mockingservicestests/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/services/core/java/com/android/server/am/OWNERS diff --git a/services/tests/servicestests/OWNERS b/services/tests/servicestests/OWNERS new file mode 100644 index 000000000000..0fb0c3021486 --- /dev/null +++ b/services/tests/servicestests/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/services/core/java/com/android/server/am/OWNERS diff --git a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java index 3ace3f4c79dc..a1d4c203de18 100644 --- a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java +++ b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java @@ -66,7 +66,7 @@ public class BluetoothAirplaneModeListenerTest { when(mHelper.isBluetoothOn()).thenReturn(true); Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); - when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); + when(mHelper.isMediaProfileConnected()).thenReturn(true); Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange()); when(mHelper.isAirplaneModeOn()).thenReturn(true); @@ -83,7 +83,7 @@ public class BluetoothAirplaneModeListenerTest { public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_NotPopToast() { mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT; when(mHelper.isBluetoothOn()).thenReturn(true); - when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); + when(mHelper.isMediaProfileConnected()).thenReturn(true); when(mHelper.isAirplaneModeOn()).thenReturn(true); mBluetoothAirplaneModeListener.handleAirplaneModeChange(); @@ -97,7 +97,7 @@ public class BluetoothAirplaneModeListenerTest { public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_PopToast() { mBluetoothAirplaneModeListener.mToastCount = 0; when(mHelper.isBluetoothOn()).thenReturn(true); - when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true); + when(mHelper.isMediaProfileConnected()).thenReturn(true); when(mHelper.isAirplaneModeOn()).thenReturn(true); mBluetoothAirplaneModeListener.handleAirplaneModeChange(); diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java index 5c53d43fa1df..9e1445cf589d 100644 --- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java @@ -26,11 +26,11 @@ import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Intent; import android.media.AudioManager; import android.media.AudioSystem; +import android.media.BtProfileConnectionInfo; import android.util.Log; import androidx.test.InstrumentationRegistry; @@ -98,16 +98,12 @@ public class AudioDeviceBrokerTest { Log.i(TAG, "starting testPostA2dpDeviceConnectionChange"); Assert.assertNotNull("invalid null BT device", mFakeBtDevice); - mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice, - BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1)); + mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( + new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null, + BtProfileConnectionInfo.a2dpInfo(true, 1), "testSource")); Thread.sleep(2 * MAX_MESSAGE_HANDLING_DELAY_MS); - verify(mSpyDevInventory, times(1)).setBluetoothA2dpDeviceConnectionState( - any(BluetoothDevice.class), - ArgumentMatchers.eq(BluetoothProfile.STATE_CONNECTED) /*state*/, - ArgumentMatchers.eq(BluetoothProfile.A2DP) /*profile*/, - ArgumentMatchers.eq(true) /*suppressNoisyIntent*/, anyInt() /*musicDevice*/, - ArgumentMatchers.eq(1) /*a2dpVolume*/ + verify(mSpyDevInventory, times(1)).setBluetoothActiveDevice( + any(AudioDeviceBroker.BtDeviceInfo.class) ); // verify the connection was reported to AudioSystem @@ -210,30 +206,29 @@ public class AudioDeviceBrokerTest { ((NoOpAudioSystemAdapter) mSpyAudioSystem).configureIsStreamActive(mockMediaPlayback); // first connection: ensure the device is connected as a starting condition for the test - mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice, - BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1)); + mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( + new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null, + BtProfileConnectionInfo.a2dpInfo(true, 1), "testSource")); Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); // disconnection - mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice, - BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP, false, -1)); + mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( + new AudioDeviceBroker.BtDeviceChangedData(null, mFakeBtDevice, + BtProfileConnectionInfo.a2dpInfo(false, -1), "testSource")); if (delayAfterDisconnection > 0) { Thread.sleep(delayAfterDisconnection); } // reconnection - mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice, - BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 2)); + mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged( + new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null, + BtProfileConnectionInfo.a2dpInfo(true, 2), "testSource")); Thread.sleep(AudioService.BECOMING_NOISY_DELAY_MS + MAX_MESSAGE_HANDLING_DELAY_MS); // Verify disconnection has been cancelled and we're seeing two connections attempts, // with the device connected at the end of the test - verify(mSpyDevInventory, times(2)).onSetA2dpSinkConnectionState( - any(BtHelper.BluetoothA2dpDeviceInfo.class), - ArgumentMatchers.eq(BluetoothProfile.STATE_CONNECTED)); + verify(mSpyDevInventory, times(2)).onSetBtActiveDevice( + any(AudioDeviceBroker.BtDeviceInfo.class), anyInt()); Assert.assertTrue("Mock device not connected", mSpyDevInventory.isA2dpDeviceConnected(mFakeBtDevice)); diff --git a/services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java b/services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java index c97a67bad590..16d97a454050 100644 --- a/services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java +++ b/services/tests/servicestests/src/com/android/server/health/HealthServiceWrapperTest.java @@ -19,11 +19,12 @@ package com.android.server.health; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.fail; +import static org.mockito.AdditionalMatchers.not; import static org.mockito.Mockito.*; -import android.hardware.health.V2_0.IHealth; import android.hidl.manager.V1_0.IServiceManager; import android.hidl.manager.V1_0.IServiceNotification; +import android.os.IServiceCallback; import android.os.RemoteException; import androidx.test.filters.SmallTest; @@ -44,28 +45,47 @@ import java.util.NoSuchElementException; @RunWith(AndroidJUnit4.class) public class HealthServiceWrapperTest { - @Mock IServiceManager mMockedManager; - @Mock IHealth mMockedHal; - @Mock IHealth mMockedHal2; + @Mock android.hardware.health.V2_0.IHealth mMockedHal; + @Mock android.hardware.health.V2_0.IHealth mMockedHal2; @Mock HealthServiceWrapperHidl.Callback mCallback; @Mock HealthServiceWrapperHidl.IServiceManagerSupplier mManagerSupplier; @Mock HealthServiceWrapperHidl.IHealthSupplier mHealthServiceSupplier; + + @Mock android.hardware.health.IHealth.Stub mMockedAidlHal; + @Mock android.hardware.health.IHealth.Stub mMockedAidlHal2; + @Mock HealthServiceWrapperAidl.ServiceManagerStub mMockedAidlManager; + @Mock HealthRegCallbackAidl mRegCallbackAidl; + HealthServiceWrapper mWrapper; private static final String VENDOR = HealthServiceWrapperHidl.INSTANCE_VENDOR; + private static final String AIDL_SERVICE_NAME = HealthServiceWrapperAidl.SERVICE_NAME; @Before public void setUp() { MockitoAnnotations.initMocks(this); + + // Mocks the conversion between IHealth and IBinder. + when(mMockedAidlHal.asBinder()).thenCallRealMethod(); // returns mMockedAidlHal + when(mMockedAidlHal2.asBinder()).thenCallRealMethod(); // returns mMockedAidlHal2 + when(mMockedAidlHal.queryLocalInterface(android.hardware.health.IHealth.DESCRIPTOR)) + .thenReturn(mMockedAidlHal); + when(mMockedAidlHal2.queryLocalInterface(android.hardware.health.IHealth.DESCRIPTOR)) + .thenReturn(mMockedAidlHal2); } @After public void tearDown() { + validateMockitoUsage(); if (mWrapper != null) mWrapper.getHandlerThread().quitSafely(); } + public static <T> ArgumentMatcher<T> isOneOf(T[] collection) { + return isOneOf(Arrays.asList(collection)); + } + public static <T> ArgumentMatcher<T> isOneOf(Collection<T> collection) { return new ArgumentMatcher<T>() { @Override @@ -75,13 +95,39 @@ public class HealthServiceWrapperTest { @Override public String toString() { - return collection.toString(); + return "is one of " + collection.toString(); } }; } - private void initForInstances(String... instanceNamesArr) throws Exception { - final Collection<String> instanceNames = Arrays.asList(instanceNamesArr); + /** + * Set up mock objects to pretend that the given AIDL and HIDL instances exists. + * + * <p>Also, when registering service notifications, the mocked service managers immediately + * sends 3 registration notifications, including 2 referring to the original HAL and 1 referring + * to the new HAL. + * + * @param aidlInstances e.g. {"android.hardware.health.IHealth/default"} + * @param hidlInstances e.g. {"default", "backup"} + * @throws Exception + */ + private void initForInstances(String[] aidlInstances, String[] hidlInstances) throws Exception { + doAnswer( + (invocation) -> { + sendAidlRegCallback(invocation, mMockedAidlHal); + sendAidlRegCallback(invocation, mMockedAidlHal); + sendAidlRegCallback(invocation, mMockedAidlHal2); + return null; + }) + .when(mMockedAidlManager) + .registerForNotifications( + argThat(isOneOf(aidlInstances)), any(IServiceCallback.class)); + when(mMockedAidlManager.waitForDeclaredService(argThat(isOneOf(aidlInstances)))) + .thenReturn(mMockedAidlHal) + .thenThrow(new RuntimeException("waitForDeclaredService called more than once")); + when(mMockedAidlManager.waitForDeclaredService(not(argThat(isOneOf(aidlInstances))))) + .thenReturn(null); + doAnswer( (invocation) -> { // technically, preexisting is ignored by @@ -93,8 +139,8 @@ public class HealthServiceWrapperTest { }) .when(mMockedManager) .registerForNotifications( - eq(IHealth.kInterfaceName), - argThat(isOneOf(instanceNames)), + eq(android.hardware.health.V2_0.IHealth.kInterfaceName), + argThat(isOneOf(hidlInstances)), any(IServiceNotification.class)); doReturn(mMockedManager).when(mManagerSupplier).get(); @@ -104,7 +150,7 @@ public class HealthServiceWrapperTest { .doReturn(mMockedHal2) // notification 3 .doThrow(new RuntimeException("Should not call getService for more than 4 times")) .when(mHealthServiceSupplier) - .get(argThat(isOneOf(instanceNames))); + .get(argThat(isOneOf(hidlInstances))); } private void waitHandlerThreadFinish() throws Exception { @@ -121,19 +167,62 @@ public class HealthServiceWrapperTest { throws Exception { ((IServiceNotification) invocation.getArguments()[2]) .onRegistration( - IHealth.kInterfaceName, (String) invocation.getArguments()[1], preexisting); + android.hardware.health.V2_0.IHealth.kInterfaceName, + (String) invocation.getArguments()[1], + preexisting); + } + + private static void sendAidlRegCallback( + InvocationOnMock invocation, android.hardware.health.IHealth service) throws Exception { + ((IServiceCallback) invocation.getArguments()[1]) + .onRegistration((String) invocation.getArguments()[0], service.asBinder()); } private void createWrapper() throws RemoteException { - mWrapper = HealthServiceWrapper.create(mCallback, mManagerSupplier, mHealthServiceSupplier); + mWrapper = + HealthServiceWrapper.create( + mRegCallbackAidl, + mMockedAidlManager, + mCallback, + mManagerSupplier, + mHealthServiceSupplier); + } + + @SmallTest + @Test + public void testWrapAidlOnly() throws Exception { + initForInstances(new String[] {AIDL_SERVICE_NAME}, new String[0]); + createWrapper(); + waitHandlerThreadFinish(); + verify(mRegCallbackAidl, times(1)).onRegistration(same(null), same(mMockedAidlHal)); + verify(mRegCallbackAidl, never()) + .onRegistration(same(mMockedAidlHal), same(mMockedAidlHal)); + verify(mRegCallbackAidl, times(1)) + .onRegistration(same(mMockedAidlHal), same(mMockedAidlHal2)); + verify(mCallback, never()).onRegistration(any(), any(), anyString()); + } + + @SmallTest + @Test + public void testWrapPreferAidl() throws Exception { + initForInstances(new String[] {AIDL_SERVICE_NAME}, new String[] {VENDOR}); + createWrapper(); + waitHandlerThreadFinish(); + verify(mRegCallbackAidl, times(1)).onRegistration(same(null), same(mMockedAidlHal)); + verify(mRegCallbackAidl, never()) + .onRegistration(same(mMockedAidlHal), same(mMockedAidlHal)); + verify(mRegCallbackAidl, times(1)) + .onRegistration(same(mMockedAidlHal), same(mMockedAidlHal2)); + verify(mCallback, never()).onRegistration(any(), any(), anyString()); } @SmallTest @Test - public void testWrapPreferVendor() throws Exception { - initForInstances(VENDOR); + public void testWrapFallbackHidl() throws Exception { + initForInstances(new String[0], new String[] {VENDOR}); createWrapper(); waitHandlerThreadFinish(); + verify(mRegCallbackAidl, never()).onRegistration(any(), any()); verify(mCallback, times(1)).onRegistration(same(null), same(mMockedHal), eq(VENDOR)); verify(mCallback, never()).onRegistration(same(mMockedHal), same(mMockedHal), anyString()); verify(mCallback, times(1)).onRegistration(same(mMockedHal), same(mMockedHal2), eq(VENDOR)); @@ -142,7 +231,7 @@ public class HealthServiceWrapperTest { @SmallTest @Test public void testNoService() throws Exception { - initForInstances("unrelated"); + initForInstances(new String[0], new String[] {"unrelated"}); try { createWrapper(); fail("Expect NoSuchElementException"); diff --git a/services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java b/services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java index ce72499007ba..d5d2cbd0749e 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/ReferenceWithHistoryTest.java @@ -17,17 +17,19 @@ package com.android.server.timedetector; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import android.util.IndentingPrintWriter; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.util.IndentingPrintWriter; import com.android.server.timezonedetector.ReferenceWithHistory; import org.junit.Test; import org.junit.runner.RunWith; import java.io.StringWriter; +import java.util.Arrays; @RunWith(AndroidJUnit4.class) public class ReferenceWithHistoryTest { @@ -41,31 +43,34 @@ public class ReferenceWithHistoryTest { // Check unset behavior. compareGet(referenceWithHistory, reference, null); - assertNotNull(dumpReferenceWithHistory(referenceWithHistory)); + assertDumpContent(referenceWithHistory); compareToString(referenceWithHistory, reference, "null"); // Try setting null. setAndCompareReturnValue(referenceWithHistory, reference, null); compareGet(referenceWithHistory, reference, null); - assertNotNull(dumpReferenceWithHistory(referenceWithHistory)); + assertDumpContent(referenceWithHistory, new DumpLine(0, "null")); compareToString(referenceWithHistory, reference, "null"); // Try setting a non-null value. setAndCompareReturnValue(referenceWithHistory, reference, "Foo"); compareGet(referenceWithHistory, reference, "Foo"); - assertNotNull(dumpReferenceWithHistory(referenceWithHistory)); + assertDumpContent(referenceWithHistory, + new DumpLine(0, "null"), new DumpLine(1, "Foo")); compareToString(referenceWithHistory, reference, "Foo"); // Try setting null again. - setAndCompareReturnValue(referenceWithHistory, reference, "Foo"); - compareGet(referenceWithHistory, reference, "Foo"); - assertNotNull(dumpReferenceWithHistory(referenceWithHistory)); - compareToString(referenceWithHistory, reference, "Foo"); + setAndCompareReturnValue(referenceWithHistory, reference, null); + compareGet(referenceWithHistory, reference, null); + assertDumpContent(referenceWithHistory, + new DumpLine(1, "Foo"), new DumpLine(2, "null")); + compareToString(referenceWithHistory, reference, "null"); // Try a non-null value again. setAndCompareReturnValue(referenceWithHistory, reference, "Bar"); compareGet(referenceWithHistory, reference, "Bar"); - assertNotNull(dumpReferenceWithHistory(referenceWithHistory)); + assertDumpContent(referenceWithHistory, + new DumpLine(2, "null"), new DumpLine(3, "Bar")); compareToString(referenceWithHistory, reference, "Bar"); } @@ -132,11 +137,54 @@ public class ReferenceWithHistoryTest { assertEquals(expected, referenceWithHistory.toString()); } - private static String dumpReferenceWithHistory(ReferenceWithHistory<?> referenceWithHistory) { + private static void assertDumpContent( + ReferenceWithHistory<?> referenceWithHistory, DumpLine... expectedLines) { + String[] actualLines = dumpReferenceWithHistory(referenceWithHistory); + + if (expectedLines.length == 0) { + String expectedEmptyOutput = "{Empty}"; + assertEquals(expectedEmptyOutput, 1, actualLines.length); + assertEquals(expectedEmptyOutput, actualLines[0]); + } else { + assertEquals("Expected=" + Arrays.toString(expectedLines) + + ", actual=" + Arrays.toString(actualLines), + expectedLines.length, actualLines.length); + for (int i = 0; i < expectedLines.length; i++) { + DumpLine expectedLine = expectedLines[i]; + String actualLine = actualLines[i]; + assertTrue("i=" + i + ", expected=" + expectedLine + ", actual=" + actualLine, + actualLine.startsWith(Integer.toString(expectedLine.mIndex))); + assertTrue("i=" + i + ", expected=" + expectedLine + ", actual=" + actualLine, + actualLine.endsWith(expectedLine.mLine)); + } + } + } + + private static String[] dumpReferenceWithHistory(ReferenceWithHistory<?> referenceWithHistory) { StringWriter stringWriter = new StringWriter(); try (IndentingPrintWriter ipw = new IndentingPrintWriter(stringWriter, " ")) { referenceWithHistory.dump(ipw); - return stringWriter.toString(); + return stringWriter.toString().split("\n"); + } + } + + /** An expected line of {@link ReferenceWithHistory#dump} output. */ + private static class DumpLine { + + final int mIndex; + final String mLine; + + DumpLine(int index, String line) { + mIndex = index; + mLine = line; + } + + @Override + public String toString() { + return "DumpLine{" + + "mIndex=" + mIndex + + ", mLine='" + mLine + '\'' + + '}'; } } } diff --git a/telephony/common/Android.bp b/telephony/common/Android.bp index 1cacc0365fe1..b0a812bf57a3 100644 --- a/telephony/common/Android.bp +++ b/telephony/common/Android.bp @@ -21,7 +21,10 @@ filegroup { filegroup { name: "framework-mms-shared-srcs", - visibility: ["//packages/apps/Bluetooth"], + visibility: [ + "//packages/apps/Bluetooth", + "//packages/modules/Bluetooth/android/app", + ], srcs: [ "com/google/android/mms/**/*.java", ], diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index df1f13294961..5ffe45f8c489 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -5141,16 +5141,6 @@ public class CarrierConfigManager { "call_composer_picture_server_url_string"; /** - * For Android 11, provide a temporary solution for OEMs to use the lower of the two MTU values - * for IPv4 and IPv6 if both are sent. - * TODO: remove in later release - * - * @hide - */ - public static final String KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED = - "use_lower_mtu_value_if_both_received"; - - /** * Determines the default RTT mode. * * Upon first boot, when the user has not yet set a value for their preferred RTT mode, @@ -5269,6 +5259,16 @@ public class CarrierConfigManager { public static final String KEY_VONR_SETTING_VISIBILITY_BOOL = "vonr_setting_visibility_bool"; /** + * Flag specifying whether VoNR should be enabled for carrier. + * If true, VoNr will be enabled. If false, hard disabled. + * + * Disabled by default. + * + * @hide + */ + public static final String KEY_VONR_ENABLED_BOOL = "vonr_enabled_bool"; + + /** * Determine whether unthrottle data retry when tracking area code (TAC/LAC) from cell changes * * @hide @@ -5878,7 +5878,6 @@ public class CarrierConfigManager { sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, ""); sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false); sDefaults.putString(KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING, ""); - sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false); sDefaults.putBoolean(KEY_USE_ACS_FOR_RCS_BOOL, false); sDefaults.putBoolean(KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL, true); sDefaults.putInt(KEY_DEFAULT_RTT_MODE_INT, 0); @@ -5893,6 +5892,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_DISPLAY_NO_DATA_NOTIFICATION_ON_PERMANENT_FAILURE_BOOL, false); sDefaults.putBoolean(KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL, false); sDefaults.putBoolean(KEY_VONR_SETTING_VISIBILITY_BOOL, false); + sDefaults.putBoolean(KEY_VONR_ENABLED_BOOL, false); } /** diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java index 42d7707c97d9..fc76f99cf074 100644 --- a/telephony/java/android/telephony/ImsManager.java +++ b/telephony/java/android/telephony/ImsManager.java @@ -119,7 +119,7 @@ public class ImsManager { throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId); } - return new ImsRcsManager(mContext, subscriptionId, sRcsCache); + return new ImsRcsManager(mContext, subscriptionId, sRcsCache, sTelephonyCache); } /** @@ -135,7 +135,7 @@ public class ImsManager { throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId); } - return new ImsMmTelManager(subscriptionId, sTelephonyCache); + return new ImsMmTelManager(mContext, subscriptionId, sTelephonyCache); } /** @@ -157,7 +157,7 @@ public class ImsManager { throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId); } - return new SipDelegateManager(mContext, subscriptionId, sRcsCache); + return new SipDelegateManager(mContext, subscriptionId, sRcsCache, sTelephonyCache); } private static IImsRcsController getIImsRcsControllerInterface() { diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java index 2ff4ac5e30d3..9cb80f1814f9 100644 --- a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java +++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java @@ -71,12 +71,7 @@ public final class SignalStrengthUpdateRequest implements Parcelable { @Nullable List<SignalThresholdInfo> signalThresholdInfos, boolean isReportingRequestedWhileIdle, boolean isSystemThresholdReportingRequestedWhileIdle) { - // System app (like Bluetooth) can specify the request to report system thresholds while - // device is idle (with permission protection). In this case, the request doesn't need to - // provide a non-empty list of SignalThresholdInfo which is only asked for public apps. - if (!isSystemThresholdReportingRequestedWhileIdle) { - validate(signalThresholdInfos); - } + validate(signalThresholdInfos, isSystemThresholdReportingRequestedWhileIdle); mSignalThresholdInfos = signalThresholdInfos; mIsReportingRequestedWhileIdle = isReportingRequestedWhileIdle; @@ -274,8 +269,12 @@ public final class SignalStrengthUpdateRequest implements Parcelable { * Throw IAE if SignalThresholdInfo collection is null or empty, * or the SignalMeasurementType for the same RAN in the collection is not unique. */ - private static void validate(Collection<SignalThresholdInfo> infos) { - if (infos == null || infos.isEmpty()) { + private static void validate(Collection<SignalThresholdInfo> infos, + boolean isSystemThresholdReportingRequestedWhileIdle) { + // System app (like Bluetooth) can specify the request to report system thresholds while + // device is idle (with permission protection). In this case, the request doesn't need to + // provide a non-empty list of SignalThresholdInfo which is only asked for public apps. + if (infos == null || (infos.isEmpty() && !isSystemThresholdReportingRequestedWhileIdle)) { throw new IllegalArgumentException("SignalThresholdInfo collection is null or empty"); } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 3b44a34048b8..d5315acf60d5 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -949,6 +949,15 @@ public class SubscriptionManager { public static final String VOIMS_OPT_IN_STATUS = SimInfo.COLUMN_VOIMS_OPT_IN_STATUS; /** + * TelephonyProvider column name for NR Advanced calling + * Determines if the user has enabled VoNR settings for this subscription. + * + * @hide + */ + public static final String NR_ADVANCED_CALLING_ENABLED = + SimInfo.COLUMN_NR_ADVANCED_CALLING_ENABLED; + + /** * Profile class of the subscription * @hide */ diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 6ffdc6b1696d..a1d68b215ba5 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -315,6 +315,12 @@ public class TelephonyManager { */ public static final int UNINITIALIZED_CARD_ID = -2; + /** + * Default port index for the UICC Card + * @hide + */ + public static final int DEFAULT_PORT_INDEX = 0; + private final Context mContext; private final int mSubId; @UnsupportedAppUsage diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index 36082dc8180e..683bb92845ba 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -25,6 +25,7 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressAutoDoc; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.content.Context; import android.os.Binder; import android.os.RemoteException; import android.os.ServiceSpecificException; @@ -45,6 +46,7 @@ import com.android.internal.telephony.ITelephony; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -214,6 +216,7 @@ public class ImsMmTelManager implements RegistrationManager { } } + private final Context mContext; private final int mSubId; private final BinderCacheManager<ITelephony> mBinderCache; @@ -255,6 +258,16 @@ public class ImsMmTelManager implements RegistrationManager { */ @VisibleForTesting public ImsMmTelManager(int subId, BinderCacheManager<ITelephony> binderCache) { + this(null, subId, binderCache); + } + + /** + * Only visible for testing, use {@link ImsManager#getImsMmTelManager(int)} instead. + * @hide + */ + @VisibleForTesting + public ImsMmTelManager(Context context, int subId, BinderCacheManager<ITelephony> binderCache) { + mContext = context; mSubId = subId; mBinderCache = binderCache; } @@ -1482,6 +1495,74 @@ public class ImsMmTelManager implements RegistrationManager { } } + /** + * Register a new callback, which is used to notify the registrant of changes to + * the state of the underlying IMS service that is attached to telephony to + * implement IMS functionality. If the manager is created for + * the {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}, + * this throws an {@link ImsException}. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE} + * or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param executor the Executor that will be used to call the {@link ImsStateCallback}. + * @param callback The callback instance being registered. + * @throws ImsException in the case that the callback can not be registered. + * See {@link ImsException#getCode} for more information on when this is called. + */ + @RequiresPermission(anyOf = {Manifest.permission.READ_PRECISE_PHONE_STATE, + Manifest.permission.READ_PRIVILEGED_PHONE_STATE}) + public void registerImsStateCallback(@NonNull Executor executor, + @NonNull ImsStateCallback callback) throws ImsException { + Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); + Objects.requireNonNull(executor, "Must include a non-null Executor."); + + callback.init(executor); + ITelephony telephony = mBinderCache.listenOnBinder(callback, callback::binderDied); + if (telephony == null) { + throw new ImsException("Telephony server is down", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + + try { + telephony.registerImsStateCallback( + mSubId, ImsFeature.FEATURE_MMTEL, + callback.getCallbackBinder(), getOpPackageName()); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); + } catch (RemoteException | IllegalStateException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** + * Unregisters a previously registered callback. + * + * @param callback The callback instance to be unregistered. + */ + public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) { + Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); + + ITelephony telephony = mBinderCache.removeRunnable(callback); + try { + if (telephony != null) { + telephony.unregisterImsStateCallback(callback.getCallbackBinder()); + } + } catch (RemoteException ignore) { + // ignore it + } + } + + private String getOpPackageName() { + if (mContext != null) { + return mContext.getOpPackageName(); + } else { + return null; + } + } + private ITelephony getITelephony() { return mBinderCache.getBinder(); } diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java index 8d6fa4141b5c..1b047c77d80b 100644 --- a/telephony/java/android/telephony/ims/ImsRcsManager.java +++ b/telephony/java/android/telephony/ims/ImsRcsManager.java @@ -39,9 +39,11 @@ import android.telephony.ims.stub.ImsRegistrationImplBase; import android.util.Log; import com.android.internal.telephony.IIntegerConsumer; +import com.android.internal.telephony.ITelephony; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -159,6 +161,7 @@ public class ImsRcsManager { private final int mSubId; private final Context mContext; private final BinderCacheManager<IImsRcsController> mBinderCache; + private final BinderCacheManager<ITelephony> mTelephonyBinderCache; private final Map<OnAvailabilityChangedListener, AvailabilityCallbackAdapter> mAvailabilityChangedCallbacks; @@ -167,11 +170,13 @@ public class ImsRcsManager { * @hide */ public ImsRcsManager(Context context, int subId, - BinderCacheManager<IImsRcsController> binderCache) { + BinderCacheManager<IImsRcsController> binderCache, + BinderCacheManager<ITelephony> telephonyBinderCache) { mSubId = subId; mContext = context; mBinderCache = binderCache; mAvailabilityChangedCallbacks = new HashMap<>(); + mTelephonyBinderCache = telephonyBinderCache; } /** @@ -534,6 +539,67 @@ public class ImsRcsManager { } /** + * Register a new callback, which is used to notify the registrant of changes to + * the state of the underlying IMS service that is attached to telephony to + * implement IMS functionality. If the manager is created for + * the {@link android.telephony.SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}, + * this throws an {@link ImsException}. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE} + * or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param executor the Executor that will be used to call the {@link ImsStateCallback}. + * @param callback The callback instance being registered. + * @throws ImsException in the case that the callback can not be registered. + * See {@link ImsException#getCode} for more information on when this is called. + */ + @RequiresPermission(anyOf = {Manifest.permission.READ_PRECISE_PHONE_STATE, + Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE}) + public void registerImsStateCallback(@NonNull Executor executor, + @NonNull ImsStateCallback callback) throws ImsException { + Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); + Objects.requireNonNull(executor, "Must include a non-null Executor."); + + callback.init(executor); + ITelephony telephony = mTelephonyBinderCache.listenOnBinder(callback, callback::binderDied); + if (telephony == null) { + throw new ImsException("Telephony server is down", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + + try { + telephony.registerImsStateCallback( + mSubId, ImsFeature.FEATURE_RCS, + callback.getCallbackBinder(), mContext.getOpPackageName()); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); + } catch (RemoteException | IllegalStateException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** + * Unregisters a previously registered callback. + * + * @param callback The callback instance to be unregistered. + */ + public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) { + Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); + + ITelephony telephony = mTelephonyBinderCache.removeRunnable(callback); + try { + if (telephony != null) { + telephony.unregisterImsStateCallback(callback.getCallbackBinder()); + } + } catch (RemoteException ignore) { + // ignore it + } + } + + /** * Add the {@link OnAvailabilityChangedListener} to collection for tracking. * @param executor The executor that will be used when the publish state is changed and the * {@link OnAvailabilityChangedListener} is called. diff --git a/telephony/java/android/telephony/ims/ImsStateCallback.java b/telephony/java/android/telephony/ims/ImsStateCallback.java new file mode 100644 index 000000000000..b9ba93ff0db6 --- /dev/null +++ b/telephony/java/android/telephony/ims/ImsStateCallback.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2021 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.telephony.ims; + +import android.annotation.CallbackExecutor; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Binder; + +import com.android.internal.telephony.IImsStateCallback; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; +import java.util.concurrent.Executor; + +/** + * A callback class used for monitoring changes in IMS service connection states + * for a specific subscription. + * <p> + * @see ImsMmTelManager#registerImsStateCallback(Executor, ImsStateCallback) + * @see ImsRcsManager#registerImsStateCallback(Executor, ImsStateCallback) + */ +public abstract class ImsStateCallback { + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "REASON_", value = { + REASON_UNKNOWN_TEMPORARY_ERROR, + REASON_UNKNOWN_PERMANENT_ERROR, + REASON_IMS_SERVICE_DISCONNECTED, + REASON_NO_IMS_SERVICE_CONFIGURED, + REASON_SUBSCRIPTION_INACTIVE, + REASON_IMS_SERVICE_NOT_READY + }) + public @interface DisconnectedReason {} + + /** + * The underlying IMS service is temporarily unavailable for the + * associated subscription. + * {@link #onAvailable} will be called when the IMS service becomes + * available again. + */ + public static final int REASON_UNKNOWN_TEMPORARY_ERROR = 1; + + /** + * The underlying IMS service is permanently unavailable for the + * associated subscription and there will be no Manager available for + * this subscription. + */ + public static final int REASON_UNKNOWN_PERMANENT_ERROR = 2; + + /** + * The underlying IMS service has died, is reconfiguring, or has never + * come up yet and as a result is currently unavailable. + * {@link #onAvailable} will be called when the IMS service becomes + * available. All callbacks should be unregistered now and registered again + * if the IMS service moves back to available. + */ + public static final int REASON_IMS_SERVICE_DISCONNECTED = 3; + + /** + * There is no IMS service configured for the subscription ID specified. + * This is a permanent error and there will be no Manager available for + * this subscription. + */ + public static final int REASON_NO_IMS_SERVICE_CONFIGURED = 4; + + /** + * The subscription associated with this Manager has moved to an inactive + * state (e.g. SIM removed) and the IMS service has torn down the resources + * related to this subscription. This has caused this callback + * to be deregistered. The callback must be re-registered when this subscription + * becomes active in order to continue listening to the IMS service state. + */ + public static final int REASON_SUBSCRIPTION_INACTIVE = 5; + + /** + * The IMS service is connected, but in a NOT_READY state. Once the + * service moves to ready, {@link #onAvailable} will be called. + */ + public static final int REASON_IMS_SERVICE_NOT_READY = 6; + + private IImsStateCallbackStub mCallback; + + /** + * @hide + */ + public void init(@NonNull @CallbackExecutor Executor executor) { + if (executor == null) { + throw new IllegalArgumentException("ImsStateCallback Executor must be non-null"); + } + mCallback = new IImsStateCallbackStub(this, executor); + } + + /** + * Using a static class and weak reference here to avoid memory leak caused by the + * IImsStateCallback.Stub callback retaining references to the outside ImsStateCallback. + */ + private static class IImsStateCallbackStub extends IImsStateCallback.Stub { + private WeakReference<ImsStateCallback> mImsStateCallbackWeakRef; + private Executor mExecutor; + + IImsStateCallbackStub(ImsStateCallback imsStateCallback, Executor executor) { + mImsStateCallbackWeakRef = new WeakReference<ImsStateCallback>(imsStateCallback); + mExecutor = executor; + } + + Executor getExecutor() { + return mExecutor; + } + + public void onAvailable() { + ImsStateCallback callback = mImsStateCallbackWeakRef.get(); + if (callback == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> callback.onAvailable())); + } + + public void onUnavailable(int reason) { + ImsStateCallback callback = mImsStateCallbackWeakRef.get(); + if (callback == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute(() -> callback.onUnavailable(reason))); + } + } + + /** + * The IMS service has disconnected or is reporting NOT_READY and is no longer + * available to users. The user should clean up all related state and + * unregister callbacks. If it is a temporary error, {@link #onAvailable} will + * be called when the IMS service becomes available again. + * + * @param reason the specified reason + */ + public abstract void onUnavailable(@DisconnectedReason int reason); + + /** + * The IMS service is connected and is ready for communication over the + * provided Manager. + */ + public abstract void onAvailable(); + + /** + * An unexpected error has occurred and the Telephony process has crashed. This + * has caused this callback to be deregistered. The callback must be + * re-registered in order to continue listening to the IMS service state. + */ + public abstract void onError(); + + /** + * The callback to notify the death of telephony process + * @hide + */ + public final void binderDied() { + if (mCallback != null) { + mCallback.getExecutor().execute(() -> onError()); + } + } + + /** + * Return the callback binder + * @hide + */ + public IImsStateCallbackStub getCallbackBinder() { + return mCallback; + } +} diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java index 5a8066320e99..f913df588f40 100644 --- a/telephony/java/android/telephony/ims/SipDelegateManager.java +++ b/telephony/java/android/telephony/ims/SipDelegateManager.java @@ -28,15 +28,16 @@ import android.content.pm.PackageManager; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.telephony.BinderCacheManager; -import android.telephony.CarrierConfigManager; import android.telephony.ims.aidl.IImsRcsController; import android.telephony.ims.aidl.SipDelegateConnectionAidlWrapper; +import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.stub.DelegateConnectionMessageCallback; import android.telephony.ims.stub.DelegateConnectionStateCallback; import android.telephony.ims.stub.SipDelegate; import android.util.ArrayMap; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.telephony.ITelephony; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -282,6 +283,7 @@ public class SipDelegateManager { private final Context mContext; private final int mSubId; private final BinderCacheManager<IImsRcsController> mBinderCache; + private final BinderCacheManager<ITelephony> mTelephonyBinderCache; /** * Only visible for testing. To instantiate an instance of this class, please use @@ -290,10 +292,12 @@ public class SipDelegateManager { */ @VisibleForTesting public SipDelegateManager(Context context, int subId, - BinderCacheManager<IImsRcsController> binderCache) { + BinderCacheManager<IImsRcsController> binderCache, + BinderCacheManager<ITelephony> telephonyBinderCache) { mContext = context; mSubId = subId; mBinderCache = binderCache; + mTelephonyBinderCache = telephonyBinderCache; } /** @@ -446,4 +450,65 @@ public class SipDelegateManager { + " into this method"); } } + + /** + * Register a new callback, which is used to notify the registrant of changes to + * the state of the underlying IMS service that is attached to telephony to + * implement IMS functionality. If the manager is created for + * the {@link android.telephony.SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}, + * this throws an {@link ImsException}. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE} + * or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * @param executor the Executor that will be used to call the {@link ImsStateCallback}. + * @param callback The callback instance being registered. + * @throws ImsException in the case that the callback can not be registered. + * See {@link ImsException#getCode} for more information on when this is called. + */ + @RequiresPermission(anyOf = {Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION}) + public void registerImsStateCallback(@NonNull Executor executor, + @NonNull ImsStateCallback callback) throws ImsException { + Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); + Objects.requireNonNull(executor, "Must include a non-null Executor."); + + callback.init(executor); + ITelephony telephony = mTelephonyBinderCache.listenOnBinder(callback, callback::binderDied); + if (telephony == null) { + throw new ImsException("Telephony server is down", + ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + + try { + telephony.registerImsStateCallback( + mSubId, ImsFeature.FEATURE_RCS, + callback.getCallbackBinder(), mContext.getOpPackageName()); + } catch (ServiceSpecificException e) { + throw new ImsException(e.getMessage(), e.errorCode); + } catch (RemoteException | IllegalStateException e) { + throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); + } + } + + /** + * Unregisters a previously registered callback. + * + * @param callback The callback instance to be unregistered. + */ + public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) { + Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback."); + + ITelephony telephony = mTelephonyBinderCache.removeRunnable(callback); + + try { + if (telephony != null) { + telephony.unregisterImsStateCallback(callback.getCallbackBinder()); + } + } catch (RemoteException ignore) { + // ignore it + } + } } diff --git a/telephony/java/com/android/internal/telephony/IImsStateCallback.aidl b/telephony/java/com/android/internal/telephony/IImsStateCallback.aidl new file mode 100644 index 000000000000..e04b01d26e47 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IImsStateCallback.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021 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.internal.telephony; + +oneway interface IImsStateCallback { + void onUnavailable(int reason); + void onAvailable(); +} diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index d586a4a38f73..6b33a6894365 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -67,6 +67,7 @@ import com.android.ims.internal.IImsServiceFeatureCallback; import com.android.internal.telephony.CellNetworkScanResult; import com.android.internal.telephony.IBooleanConsumer; import com.android.internal.telephony.ICallForwardingInfoCallback; +import com.android.internal.telephony.IImsStateCallback; import com.android.internal.telephony.IIntegerConsumer; import com.android.internal.telephony.INumberVerificationCallback; import com.android.internal.telephony.OperatorInfo; @@ -2497,4 +2498,15 @@ interface ITelephony { * NSSAIs (configured, allowed and rejected). */ void getSlicingConfig(in ResultReceiver callback); + + /** + * Register an IMS connection state callback + */ + void registerImsStateCallback(int subId, int feature, in IImsStateCallback cb, + in String callingPackage); + + /** + * Unregister an IMS connection state callback + */ + void unregisterImsStateCallback(in IImsStateCallback cb); } diff --git a/tests/AttestationVerificationTest/OWNERS b/tests/AttestationVerificationTest/OWNERS new file mode 100644 index 000000000000..a7a6ef156eda --- /dev/null +++ b/tests/AttestationVerificationTest/OWNERS @@ -0,0 +1 @@ +include /core/java/android/security/attestationverification/OWNERS diff --git a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java index 84448333a8c6..525a78486efc 100644 --- a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java +++ b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java @@ -207,17 +207,6 @@ public class SystemPreparer extends ExternalResource { default: device.executeShellCommand("stop"); device.executeShellCommand("start"); - ITestDevice.RecoveryMode cachedRecoveryMode = device.getRecoveryMode(); - device.setRecoveryMode(ITestDevice.RecoveryMode.ONLINE); - - if (device.isEncryptionSupported()) { - if (device.isDeviceEncrypted()) { - LogUtil.CLog.e("Device is encrypted after userspace reboot!"); - device.unlockDevice(); - } - } - - device.setRecoveryMode(cachedRecoveryMode); device.waitForDeviceAvailable(); break; } diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh index ac23c3d9cb4a..36bea57b710f 100755 --- a/tools/aosp/aosp_sha.sh +++ b/tools/aosp/aosp_sha.sh @@ -1,7 +1,7 @@ #!/bin/bash LOCAL_DIR="$( dirname "${BASH_SOURCE}" )" -if git branch -vv | grep -q -P "^\*[^\[]+\[aosp/"; then +if git branch -vv | grep -q -E "^\*[^\[]+\[aosp/"; then # Change appears to be in AOSP exit 0 elif git log -n 1 --format='%B' $1 | grep -q -E "^Ignore-AOSP-First: .+" ; then |