Android Setup

Learn how to implement Lens in an Android application

Getting Started

Lens is a camera framework that captures C2PA-compliant verified photos and videos. After successful capture, photos contain assertions (such as location, date and time, used to verify the provenance of the image), and are
cryptographically signed.

Requirements

  • Lens Android API key. Truepic requires the following information for production keys:
    • App name - The name of the application as listed in the Play Store.
    • Package name - The name of the package as it will be submitted to the Google Play Store.
    • Certificate digest - A short and unique representation of a certificate used to digitally sign your APK before it is installed on a device or updated. Please convert the hexadecimal fingerprint to base64 before sharing with us. See Certificate Digests to learn more.
  • Recommended Android 13, API level 33
    • Minimum of Android 9, API level 28 1.7.3+
  • Gradle 7.4.2+

Basic installation

  1. Validate the SHA256 release checksum
    Before using the Lens SDK, make sure that its SHA256 checksum is correct.
    For MacOS, run shasum -c checksum_release.txt.
    The result should be LensSdk-release-X.X.X.X.zip: OK
  2. Import the library
    Extract files from the LensSDK zip into a folder inside your Android project, for example libs/lens.
    The SDK comes with a built-in consumers-rules.pro file that contains all the necessary rules for the Proguard to work correctly.

Integration

Gradle setup

  1. Specify your folder as repository in your project build.gradle:
allprojects {
    repositories {
         // LensSDK
         maven {
            url rootProject.file("libs/lens")
        }

        // other repositories
        google()
        mavenCentral()
    }
}
  1. Then you can use LensSDK as any other dependency in your module build.gradle:
implementation 'com.truepic:lens:X.X.X.X'
  1. Add the following Lens dependency to your gradle.properties. Missing this line will cause this build error:Unsupported class file major version 59.
android.jetifier.ignorelist = bcprov-jdk18on

Manifest configuration

These steps are all required for your camera to work properly.

  1. Request Android permissions. Before opening a camera, the following permissions need to be defined in the project’s manifest file:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  1. Check permissions. Then, request and check for permissions at runtime. The camera view will not work without these and the LensError object will be raised with additional details.
Manifest.permission.CAMERA,
Manifest.permission.ACCESS_FINE_LOCATION
  1. Set application parameters. The application elements in the manifest need to include the following parameters in order for the SecuredSharedPreferences and timestamping to work correctly.
<application
   ...
   android:allowBackup="false"
   android:fullBackupContent="false"
   android:usesCleartextTraffic="true" />
  1. Set attributes. Attribute tags must be declared in the manifest file.
<activity
    ...
    android:exported="true">

Play Integrity configuration

ℹ️

Note

This section only applies to production apps distributed via the Google Play store.

Lens uses the Google Play Integrity API. This works out of the box with no configuration required for development apps, or production apps not distributed via the Google Play store. For apps in the Google Play store, Google requires a few additional steps, easily configurable in the Play Console. If not configured properly, the store will alert you.

  1. In the Play Console, navigate to the Release section of the left menu.
  2. Go to Setup > App Integrity and select the API tab.
  3. Choose an existing project or create a new project from the Google Cloud Console.
  4. Go to APIs and services and select enable APIs and services.
  5. Search for Play Integrity API. Select it and then select Enable.

Please allow about 30 minutes for the change to take effect.

React Native notes

If you're encountering difficulties integrating Lens into a React Native module inside a view, call setupLayoutHack().

fun setupLayoutHack() {
    Choreographer.getInstance().postFrameCallback(object : Choreographer.FrameCallback {
        override fun doFrame(frameTimeNanos: Long) {
            manuallyLayoutChildren()
            viewTreeObserver.dispatchOnGlobalLayout()
            Choreographer.getInstance().postFrameCallback(this)
        }
    })
}

fun manuallyLayoutChildren() {
    for (i in 0 until childCount) {
        val child = getChildAt(i)
        child.measure(MeasureSpec.makeMeasureSpec(measuredWidth, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY))
        child.layout(0, 0, child.measuredWidth, child.measuredHeight)
    }
}

Implementation

Set up camera preview

LensCameraView is provided to facilitate camera preview:

<com.truepic.lenssdk.LensCameraView
   android:id="@+id/lensCameraView"
   android:layout_width="match_parent"
   android:layout_height="match_parent" />

