三方Camera App Framework方向控制(API2)

(1)修改Preview方向

//设置应用包名

//frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp

//createStream
binder::Status CameraDeviceClient::setStreamTransformLocked(int streamId) {
    int32_t transform = 0;
    err = getRotationTransformLocked(&transform);
}

status_t CameraDeviceClient::getRotationTransformLocked(int32_t* transform) {
    ALOGV("%s: begin", __FUNCTION__);
	
	//add start
    char value[PROPERTY_VALUE_MAX] = {'\0'};
     property_set("sys.packageName.display", String8(mClientPackageName).string());
     ALOGV("%s:    mClientPackageName: %s", __FUNCTION__, String8(mClientPackageName).string());
     //add end
    
    const CameraMetadata& staticInfo = mDevice->info();
    return CameraUtils::getRotationTransform(staticInfo, transform);
}

//获取应用包名修改orientation值

//frameworks/av/camera/CameraUtils.cpp
status_t CameraUtils::getRotationTransform(const CameraMetadata& staticInfo,
                /*out*/int32_t* transform) {
    ALOGV("%s", __FUNCTION__);

    if (transform == NULL) {
        ALOGW("%s: null transform", __FUNCTION__);
        return BAD_VALUE;
    }

    *transform = 0;
	
	//获取sensor方向
    camera_metadata_ro_entry_t entry = staticInfo.find(ANDROID_SENSOR_ORIENTATION);
    if (entry.count == 0) {
        ALOGE("%s: Can't find android.sensor.orientation in static metadata!", __FUNCTION__);
        return INVALID_OPERATION;
    }
	
	//获取facing信息
    camera_metadata_ro_entry_t entryFacing = staticInfo.find(ANDROID_LENS_FACING);
    if (entryFacing.count == 0) {
        ALOGE("%s: Can't find android.lens.facing in static metadata!", __FUNCTION__);
        return INVALID_OPERATION;
    }

    int32_t& flags = *transform;

	//前摄具有mirror效果
    bool mirror = (entryFacing.data.u8[0] == ANDROID_LENS_FACING_FRONT);
    //sensor方向
    int orientation = entry.data.i32[0];

	char value[PROPERTY_VALUE_MAX] = {'\0'};
	//获取系统应用包名
	//add start
    if (property_get("sys.packageName.display", value, NULL)) {

        if (strcmp(value,"com.skype.raider") == 0) {
            if (!mirror) {
                orientation = 90;
            } else {
                orientation = 270;
            }
        }
  }
  //add end

    if (!mirror) {
    	//表示没有mirror,一般后摄
        switch (orientation) {
            case 0:
                flags = 0;
                break;
            case 90:
                flags = NATIVE_WINDOW_TRANSFORM_ROT_90;
                break;
            case 180:
                flags = NATIVE_WINDOW_TRANSFORM_ROT_180;
                break;
            case 270:
                flags = NATIVE_WINDOW_TRANSFORM_ROT_270;
                break;
            default:
                ALOGE("%s: Invalid HAL android.sensor.orientation value: %d",
                      __FUNCTION__, orientation);
                return INVALID_OPERATION;
        }
    } else {
        // Front camera needs to be horizontally flipped for mirror-like behavior.
        // Note: Flips are applied before rotates; using XOR here as some of these flags are
        // composed in terms of other flip/rotation flags, and are not bitwise-ORable.
        switch (orientation) {
            case 0:
                flags = NATIVE_WINDOW_TRANSFORM_FLIP_H;
                break;
            case 90:
                flags = NATIVE_WINDOW_TRANSFORM_FLIP_H ^
                        NATIVE_WINDOW_TRANSFORM_ROT_270;
                break;
            case 180:
                flags = NATIVE_WINDOW_TRANSFORM_FLIP_H ^
                        NATIVE_WINDOW_TRANSFORM_ROT_180;
                break;
            case 270:
                flags = NATIVE_WINDOW_TRANSFORM_FLIP_H ^
                        NATIVE_WINDOW_TRANSFORM_ROT_90;

                break;
            default:
                ALOGE("%s: Invalid HAL android.sensor.orientation value: %d",
                      __FUNCTION__, orientation);
                return INVALID_OPERATION;
        }
    }

    /**
     * This magic flag makes surfaceflinger un-rotate the buffers
     * to counter the extra global device UI rotation whenever the user
     * physically rotates the device.
     *
     * By doing this, the camera buffer always ends up aligned
     * with the physical camera for a "see through" effect.
     *
     * In essence, the buffer only gets rotated during preview use-cases.
     * The user is still responsible to re-create streams of the proper
     * aspect ratio, or the preview will end up looking non-uniformly
     * stretched.
     */
    flags |= NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;

    ALOGV("%s: final transform = 0x%x", __FUNCTION__, flags);

    return OK;
}

