diff --git a/camera/Android.bp b/camera/Android.bp
new file mode 100644
index 0000000..5ea1559
--- /dev/null
+++ b/camera/Android.bp
@@ -0,0 +1,22 @@
+//
+// Copyright (C) 2023 ArrowOS
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+
+android_app {
+ name: "DubaiCameraService",
+ srcs: ["src/**/*.java"],
+ certificate: "platform",
+ platform_apis: true,
+ privileged: true,
+ system_ext_specific: true,
+ required: ["privapp-permissions-dubaicameraservice.xml"],
+}
+
+prebuilt_etc {
+ name: "privapp-permissions-dubaicameraservice.xml",
+ src: "privapp-permissions-dubaicameraservice.xml",
+ system_ext_specific: true,
+ sub_dir: "permissions",
+}
diff --git a/camera/AndroidManifest.xml b/camera/AndroidManifest.xml
new file mode 100644
index 0000000..26526e5
--- /dev/null
+++ b/camera/AndroidManifest.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/camera/privapp-permissions-dubaicameraservice.xml b/camera/privapp-permissions-dubaicameraservice.xml
new file mode 100644
index 0000000..0b11906
--- /dev/null
+++ b/camera/privapp-permissions-dubaicameraservice.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
diff --git a/camera/src/com/arrow/dubaicameraservice/BootReceiver.java b/camera/src/com/arrow/dubaicameraservice/BootReceiver.java
new file mode 100644
index 0000000..206385b
--- /dev/null
+++ b/camera/src/com/arrow/dubaicameraservice/BootReceiver.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2023 ArrowOS
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package com.arrow.dubaicameraservice;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class BootReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(final Context context, Intent intent) {
+ if (!intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED))
+ return;
+
+ DubaiCameraService.startService(context);
+ }
+}
diff --git a/camera/src/com/arrow/dubaicameraservice/DubaiCameraService.java b/camera/src/com/arrow/dubaicameraservice/DubaiCameraService.java
new file mode 100644
index 0000000..195039a
--- /dev/null
+++ b/camera/src/com/arrow/dubaicameraservice/DubaiCameraService.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2023 ArrowOS
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package com.arrow.dubaicameraservice;
+
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+import static android.telephony.TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER;
+import static android.telephony.TelephonyManager.NETWORK_TYPE_BITMASK_NR;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.camera2.CameraManager;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import java.util.concurrent.Executor;
+
+public class DubaiCameraService extends Service {
+
+ private static final boolean DEBUG = false;
+ private static final String TAG = "DubaiCameraService";
+
+ private static final String FRONT_CAMERA_ID = "1";
+
+ private CameraManager mCameraManager;
+ private SubscriptionManager mSubManager;
+ private TelephonyManager mTelephonyManager;
+
+ private boolean mIsFrontCamInUse = false;
+ private int mSubId = INVALID_SUBSCRIPTION_ID;
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final Executor mExecutor = new HandlerExecutor(mHandler);
+
+ private final CameraManager.AvailabilityCallback mCameraCallback =
+ new CameraManager.AvailabilityCallback() {
+ @Override
+ public void onCameraAvailable(String cameraId) {
+ dlog("onCameraAvailable id:" + cameraId);
+ if (cameraId.equals(FRONT_CAMERA_ID)) {
+ mIsFrontCamInUse = false;
+ update5gState();
+ }
+ }
+
+ @Override
+ public void onCameraUnavailable(String cameraId) {
+ dlog("onCameraUnavailable id:" + cameraId);
+ if (cameraId.equals(FRONT_CAMERA_ID)) {
+ mIsFrontCamInUse = true;
+ update5gState();
+ }
+ }
+ };
+
+ private final SubscriptionManager.OnSubscriptionsChangedListener mSubListener =
+ new SubscriptionManager.OnSubscriptionsChangedListener() {
+ @Override
+ public void onSubscriptionsChanged() {
+ dlog("onSubscriptionsChanged");
+ update5gState();
+ }
+ };
+
+ private class ActiveDataSubIdCallback extends TelephonyCallback implements
+ TelephonyCallback.ActiveDataSubscriptionIdListener {
+ @Override
+ public void onActiveDataSubscriptionIdChanged(int subId) {
+ dlog("onActiveDataSubscriptionIdChanged subId:" + subId);
+ mSubId = subId;
+ update5gState();
+ }
+ };
+
+ private final TelephonyCallback mTelephonyCallback = new ActiveDataSubIdCallback();
+
+ @Override
+ public void onCreate() {
+ dlog("onCreate");
+ mCameraManager = getSystemService(CameraManager.class);
+ mSubManager = getSystemService(SubscriptionManager.class);
+ mTelephonyManager = getSystemService(TelephonyManager.class);
+
+ mCameraManager.registerAvailabilityCallback(mCameraCallback, mHandler);
+ mTelephonyManager.registerTelephonyCallback(mExecutor, mTelephonyCallback);
+ mSubManager.addOnSubscriptionsChangedListener(mExecutor, mSubListener);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ dlog("onStartCommand");
+ return START_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ dlog("onDestroy");
+ mCameraManager.unregisterAvailabilityCallback(mCameraCallback);
+ mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback);
+ mSubManager.removeOnSubscriptionsChangedListener(mSubListener);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ public static void startService(Context context) {
+ Log.i(TAG, "Starting service");
+ context.startServiceAsUser(new Intent(context, DubaiCameraService.class),
+ UserHandle.CURRENT);
+ }
+
+ private void update5gState() {
+ if (mSubId == INVALID_SUBSCRIPTION_ID
+ || mSubManager.getActiveSubscriptionIdList().length <= 0) {
+ dlog("update5gState: Invalid subid or no active subs!");
+ return;
+ }
+ final TelephonyManager tm = mTelephonyManager.createForSubscriptionId(mSubId);
+ // Arguably we should use ALLOWED_NETWORK_TYPES_REASON_POWER here but that's already
+ // used by battery saver, and we are out of other reasons
+ long allowedNetworkTypes = tm.getAllowedNetworkTypesForReason(
+ ALLOWED_NETWORK_TYPES_REASON_CARRIER);
+ final boolean is5gAllowed = (allowedNetworkTypes & NETWORK_TYPE_BITMASK_NR) != 0;
+ dlog("update5gState mIsFrontCamInUse:" + mIsFrontCamInUse + " is5gAllowed:" + is5gAllowed);
+ if (mIsFrontCamInUse && is5gAllowed) {
+ allowedNetworkTypes &= ~NETWORK_TYPE_BITMASK_NR;
+ } else if (!mIsFrontCamInUse && !is5gAllowed) {
+ allowedNetworkTypes |= NETWORK_TYPE_BITMASK_NR;
+ } else {
+ return;
+ }
+ tm.setAllowedNetworkTypesForReason(ALLOWED_NETWORK_TYPES_REASON_CARRIER,
+ allowedNetworkTypes);
+ }
+
+ private static void dlog(String msg) {
+ if (DEBUG) Log.d(TAG, msg);
+ }
+}
diff --git a/device.mk b/device.mk
index c5adcc9..afa5475 100644
--- a/device.mk
+++ b/device.mk
@@ -55,6 +55,7 @@ PRODUCT_COPY_FILES += \
# Camera
PRODUCT_PACKAGES += \
+ DubaiCameraService \
libgui_shim_vendor
# Fingerprint