Add your API key

⚠️

Warning

API keys should not be stored in plain text. We recommend you store it obfuscated using NDK. See the secrets gradle plugin or read this Medium article for additional advice.

Once LensCameraView is present in one of the layouts, set up a valid API key and listeners for it. This could be done in an activity’s onCreate or fragment’s onCreateView methods (depending on where the view from above is being used).

lensCameraView.setApiKey("api key here", object : LensHandler {
    override fun onError(error: LensError) {
        // handle errors here
    }

    override fun enableCapture(cameraEnabled: Boolean) {
        // use cameraEnabled boolean
    }
}, useStrongbox)
lensCameraView.setApiKey("api key here", new LensHandler() {
    @Override
    public void onError(LensError lensError) {
        // handle errors here
    }

    @Override
    public void enableCapture(boolean cameraEnabled) {
        // use cameraEnabled boolean
    }
}, useStrongbox);
  • We recommend you enable camera capture only when cameraEnabled is true. If there’s any error that would prevent the camera from working, the app will be notified using the LensError object that contains the message and type of the error.
  • useStrongbox - Boolean - Enable or Disable hardware security StrongBox for supported devices.

Capture a photo

In order to initiate secure capture, the application needs to call the startPhotoCapture method of the LensCamera interface. First, we'll call onThumbnailCaptured, which contains a low resolution image that could be used as a preview. This image does not contain image provenance. Then, onTruepicCaptured will return the C2PA signed image. UUID could be used to update or remove previously created low resolution thumbnails.

lensCamera.startPhotoCapture(object : LensPhotoCaptureHandler {
   override fun onTruepicCaptured(uuid: UUID, truepic: Truepic) {
       // truepic captured
   }

   override fun onThumbnailCaptured(uuid: UUID, truepic: Truepic) {
       // thumbnail captured
   }
   
   override fun onBlurScoreCalculated(uuid: UUID, blurScore: float) {
       // Evaluate blur score (or ignore it)
   }
})
lensCamera.startPhotoCapture(new LensPhotoCaptureHandler() {
   @Override
   public void onTruepicCaptured(UUID uuid, Truepic truepic) {
       // truepic captured
   }

   @Override
   public void onThumbnailCaptured(UUID uuid, Truepic truepic) {
       // thumbnail captured
   }
   
   @Override
   public void onBlurScoreCalculated(UUID uuid, float blurScore) {
       // Evaluate blur score (or ignore it)
   }
});

Capture a video

⏺️

Lens API file size restriction

There are no restrictions on file size or duration to record verified videos and upload them to your own server. However, the Lens API restricts uploads to 100 MB per file. If you use the Lens API for video verification and processing, we recommend limiting recording to under 45 seconds per video.

To capture a video, the SDK will need to switch capture modes from LensCaptureMode.Photo to LensCaptureMode.Video using LensCamera.setCaptureMode(LensCaptureMode lensCaptureMode). After switching the capture mode, your application will need to call LensCamera.startRecordingVideo to start recording by providing it a LensVideoCaptureHandler.

Once recording has started you must call LensCamera.stopRecordingVideo() to stop recording. Please note that it is possible to encounter an error, which stops recording prematurely. Be sure to check for any errors if LensVideoCaptureHandler.onRecordingFinished() is raised without explicitly telling the SDK to stop the recording.

lensCamera.startRecordingVideo(object : LensVideoCaptureHandler {
    override fun onTruepicVideoCaptured(uuid: UUID, truepicVideo: TruepicVideo?) {
        // This is called when recording has finished.
    }

    override fun onThumbnailCaptured(uuid: UUID, thumbnailAsJpeg: ByteArray) {
       // This is called when a video thumbnail has been generated. The thumbnail is stored as a JPEG in the byte array.
    }

    override fun onRecordingStarted() {
        // This is called when recording has started.
    }

    override fun onUpdate(recordedDurationNanos: Long) {
        // While recording this is called periodically, use this to update your duration timer. If your user experience
        // does not include a duration timer this method can be left empty.
    }

    override fun onRecordingFinished() {
        // This is called when recording has finished. If the recording finished prematurely please check for any recent
        // errors.
    }
})
lensCamera.startRecordingVideo(new LensVideoCaptureHandler() {
    @Override
    public void onTruepicVideoCaptured(UUID uuid, @Nullable TruepicVideo truepicVideo) {
        // This is called when recording has finished.
    }

    @Override
    public void onThumbnailCaptured(UUID uuid, byte[] thumbnailAsJpeg) {
        // This is called when a video thumbnail has been generated. The thumbnail is stored as a JPEG in the byte array.
    }

    @Override
    public void onRecordingStarted() {
        // This is called when recording has started.
    }

    @Override
    public void onUpdate(long recordedDurationNanos) {
       // While recording this is called periodically, use this to update your duration timer. If your user experience 
       // does not include a duration timer this method can be left empty. 
    }

    @Override
    public void onRecordingFinished() {
       // This is called when recording has finished. If the recording finished prematurely please check for any recent
       // errors. 
    }
});