(2)拦截JPEG_ORIENTATION,修改拍照方向

//通过拦截request当中的JPEG_ORIENTATION参数来进行定制修改
//frameworks/base/core/java/android/hardware/camera2/CaptureRequest
 
public <T> void set(@NonNull Key<T> key, T value) {
    //新增如下判断,这里以com.whatsapp为例
        if(key != null && JPEG_ORIENTATION.equals(key)){
            Log.d("request set", "key = " + key + ", value = " + value);
            if(mPackageName != null && mPackageName.equals("com.whatsapp")) {
                String cameraId = CameraManager.getCameraId();
                if(cameraId != null && "0".equals(cameraId)){
                    value = (T)(Object)90;
                }else{
                    value = (T)(Object)270;
                }
            }
        }
    mRequest.mLogicalCameraSettings.set(key, value);
}


//frameworks/base/core/java/android/hardware/camera2/CameraManager.java

public void openCameraForUid(@NonNull String cameraId,
            @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor,
            int clientUid)
            throws CameraAccessException {

        if (cameraId == null) {
            throw new IllegalArgumentException("cameraId was null");
        } else if (callback == null) {
            throw new IllegalArgumentException("callback was null");
        }
        if (CameraManagerGlobal.sCameraServiceDisabled) {
            throw new IllegalArgumentException("No cameras available on device");
        }

++        mCameraId = cameraId;
++       Log.d(TAG, "openCameraForUid cameraId = " + cameraId);
        openCameraDeviceUserAsync(cameraId, callback, executor, clientUid);
    }

++    private static volatile String mCameraId;

    /**
     * @hide
     */
    @Nullable
++    public static String getCameraId() {
        return mCameraId;
    }

    /**
     * @hide
     */
++    public static void setCameraId(@Nullable String cameraId) {
        mCameraId = cameraId;
    }

(3)修改App拿到的SENSOR_ORIENTATION方向

//frameworks/base/core/java/android/hardware/camera2/CameraCharacteristics.java

@PublicKey
    @NonNull
    public static final Key<Integer> SENSOR_ORIENTATION =
            new Key<Integer>("android.sensor.orientation", int.class);

@Nullable
    public <T> T get(Key<T> key) {
        Log.d("request get",  "Activity: " + ActivityThread.currentActivityThread().currentActivityName() + " ,key = " + key);
            if(key != null && SENSOR_ORIENTATION.equals(key)){
            	//这里以whatsapp为例
                if(ActivityThread.currentOpPackageName().contains("com.whatsapp")){
                    String cameraId = CameraManager.getCameraId();
                    if(cameraId != null && "0".equals(cameraId)){
                        T value = (T)(Object)90;
                        Log.d("request get 0",  "value = " + value + " ,ori = " + mProperties.get(key));
                        return value;
                    }else{
                        T value = (T)(Object)270;
                        Log.d("request get 1",  "value = " + value + " ,ori = " + mProperties.get(key));
                        return value;
                    }
                }
            }
        return mProperties.get(key);
    }

(4)拦截App拿到的Perview Size和Picture Size列表

//frameworks/base/core/java/android/hardware/camera2/params/StreamConfigurationMap.java

//获取preview size列表(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED)
public <T> Size[] getOutputSizes(Class<T> klass) {
        if (isOutputSupportedFor(klass) == false) {
            return null;
        }

        Size[] sizes = getInternalFormatSizes(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
                HAL_DATASPACE_UNKNOWN,/*output*/true, /*highRes*/false);

            if(ActivityThread.currentOpPackageName().contains("com.whatsapp")){

                if(sizes != null){
                    Size[] temp = new Size[sizes.length];
                    int index = 0;
                    for(int i = 0; i < sizes.length; i++){
                    	//只留下1:1比例的size
                        if((sizes[i].getWidth() / (sizes[i].getHeight() * 1.0)) - 1 < 0.02){
                            temp[index++] = sizes[i];
                            Log.i("getOutputSizes preview","index = " + index);
                        }
                    }

                    if (index <= 0) {
                        sizes = new Size[0];
                    } else {
                        sizes = Arrays.copyOf(temp, index);
                    }

                    for(int i = 0; i < sizes.length; i++){
                        Log.i("getOutputSizes preview","size = " + sizes[i].toString());
                    }
                }
            }

        return sizes;
    }

