Skip to content

Commit 67d35a7

Browse files
authored
Merge pull request #4 from ekoindia/feat/aadhaar-face-rd-service
2 parents 292f4fc + bb7ceac commit 67d35a7

3 files changed

Lines changed: 380 additions & 0 deletions

File tree

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package in.eko.uidai_rdservice_manager_lib;
2+
import android.app.Activity;
3+
import android.content.Context;
4+
import android.content.Intent;
5+
import android.util.Log;
6+
import android.net.Uri;
7+
import androidx.annotation.NonNull;
8+
import static android.app.Activity.RESULT_OK;
9+
10+
/**
11+
* Helper class to capture face data using UIDAI Face RD Service
12+
*/
13+
public class FaceRDServiceManager {
14+
15+
private static final String TAG = "FaceRDServiceManager";
16+
private static final String FACE_RD_PACKAGE = "in.gov.uidai.facerd";
17+
18+
private final RDServiceEvents mRDEvent;
19+
private final Activity activity;
20+
private final Context context;
21+
22+
/* ---------------- CONSTRUCTOR ---------------- */
23+
24+
private FaceRDServiceManager(@NonNull Builder builder) {
25+
this.mRDEvent = builder.event;
26+
this.activity = (Activity) builder.event; // Activity implements RDServiceEvents
27+
this.context = this.activity;
28+
}
29+
30+
/* ---------------- BUILDER ---------------- */
31+
32+
public static class Builder {
33+
private final RDServiceEvents event;
34+
35+
public Builder(@NonNull RDServiceEvents eventActivity) {
36+
this.event = eventActivity;
37+
}
38+
39+
public FaceRDServiceManager create() {
40+
return new FaceRDServiceManager(this);
41+
}
42+
}
43+
44+
45+
private static final int RD_SERVICE_RESPONSE_FACE = 1002;
46+
47+
/**
48+
* capture Face RD Service with given PID options
49+
*/
50+
public void captureFaceRdService(@NonNull String rd_service_package, @NonNull String pid_options) {
51+
52+
Log.d(TAG, "captureFaceRdService: Starting Face RD flow" + rd_service_package + pid_options);
53+
54+
try {
55+
Intent intent = new Intent(rd_service_package);
56+
intent.putExtra("request", pid_options);
57+
activity.startActivityForResult(intent, RD_SERVICE_RESPONSE_FACE);
58+
59+
} catch (Exception e) {
60+
Log.w(TAG, "Face RD app not installed. Redirecting to Play Store.", e);
61+
62+
// Redirect to Play Store
63+
try {
64+
Intent playStoreIntent = new Intent(Intent.ACTION_VIEW,
65+
Uri.parse("market://details?id=" + FACE_RD_PACKAGE));
66+
playStoreIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
67+
context.startActivity(playStoreIntent);
68+
69+
} catch (Exception ex) {
70+
// Play Store not available → open in browser
71+
Intent browserIntent = new Intent(Intent.ACTION_VIEW,
72+
Uri.parse("https://play.google.com/store/apps/details?id=" + FACE_RD_PACKAGE));
73+
browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
74+
context.startActivity(browserIntent);
75+
}
76+
77+
// Notify UI / WebView
78+
mRDEvent.onRDServiceDriverNotFound();
79+
}
80+
}
81+
82+
/**
83+
* Handle the result from Face RD Service capture intent and send response back to UI / WebView
84+
*/
85+
public void onActivityResult(int requestCode, int resultCode, Intent data) {
86+
Log.d(TAG, "handleActivityResult called - requestCode: " + requestCode + ", resultCode: " + resultCode + ", data: " + (data != null ? "present" : "null"));
87+
88+
if (requestCode != RD_SERVICE_RESPONSE_FACE) {
89+
Log.d(TAG, "Request code doesn't match. Expected: " + RD_SERVICE_RESPONSE_FACE + ", Got: " + requestCode);
90+
return;
91+
}
92+
93+
if (resultCode == Activity.RESULT_OK && data != null) {
94+
String pidData = data.getStringExtra("response");
95+
mRDEvent.onRDServiceCaptureResponse(pidData, FACE_RD_PACKAGE);
96+
97+
} else {
98+
Log.e(TAG, "Face RD capture failed or data is null");
99+
if (data == null) {
100+
Log.e(TAG, "Intent data is NULL");
101+
}
102+
mRDEvent.onRDServiceCaptureFailed(resultCode, data, FACE_RD_PACKAGE);
103+
}
104+
}
105+
}
Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
package in.eko.uidai_rdservice_manager_lib;
2+
3+
import android.app.Activity;
4+
import android.content.Intent;
5+
import android.content.pm.ResolveInfo;
6+
import android.os.Bundle;
7+
import android.util.Log;
8+
9+
import androidx.annotation.NonNull;
10+
11+
import org.json.JSONException;
12+
import org.json.JSONObject;
13+
14+
import java.util.HashMap;
15+
import java.util.List;
16+
import java.util.Map;
17+
18+
import static android.app.Activity.RESULT_OK;
19+
20+
21+
public class IrisRDServiceManager {
22+
23+
private static final String TAG = "IrisRDServiceManager";
24+
private RDServiceEvents mRDEvent;
25+
26+
private static final int RC_RDSERVICE_DISCOVER_START_INDEX = 7500;
27+
private static final int RC_RDSERVICE_CAPTURE_START_INDEX = 7300;
28+
29+
private static final Map<String, Integer> mapRDDriverRCIndex = new HashMap<String, Integer>();
30+
private static final Map<Integer, String> mapRDDiscoverRC = new HashMap<Integer, String>();
31+
private static final Map<Integer, String> mapRDCaptureRC = new HashMap<Integer, String>();
32+
33+
private static final Map<String, String> mapRDDriverWhitelist = new HashMap<String, String>() {
34+
{
35+
// Mantra Softech
36+
put("com.mantra.mis100.rdservice", "MIS100V2");
37+
}
38+
};
39+
40+
private static final Map<String, String> mapRDDriverBlacklist = new HashMap<String, String>();
41+
42+
43+
private IrisRDServiceManager(@NonNull final Builder builder) {
44+
mRDEvent = builder._rdevent;
45+
mapRDDriverWhitelist.putAll(builder.mapNewWhitelistedRDDrivers);
46+
mapRDDriverBlacklist.putAll(builder.mapBlacklistedRDDrivers);
47+
}
48+
49+
50+
/**
51+
* Builder class to create a configured instance of IrisRDServiceManager.
52+
*/
53+
public static class Builder {
54+
private RDServiceEvents _rdevent = null;
55+
private Map<String, String> mapNewWhitelistedRDDrivers = new HashMap<String, String>();
56+
private Map<String, String> mapBlacklistedRDDrivers = new HashMap<String, String>();
57+
58+
/**
59+
* Constructor for the builder.
60+
*
61+
* @param eventActivity Reference to Activity that has implemented RDServiceEvents interface so that it can receive callback when RDService drivers are discovered or when iris is captured.
62+
*/
63+
public Builder(@NonNull final RDServiceEvents eventActivity) {
64+
_rdevent = eventActivity;
65+
}
66+
67+
/**
68+
* Whitelist one or more RDService drivers. The response after driver discovery will contain a flag whether the discovered driver is whitelisted or not. However, it does not stop you from using a driver that is not already whitelisted. Note that a few popular drivers (eg: IriTech, Aware, Iridian) are already whitelisted.
69+
* @param mapNewWhitelistedRDDrivers A Map of new whitelisted drivers containing PlayStore package names and an optional label for each driver.
70+
* @return A reference to this builder to easily chain other builder methods.
71+
*/
72+
public Builder whitelistRDDrivers(@NonNull final Map<String, String> mapNewWhitelistedRDDrivers) {
73+
this.mapNewWhitelistedRDDrivers = mapNewWhitelistedRDDrivers;
74+
return this;
75+
}
76+
77+
/**
78+
* Blacklist one or more RDService drivers. These drivers will be ignored during driver discovery and will not be allowed to capture from.
79+
* @param mapBlacklistedRDDrivers A Map of blacklisted drivers containing PlayStore package names and an optional label for each driver.
80+
* @return A reference to this builder to easily chain other builder methods.
81+
*/
82+
public Builder blacklistRDDrivers(@NonNull final Map<String, String> mapBlacklistedRDDrivers) {
83+
this.mapBlacklistedRDDrivers = mapBlacklistedRDDrivers;
84+
return this;
85+
}
86+
87+
/**
88+
* Create and return a configured instance of IrisRDServiceManager.
89+
* @return An instance of IrisRDServiceManager.
90+
*/
91+
public IrisRDServiceManager create() {
92+
if (_rdevent == null) {
93+
throw new IllegalStateException("First set your Activity that implements RDServiceEvent by calling setRDServiceEventActivity()");
94+
}
95+
return new IrisRDServiceManager(this);
96+
}
97+
}
98+
99+
100+
/**
101+
* Dispatch onActivityResult here from the implementing Activity.
102+
*/
103+
public void onActivityResult(@NonNull int requestCode, @NonNull int resultCode, @NonNull Intent data) {
104+
105+
Log.i(TAG, "onActivityResult: " + requestCode + ", " + resultCode + " ~~ " + mapRDDiscoverRC.toString());
106+
107+
if (mapRDDiscoverRC.containsKey(requestCode)) {
108+
String rdservice_pkg_name = mapRDDiscoverRC.get(requestCode);
109+
if (resultCode == RESULT_OK) {
110+
onRDServiceInfoResponse(data, rdservice_pkg_name); // RDService Info Received
111+
} else {
112+
mRDEvent.onRDServiceDriverDiscoveryFailed(resultCode, data, rdservice_pkg_name, ""); // RDService Info Failed
113+
}
114+
} else if (mapRDCaptureRC.containsKey(requestCode)) {
115+
String rdservice_pkg_name = mapRDCaptureRC.get(requestCode);
116+
if (resultCode == RESULT_OK) {
117+
onRDServiceCaptureIntentResponse(data, rdservice_pkg_name); // Iris Captured
118+
} else {
119+
mRDEvent.onRDServiceCaptureFailed(resultCode, data, rdservice_pkg_name); // Iris Capture Failed
120+
}
121+
}
122+
}
123+
124+
125+
/**
126+
* Initiate discovery of installed RDService drivers on current device. For every discovered driver, the onRDServiceDriverDiscovery() will be called.
127+
*/
128+
public void discoverRdService() {
129+
130+
mapRDDriverRCIndex.clear();
131+
mapRDDiscoverRC.clear();
132+
mapRDCaptureRC.clear();
133+
134+
Intent intentServiceList = new Intent("in.gov.uidai.rdservice.iris.INFO");
135+
List<ResolveInfo> resolveInfoList = ((Activity) mRDEvent).getPackageManager().queryIntentActivities(intentServiceList, 0);
136+
137+
// String packageNamesStr = "";
138+
139+
if (resolveInfoList.isEmpty()) {
140+
mRDEvent.onRDServiceDriverNotFound();
141+
return;
142+
}
143+
144+
int iInfo = 0;
145+
for (ResolveInfo resolveInfo : resolveInfoList) {
146+
String _pkg = resolveInfo.activityInfo.packageName;
147+
148+
if (!mapRDDriverBlacklist.containsKey(_pkg)) {
149+
try {
150+
// Assign an index to current RDService driver
151+
int next_rdservice_index = mapRDDriverRCIndex.size() + 1;
152+
mapRDDriverRCIndex.put(_pkg, next_rdservice_index);
153+
154+
// Calculate and map request-code for the current RDService GetInfo Intent
155+
int next_discover_rc_index = getRDServiceDiscoverRC(next_rdservice_index);
156+
mapRDDiscoverRC.put(next_discover_rc_index, _pkg);
157+
158+
// Calculate and map request-code for the current RDService Capture Intent
159+
int next_capture_rc_index = getRDServiceCaptureRC(next_rdservice_index);
160+
mapRDCaptureRC.put(next_capture_rc_index, _pkg);
161+
162+
// Get RD Service Info..
163+
Intent intentInfo = new Intent("in.gov.uidai.rdservice.iris.INFO");
164+
intentInfo.setPackage(_pkg);
165+
((Activity) mRDEvent).startActivityForResult(intentInfo, next_discover_rc_index);
166+
167+
Log.e(TAG, "RD SERVICE Package Found: (" + next_rdservice_index + ") " + next_discover_rc_index + " ~ " + _pkg + " ~~ " + mapRDDriverRCIndex + " ~ " + mapRDDiscoverRC);
168+
} catch (Exception e) {
169+
e.printStackTrace();
170+
mRDEvent.onRDServiceDriverDiscoveryFailed(0, null, _pkg, e.getMessage());
171+
}
172+
} else {
173+
mRDEvent.onRDServiceDriverDiscoveryFailed(0, null, _pkg, "Package not whitelisted");
174+
}
175+
176+
177+
++iInfo;
178+
179+
// Limit max installed driver discovery count
180+
if (iInfo > 10) {
181+
break;
182+
}
183+
}
184+
}
185+
186+
187+
/**
188+
* Initiate iris capture.
189+
* @param rd_service_package The package name of the active RDService driver to be used for capture.
190+
* @param pid_options The PID-Options XML string to configure the RDService driver as per Aadhaar Registered Devices Specification v2.0 by UIDAI (https://uidai.gov.in/images/resource/Aadhaar_Registered_Devices_2_0_4.pdf).
191+
*/
192+
public void captureRdService(@NonNull String rd_service_package, @NonNull String pid_options) {
193+
194+
if (mapRDDriverRCIndex.containsKey(rd_service_package)) {
195+
int capture_rc_index = mapRDDriverRCIndex.get(rd_service_package);
196+
int capture_rc = getRDServiceCaptureRC(capture_rc_index);
197+
198+
Log.d(TAG, "RDSERVICE BEFORE CAPTURE: pid_options: (" + capture_rc_index + ") " + capture_rc + " ~ " + pid_options);
199+
200+
// Capture iris using RD Service
201+
Intent intentCapture = new Intent("in.gov.uidai.rdservice.iris.CAPTURE");
202+
intentCapture.setPackage(rd_service_package);
203+
intentCapture.putExtra("PID_OPTIONS", pid_options);
204+
((Activity) mRDEvent).startActivityForResult(intentCapture, capture_rc);
205+
} else {
206+
mRDEvent.onRDServiceDriverDiscoveryFailed(0, null, rd_service_package, "Package not found or not whitelisted");
207+
Log.d(TAG, "RDSERVICE CAPTURE ERROR: package not found or not whitelisted: " + rd_service_package);
208+
}
209+
}
210+
211+
212+
/**
213+
* Process response RDService driver status info.
214+
* TODO: return parsed data
215+
* @param data Intent response returned from RDService driver activity
216+
* @param rd_service_package The package name of the RDService driver
217+
*/
218+
private void onRDServiceInfoResponse(@NonNull Intent data, @NonNull String rd_service_package) {
219+
220+
Bundle b = data.getExtras();
221+
222+
if (b != null) {
223+
// sendWebViewResponse("rdservice_info", b.getString("RD_SERVICE_INFO", "") + "<RD_SERVICE_ANDROID_PACKAGE=\"" + rd_service_package + "\" />");
224+
225+
Log.d(TAG, "onRDServiceInfoResponse: " + b.getString("RD_SERVICE_INFO", "") + " //// " + rd_service_package);
226+
227+
mRDEvent.onRDServiceDriverDiscovery(b.getString("RD_SERVICE_INFO", ""), rd_service_package, mapRDDriverWhitelist.containsKey(rd_service_package));
228+
229+
Log.i(TAG, "onRDServiceInfoResponse: Device Info: \n\n Device = " + b.getString("DEVICE_INFO", "") + " \n\nRDService = " + b.getString("RD_SERVICE_INFO", ""));
230+
}
231+
}
232+
233+
234+
/**
235+
* Process response RDService driver status info.
236+
* @param data Intent response returned from RDService driver activity
237+
* @param rd_service_package The package name of the RDService driver
238+
*/
239+
private void onRDServiceCaptureIntentResponse(@NonNull Intent data, @NonNull String rd_service_package) {
240+
241+
Bundle b = data.getExtras();
242+
243+
if (b != null) {
244+
// sendWebViewResponse("rdservice_resp", b.getString("PID_DATA", ""));
245+
mRDEvent.onRDServiceCaptureResponse(b.getString("PID_DATA", ""), rd_service_package);
246+
247+
Log.i(TAG, "onRDServiceCaptureIntentResponse: Capture Info: \n\n PID-DATA = " + b.getString("PID_DATA", "") + " \n\nDeviceNotConnected = " + b.getString("DNC", ""));
248+
}
249+
}
250+
251+
252+
/**
253+
* Generate and return the next result-code for RDService driver discovery
254+
* @param index Index of the RDService driver. Usually last used index + 1.
255+
* @return The result-code
256+
*/
257+
private int getRDServiceDiscoverRC(@NonNull int index) {
258+
return RC_RDSERVICE_DISCOVER_START_INDEX + index;
259+
}
260+
261+
/**
262+
* Generate and return the next result-code for RDService iris capture
263+
* @param index Index of the RDService driver. Usually last used index + 1.
264+
* @return The result-code
265+
*/
266+
private int getRDServiceCaptureRC(@NonNull int index) {
267+
return RC_RDSERVICE_CAPTURE_START_INDEX + index;
268+
}
269+
270+
}

uidai-rdservice-manager-lib/src/main/java/in/eko/uidai_rdservice_manager_lib/RDServiceManager.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ public void onActivityResult(@NonNull int requestCode, @NonNull int resultCode,
139139
* Initiate discovery of installed RDService drivers on current device. For every discovered driver, the onRDServiceDriverDiscovery() will be called.
140140
*/
141141
public void discoverRdService() {
142+
143+
mapRDDriverRCIndex.clear();
144+
mapRDDiscoverRC.clear();
145+
mapRDCaptureRC.clear();
146+
142147
Intent intentServiceList = new Intent("in.gov.uidai.rdservice.fp.INFO");
143148
List<ResolveInfo> resolveInfoList = ((Activity) mRDEvent).getPackageManager().queryIntentActivities(intentServiceList, 0);
144149

0 commit comments

Comments
 (0)