Capture an audio

In order to initiate secure audio capture, the application needs to call the startRecordingAudio method of the LensAudio interface. onTruepicAudioCaptured will return the C2PA signed audio.

lensCamera.startRecordingAudio(object : LensAudioCaptureHandler {
   override fun onTruepicAudioCaptured(uuid: UUID, truepicAudio: TruepicAudio) {
       // truepicAudio captured
   }

   override fun onAudioRecordingStarted() {
       // audio recording started
   }
   
   override fun onAudioRecordingFinished() {
       // audio recording finished
   }
})
lensCamera.startRecordingAudio(new LensAudioCaptureHandler() {
   @Override
   public void onTruepicAudioCaptured(UUID uuid, TruepicAudio truepicAudio) {
       // truepicAudio captured
   }

   @Override
   public void onAudioRecordingStarted() {
       // audio recording started
   }
   
   @Override
   public void onAudioRecordingFinished() {
       // audio recording finished
   }
});

Error Handling

The LensError object is provided whenever an error occurs while initializing and working with the camera. Each error contains three values: message, error type, and exception (optional).

Error TypeDescription
Permissions_CameraCheck permissions for accessing the camera
Permissions_LocationCheck location permissions
Permissions_MicrophoneError was encountered while accessing microphone permission
Env_AirplaneModeAirplane mode is enabled and no internet connection is available
Env_DevModeDevelopment mode or USB debugging is enabled
Env_NetworkNo internet connection is available
Env_RootedDevice is rooted, which is not allowed for controlled capture
Env_PlayStoreServicesOut of date or no play store services available on the device
Env_LocationLocation services are not available/enabled
Env_Location_Not_ReadyTemporary error while the device is determining current location
Env_Location_Not_AccurateThe location accuracy does not meet thresholds
Env_Not_Expected_LocationCurrent device location is outside of expected location (geofencing)
Env_Storage_SpaceNot enough storage space for video recording available
CameraErrors associated with using the camera
CaptureImageError
CaptureVideoError
An error has occurred while signing the image/video
RecordingVideoAn error has occurred while recording a video
Attestation
ShortValidityAttestation
LongValidityAttestation
Device integrity is compromised, more details are provided within the message
EnrollmentDevice enrollment errors
KeyGenerationErrors occurring while working and generating cryptographic keys
StrongBoxErrorNon-blocking error when strongbox is unavailable on the device
NativeErrors associated with native code
UndefinedAll other errors that might occur, please check associated message/exception
AudioGeneral audio error

Submitting your apps

Before submitting your production app to the App Store, please be sure to check that you are using:

  1. A separate Lens API key from your development app, for security. If you don't have one, please reach out to Truepic with your package name and SHA-256 fingerprint. The fingerprint may be different from your development app.
  2. The production flavor binary. For your convenience, we include a debug flavor, but this is not suitable for your production app.
  3. Play integrity is configured, see the section above.

Troubleshooting

Device is rooted error during development

If you're using an emulator like Android Studio, the good news is that it is not expected for you to be able to successfully capture or sign an image, due to our security measures that prevent this. A real, physical device must always be used.

In the standard release version of the SDK, the Android device must neither be rooted nor in developer mode. To test an app with the release version:

  1. Enable developer mode on the device
  2. Enable USB debugging
  3. Connect the device to Android Studio
  4. Deploy your app to the device
  5. Disable USB debugging on your device
  6. Disable developer mode on the device
  7. Run the app with the camera flow

During development, you can use the debugrelease version of the SDK. This version allows you to skip the above steps and leave your physical device connected to your computer and still follow logs coming from the device.