//获取其他Size列表
//ImageFormat.JPEG  = 256
//ImageFormat.YUV_420_888 = 35

 public Size[] getOutputSizes(int format) {
        return getPublicFormatSizes(format, /*output*/true, /*highRes*/ false);
    }

(5)Display影响Preview方向

在Display.java相关位置针对横屏进行修改,达到类似于横屏竖用的状态。

//frameworks/base/core/java/android/view/Display.java

public int getRotation() {
        synchronized (this) {
            updateDisplayInfoLocked();

//add start
            if (SystemProperties.getBoolean("ro.rotation.display", false)) {
                    int mRotation =  (mMayAdjustByFixedRotation
                                ? getDisplayAdjustments().getRotation(mDisplayInfo.rotation)
                                : mDisplayInfo.rotation);
                    return (mRotation + 1 ) % 4;
            }
 //and end

            return mMayAdjustByFixedRotation
                        ? getDisplayAdjustments().getRotation(mDisplayInfo.rotation)
                        : mDisplayInfo.rotation;
        }
}

(6)SystemSensorManager影响拍照角度

在SystemSensorManager.java中来拦截Gsensor的值,并对非系统应用(android包名)进行xy值对调,此处存在三方应用包名包含android字段的风险,导致拍照图片方向异常。

//frameworks/base/core/java/android/hardware/SystemSensorManager.java

protected void dispatchSensorEvent(int handle, float[] values, int inAccuracy,
                                           long timestamp) {
            final Sensor sensor = mManager.mHandleToSensor.get(handle);
            if (sensor == null) {
                // sensor disconnected
                return;
            }

//add start
        if (SystemProperties.getBoolean("ro.rotation.display", false)) {

                if(!ActivityThread.currentOpPackageName().contains("android") 
                    || (ActivityThread.currentOpPackageName().contains("jp.naver.line.android"))){  //特例应用
                    if(sensor.getType() == Sensor.TYPE_ACCELEROMETER ){
                        Log.d("wdd111_SensorEventQueue","x = " + values[0] + ",y = " + values[1] + ",z = " + values[2]);
	                    if (sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
	                        float tmp;
	                        tmp = values[0];
	                        values[0] = values[1];
	                        values[1] = -tmp;
        	            }
                    }
                }

			//修改带陀螺仪的应用问题,这里以腾讯游戏和平精英作为测试
			if(ActivityThread.currentOpPackageName().contains("com.tencent.tmgp")){
			if(sensor.getType() == Sensor.TYPE_GYROSCOPE){
				Log.d("wdd222_SensorEventQueue_gyro","x = " + values[0] + ",y = " + values[1] + ",z = " + values[2]);
                                tmp_gyro = values[0];
                                values[0] = values[1];
                                values[1] = -tmp_gyro;
			}
           	}
            }
//add end 
}           

(7)拦截系统的OrientationEventListener,修改App拿到的Orientation值

//frameworks/base/core/java/android/view/OrientationEventListener.java

+	import android.app.ActivityThread;
public abstract class OrientationEventListener {

	private int mOrientation = ORIENTATION_UNKNOWN;
	private SensorManager mSensorManager;
	private Sensor mSensor;
	private SensorEventListener mSensorEventListener;

	if (mOldListener != null) {
		mOldListener.onSensorChanged(Sensor.TYPE_ACCELEROMETER, event.values);
	}
	if (orientation != mOrientation) {
		mOrientation = orientation;
+		if(ActivityThread.currentOpPackageName().equals("com.tencent.mobileqq")){
+			orientation += 90;
+		}
		onOrientationChanged(orientation);
	}
}

(8)强制应用横竖屏

//frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

	private boolean updateOrientation(boolean forceUpdate){
		final int orientation = getOrientation();
		//可以通过包名和类名来修改此处的orientation值,从而达到固定横竖屏的作用

		return mDisplayRotation.updateOrientation(orientation, forceUpdate);
	}

//或者

//frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java

    int rotationForOrientation(@ScreenOrientation int orientation,
            @Surface.Rotation int lastRotation) {
    
		switch (orientation) {
            case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
                // Return portrait unless overridden.
                if (isAnyPortrait(preferredRotation)) {
                    return preferredRotation;
                }
				
				//add start
                if (SystemProperties.getBoolean("ro.camera.spin", false)) {
                    if(ActivityThread.currentActivityThread().currentActivityName().contains("com.android.incallui.InCallActivity")){
                        mPortraitRotation = 0;
                    } else {
                        mPortraitRotation = 3;
                    }
                }
                //add end

                return mPortraitRotation;
}

以上修改可通过获取当前应用包名和类名来进行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雪舞飞影

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值