diff --git a/android/build.gradle b/android/build.gradle index 723be882982..d0ae3b9c886 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -14,7 +14,7 @@ android { } compileSdkVersion 34 defaultConfig { - minSdkVersion 21 + minSdkVersion 22 targetSdkVersion 33 versionCode 1 versionName "1.0" diff --git a/android/src/main/java/io/grpc/android/AndroidChannelBuilder.java b/android/src/main/java/io/grpc/android/AndroidChannelBuilder.java index e56ce5fc405..54b38bc3bd3 100644 --- a/android/src/main/java/io/grpc/android/AndroidChannelBuilder.java +++ b/android/src/main/java/io/grpc/android/AndroidChannelBuilder.java @@ -217,7 +217,6 @@ private void configureNetworkMonitoring() { connectivityManager.registerDefaultNetworkCallback(defaultNetworkCallback); unregisterRunnable = new Runnable() { - @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public void run() { connectivityManager.unregisterNetworkCallback(defaultNetworkCallback); @@ -231,7 +230,6 @@ public void run() { context.registerReceiver(networkReceiver, networkIntentFilter); unregisterRunnable = new Runnable() { - @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public void run() { context.unregisterReceiver(networkReceiver); diff --git a/binder/build.gradle b/binder/build.gradle index dc9df6b04de..d1302ddfed0 100644 --- a/binder/build.gradle +++ b/binder/build.gradle @@ -13,7 +13,7 @@ android { targetCompatibility 1.8 } defaultConfig { - minSdkVersion 21 + minSdkVersion 22 targetSdkVersion 33 versionCode 1 versionName "1.0" diff --git a/binder/src/main/java/io/grpc/binder/AndroidComponentAddress.java b/binder/src/main/java/io/grpc/binder/AndroidComponentAddress.java index b390c1f0ccd..21781cd4ad3 100644 --- a/binder/src/main/java/io/grpc/binder/AndroidComponentAddress.java +++ b/binder/src/main/java/io/grpc/binder/AndroidComponentAddress.java @@ -17,12 +17,15 @@ package io.grpc.binder; import static android.content.Intent.URI_ANDROID_APP_SCHEME; +import static android.content.Intent.URI_INTENT_SCHEME; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; +import android.annotation.SuppressLint; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.os.Build; import android.os.UserHandle; import com.google.common.base.Objects; import io.grpc.ExperimentalApi; @@ -172,7 +175,8 @@ public String asAndroidAppUri() { // factory methods. Oddly, a ComponentName is not enough. intentForUri = intentForUri.cloneFilter().setPackage(getComponent().getPackageName()); } - return intentForUri.toUri(URI_ANDROID_APP_SCHEME); + return intentForUri.toUri( + URI_ANDROID_APP_SCHEME); } @Override diff --git a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java index 05e8c43da79..c0f6fe81989 100644 --- a/binder/src/main/java/io/grpc/binder/SecurityPolicies.java +++ b/binder/src/main/java/io/grpc/binder/SecurityPolicies.java @@ -184,7 +184,6 @@ public Status checkAuthorization(int uid) { * Creates {@link SecurityPolicy} which checks if the app is a device owner app. See {@link * DevicePolicyManager}. */ - @RequiresApi(18) public static io.grpc.binder.SecurityPolicy isDeviceOwner(Context applicationContext) { DevicePolicyManager devicePolicyManager = (DevicePolicyManager) applicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE); @@ -199,7 +198,6 @@ public static io.grpc.binder.SecurityPolicy isDeviceOwner(Context applicationCon * Creates {@link SecurityPolicy} which checks if the app is a profile owner app. See {@link * DevicePolicyManager}. */ - @RequiresApi(21) public static SecurityPolicy isProfileOwner(Context applicationContext) { DevicePolicyManager devicePolicyManager = (DevicePolicyManager) applicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE); diff --git a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java index f0cbe9ec56b..d195cbf7484 100644 --- a/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java +++ b/binder/src/main/java/io/grpc/binder/internal/ServiceBinding.java @@ -193,18 +193,27 @@ private static Status bindInternal( bindResult = context.bindService(bindIntent, conn, flags); break; case BIND_SERVICE_AS_USER: - bindResult = context.bindServiceAsUser(bindIntent, conn, flags, targetUserHandle); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + bindResult = context.bindServiceAsUser(bindIntent, conn, flags, targetUserHandle); + } else { + return Status.INTERNAL. + withDescription("Cross user Channel requires Android R+"); + } break; case DEVICE_POLICY_BIND_SEVICE_ADMIN: DevicePolicyManager devicePolicyManager = - (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); - bindResult = - devicePolicyManager.bindDeviceAdminServiceAsUser( - channelCredentials.getDevicePolicyAdminComponentName(), - bindIntent, - conn, - flags, - targetUserHandle); + (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + bindResult = devicePolicyManager.bindDeviceAdminServiceAsUser( + channelCredentials.getDevicePolicyAdminComponentName(), + bindIntent, + conn, + flags, + targetUserHandle); + } else { + return Status.INTERNAL. + withDescription("Device policy admin binding requires Android R+"); + } break; } if (!bindResult) { diff --git a/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java b/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java index bd51c522d15..4f19ffafb94 100644 --- a/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java +++ b/binder/src/test/java/io/grpc/binder/internal/ServiceBindingTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; import static org.robolectric.Shadows.shadowOf; import android.app.Application; @@ -29,6 +30,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ServiceInfo; +import android.os.Build; import android.os.IBinder; import android.os.Parcel; import android.os.UserHandle; @@ -327,6 +329,47 @@ public void testResolveNonExistentServiceWithTargetUserThrows() throws Exception assertThat(statusException.getStatus().getDescription()).contains("12345"); } + @Test + @Config(sdk = 30) + public void testBindService_doesNotThrowInternalErrorWhenSDkAtLeastR() { + UserHandle userHandle = mock(UserHandle.class); + binding = newBuilder().setTargetUserHandle(userHandle).build(); + binding.bind(); + shadowOf(getMainLooper()).idle(); + + assertThat(Build.VERSION.SDK_INT).isEqualTo(Build.VERSION_CODES.R); + assertThat(observer.unboundReason).isNull(); + } + + @Test + @Config(sdk = 28) + public void testBindServiceAsUser_returnsErrorWhenSDkBelowR() { + UserHandle userHandle = mock(UserHandle.class); + binding = newBuilder().setTargetUserHandle(userHandle).build(); + binding.bind(); + shadowOf(getMainLooper()).idle(); + + assertThat(observer.unboundReason.getCode()).isEqualTo(Code.INTERNAL); + assertThat(observer.unboundReason.getDescription()).isEqualTo("Cross user Channel requires Android R+"); + } + + @Test + @Config(sdk = 28) + public void testDevicePolicyBlind_returnsErrorWhenSDkBelowR() { + String deviceAdminClassName = "DevicePolicyAdmin"; + ComponentName adminComponent = new ComponentName(appContext, deviceAdminClassName); + allowBindDeviceAdminForUser(appContext, adminComponent,10); + binding = newBuilder() + .setTargetUserHandle(UserHandle.getUserHandleForUid(10)) + .setChannelCredentials(BinderChannelCredentials.forDevicePolicyAdmin(adminComponent)) + .build(); + binding.bind(); + shadowOf(getMainLooper()).idle(); + + assertThat(observer.unboundReason.getCode()).isEqualTo(Code.INTERNAL); + assertThat(observer.unboundReason.getDescription()).isEqualTo("Device policy admin binding requires Android R+"); + } + @Test @Config(sdk = 30) public void testBindWithDeviceAdmin() throws Exception { diff --git a/cronet/build.gradle b/cronet/build.gradle index 3cc86201298..0715b4129bf 100644 --- a/cronet/build.gradle +++ b/cronet/build.gradle @@ -14,7 +14,7 @@ android { namespace = 'io.grpc.cronet' compileSdkVersion 33 defaultConfig { - minSdkVersion 21 + minSdkVersion 22 targetSdkVersion 33 versionCode 1 versionName "1.0" diff --git a/lint.xml b/lint.xml new file mode 100644 index 00000000000..93e2f603108 --- /dev/null +++ b/lint.xml @@ -0,0 +1,8 @@ + + + + +