blob: b58975f2344071fee77456da0e8d16515c4a3a5a [file] [log] [blame]
Shuzhen Wang316781a2020-08-18 18:11:01 -07001/*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "CameraServiceProxyWrapper"
18#define ATRACE_TAG ATRACE_TAG_CAMERA
19//#define LOG_NDEBUG 0
20
21#include <inttypes.h>
22#include <utils/Log.h>
23#include <binder/IServiceManager.h>
24
25#include "CameraServiceProxyWrapper.h"
26
27namespace android {
28
Avichal Rakesh6e57a2b2023-05-01 17:53:37 -070029using hardware::CameraExtensionSessionStats;
Shuzhen Wang316781a2020-08-18 18:11:01 -070030using hardware::CameraSessionStats;
Avichal Rakesh6e57a2b2023-05-01 17:53:37 -070031using hardware::ICameraServiceProxy;
32
33namespace {
34// Sentinel value to be returned when extension session with a stale or invalid key is reported.
35const String16 POISON_EXT_STATS_KEY("poisoned_stats");
36} // anonymous namespace
Shuzhen Wang316781a2020-08-18 18:11:01 -070037
Shuzhen Wang316781a2020-08-18 18:11:01 -070038/**
39 * CameraSessionStatsWrapper functions
40 */
41
Austin Borger74fca042022-05-23 12:41:21 -070042void CameraServiceProxyWrapper::CameraSessionStatsWrapper::updateProxyDeviceState(
43 sp<hardware::ICameraServiceProxy>& proxyBinder) {
44 if (proxyBinder == nullptr) return;
45 proxyBinder->notifyCameraState(mSessionStats);
Shuzhen Wang316781a2020-08-18 18:11:01 -070046}
47
Austin Borger74fca042022-05-23 12:41:21 -070048void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onOpen(
49 sp<hardware::ICameraServiceProxy>& proxyBinder) {
50 Mutex::Autolock l(mLock);
51 updateProxyDeviceState(proxyBinder);
52}
53
54void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onClose(
Shuzhen Wang03fe6232023-02-05 12:41:15 -080055 sp<hardware::ICameraServiceProxy>& proxyBinder, int32_t latencyMs,
56 bool deviceError) {
Shuzhen Wang316781a2020-08-18 18:11:01 -070057 Mutex::Autolock l(mLock);
58
59 mSessionStats.mNewCameraState = CameraSessionStats::CAMERA_STATE_CLOSED;
60 mSessionStats.mLatencyMs = latencyMs;
Shuzhen Wang03fe6232023-02-05 12:41:15 -080061 mSessionStats.mDeviceError = deviceError;
Avichal Rakesh89691e12023-05-01 19:41:02 -070062 mSessionStats.mSessionIndex = 0;
Austin Borger74fca042022-05-23 12:41:21 -070063 updateProxyDeviceState(proxyBinder);
Shuzhen Wang316781a2020-08-18 18:11:01 -070064}
65
66void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onStreamConfigured(
67 int operatingMode, bool internalReconfig, int32_t latencyMs) {
68 Mutex::Autolock l(mLock);
69
70 if (internalReconfig) {
71 mSessionStats.mInternalReconfigure++;
72 } else {
73 mSessionStats.mLatencyMs = latencyMs;
74 mSessionStats.mSessionType = operatingMode;
75 }
76}
77
Austin Borger74fca042022-05-23 12:41:21 -070078void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onActive(
79 sp<hardware::ICameraServiceProxy>& proxyBinder, float maxPreviewFps) {
Shuzhen Wang316781a2020-08-18 18:11:01 -070080 Mutex::Autolock l(mLock);
81
82 mSessionStats.mNewCameraState = CameraSessionStats::CAMERA_STATE_ACTIVE;
Austin Borger4a870a32022-02-25 01:48:41 +000083 mSessionStats.mMaxPreviewFps = maxPreviewFps;
Avichal Rakesh89691e12023-05-01 19:41:02 -070084 mSessionStats.mSessionIndex++;
Austin Borger74fca042022-05-23 12:41:21 -070085 updateProxyDeviceState(proxyBinder);
Shuzhen Wang316781a2020-08-18 18:11:01 -070086
87 // Reset mCreationDuration to -1 to distinguish between 1st session
88 // after configuration, and all other sessions after configuration.
89 mSessionStats.mLatencyMs = -1;
90}
91
92void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onIdle(
Austin Borger74fca042022-05-23 12:41:21 -070093 sp<hardware::ICameraServiceProxy>& proxyBinder,
Shuzhen Wang316781a2020-08-18 18:11:01 -070094 int64_t requestCount, int64_t resultErrorCount, bool deviceError,
Shuzhen Wang9372b0b2022-05-11 18:55:19 -070095 const std::string& userTag, int32_t videoStabilizationMode,
Shuzhen Wang316781a2020-08-18 18:11:01 -070096 const std::vector<hardware::CameraStreamStats>& streamStats) {
97 Mutex::Autolock l(mLock);
98
99 mSessionStats.mNewCameraState = CameraSessionStats::CAMERA_STATE_IDLE;
100 mSessionStats.mRequestCount = requestCount;
101 mSessionStats.mResultErrorCount = resultErrorCount;
102 mSessionStats.mDeviceError = deviceError;
Shuzhen Wangd26b1862022-03-07 12:05:05 -0800103 mSessionStats.mUserTag = String16(userTag.c_str());
Shuzhen Wang9372b0b2022-05-11 18:55:19 -0700104 mSessionStats.mVideoStabilizationMode = videoStabilizationMode;
Shuzhen Wang316781a2020-08-18 18:11:01 -0700105 mSessionStats.mStreamStats = streamStats;
Avichal Rakesh6e57a2b2023-05-01 17:53:37 -0700106
Austin Borger74fca042022-05-23 12:41:21 -0700107 updateProxyDeviceState(proxyBinder);
Shuzhen Wang316781a2020-08-18 18:11:01 -0700108
109 mSessionStats.mInternalReconfigure = 0;
110 mSessionStats.mStreamStats.clear();
Avichal Rakesh6e57a2b2023-05-01 17:53:37 -0700111 mSessionStats.mCameraExtensionSessionStats = {};
Shuzhen Wang316781a2020-08-18 18:11:01 -0700112}
113
Avichal Rakesh1fff2d12023-03-03 15:05:48 -0800114int64_t CameraServiceProxyWrapper::CameraSessionStatsWrapper::getLogId() {
115 Mutex::Autolock l(mLock);
116 return mSessionStats.mLogId;
117}
118
Avichal Rakesh6e57a2b2023-05-01 17:53:37 -0700119String16 CameraServiceProxyWrapper::CameraSessionStatsWrapper::updateExtensionSessionStats(
120 const hardware::CameraExtensionSessionStats& extStats) {
121 Mutex::Autolock l(mLock);
122 CameraExtensionSessionStats& currStats = mSessionStats.mCameraExtensionSessionStats;
123 if (currStats.key != extStats.key) {
124 // Mismatched keys. Extensions stats likely reported for a closed session
125 ALOGW("%s: mismatched extensions stats key: current='%s' reported='%s'. Dropping stats.",
126 __FUNCTION__, String8(currStats.key).c_str(), String8(extStats.key).c_str());
127 return POISON_EXT_STATS_KEY; // return poisoned key to so future calls are
128 // definitely dropped.
129 }
130
131 // Matching keys...
132 if (currStats.key.size()) {
133 // non-empty matching keys. overwrite.
134 ALOGV("%s: Overwriting extension session stats: %s", __FUNCTION__,
135 extStats.toString().c_str());
136 currStats = extStats;
137 return currStats.key;
138 }
139
140 // Matching empty keys...
141 if (mSessionStats.mClientName != extStats.clientName) {
142 ALOGW("%s: extension stats reported for unexpected package: current='%s' reported='%s'. "
143 "Dropping stats.", __FUNCTION__,
144 String8(mSessionStats.mClientName).c_str(),
145 String8(extStats.clientName).c_str());
146 return POISON_EXT_STATS_KEY;
147 }
148
149 // Matching empty keys for the current client...
150 if (mSessionStats.mNewCameraState == CameraSessionStats::CAMERA_STATE_OPEN ||
151 mSessionStats.mNewCameraState == CameraSessionStats::CAMERA_STATE_IDLE) {
152 // Camera is open, but not active. It is possible that the active callback hasn't
153 // occurred yet. Keep the stats, but don't associate it with any session.
154 ALOGV("%s: extension stat reported for an open, but not active camera. "
155 "Saving stats, but not generating key.", __FUNCTION__);
156 currStats = extStats;
157 return {}; // Subsequent calls will handle setting the correct key.
158 }
159
160 if (mSessionStats.mNewCameraState == CameraSessionStats::CAMERA_STATE_ACTIVE) {
161 // camera is active. First call for the session!
162 currStats = extStats;
163
164 // Generate a new key from logId and sessionIndex.
165 std::ostringstream key;
166 key << mSessionStats.mSessionIndex << '/' << mSessionStats.mLogId;
167 currStats.key = String16(key.str().c_str());
168 ALOGV("%s: New extension session stats: %s", __FUNCTION__, currStats.toString().c_str());
169 return currStats.key;
170 }
171
172 // Camera is closed. Probably a stale call.
173 ALOGW("%s: extension stats reported for closed camera id '%s'. Dropping stats.",
174 __FUNCTION__, String8(mSessionStats.mCameraId).c_str());
175 return {};
176}
177
Shuzhen Wang316781a2020-08-18 18:11:01 -0700178/**
179 * CameraServiceProxyWrapper functions
180 */
181
182sp<ICameraServiceProxy> CameraServiceProxyWrapper::getCameraServiceProxy() {
183#ifndef __BRILLO__
Austin Borger74fca042022-05-23 12:41:21 -0700184 Mutex::Autolock al(mProxyMutex);
185 if (mCameraServiceProxy == nullptr) {
186 mCameraServiceProxy = getDefaultCameraServiceProxy();
Shuzhen Wang316781a2020-08-18 18:11:01 -0700187 }
188#endif
Austin Borger74fca042022-05-23 12:41:21 -0700189 return mCameraServiceProxy;
190}
191
192sp<hardware::ICameraServiceProxy> CameraServiceProxyWrapper::getDefaultCameraServiceProxy() {
193#ifndef __BRILLO__
194 sp<IServiceManager> sm = defaultServiceManager();
195 // Use checkService because cameraserver normally starts before the
196 // system server and the proxy service. So the long timeout that getService
197 // has before giving up is inappropriate.
198 sp<IBinder> binder = sm->checkService(String16("media.camera.proxy"));
199 if (binder != nullptr) {
200 return interface_cast<ICameraServiceProxy>(binder);
201 }
202#endif
203 return nullptr;
Shuzhen Wang316781a2020-08-18 18:11:01 -0700204}
205
206void CameraServiceProxyWrapper::pingCameraServiceProxy() {
207 sp<ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
208 if (proxyBinder == nullptr) return;
209 proxyBinder->pingForUserUpdate();
210}
211
Emilian Peev065b2c12021-11-23 13:12:57 -0800212int CameraServiceProxyWrapper::getRotateAndCropOverride(String16 packageName, int lensFacing,
213 int userId) {
Emilian Peevb91f1802021-03-23 14:50:28 -0700214 sp<ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
215 if (proxyBinder == nullptr) return true;
Emilian Peev5368ebf2021-10-08 17:52:18 -0700216 int ret = 0;
Emilian Peev065b2c12021-11-23 13:12:57 -0800217 auto status = proxyBinder->getRotateAndCropOverride(packageName, lensFacing, userId, &ret);
Emilian Peevb91f1802021-03-23 14:50:28 -0700218 if (!status.isOk()) {
219 ALOGE("%s: Failed during top activity orientation query: %s", __FUNCTION__,
220 status.exceptionMessage().c_str());
221 }
222
223 return ret;
224}
225
Bharatt Kukreja7146ced2022-10-25 15:45:29 +0000226int CameraServiceProxyWrapper::getAutoframingOverride(const String16& packageName) {
227 sp<ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
228 if (proxyBinder == nullptr) {
229 return ANDROID_CONTROL_AUTOFRAMING_OFF;
230 }
231 int ret = 0;
232 auto status = proxyBinder->getAutoframingOverride(packageName, &ret);
233 if (!status.isOk()) {
234 ALOGE("%s: Failed during autoframing override query: %s", __FUNCTION__,
235 status.exceptionMessage().c_str());
236 }
237
238 return ret;
239}
240
Shuzhen Wang316781a2020-08-18 18:11:01 -0700241void CameraServiceProxyWrapper::logStreamConfigured(const String8& id,
242 int operatingMode, bool internalConfig, int32_t latencyMs) {
243 std::shared_ptr<CameraSessionStatsWrapper> sessionStats;
244 {
245 Mutex::Autolock l(mLock);
Shuzhen Wang25152e42023-06-13 22:18:58 +0000246 if (mSessionStatsMap.count(id) == 0) {
Shuzhen Wang316781a2020-08-18 18:11:01 -0700247 ALOGE("%s: SessionStatsMap should contain camera %s",
248 __FUNCTION__, id.c_str());
249 return;
250 }
Shuzhen Wang25152e42023-06-13 22:18:58 +0000251 sessionStats = mSessionStatsMap[id];
Shuzhen Wang316781a2020-08-18 18:11:01 -0700252 }
253
254 ALOGV("%s: id %s, operatingMode %d, internalConfig %d, latencyMs %d",
255 __FUNCTION__, id.c_str(), operatingMode, internalConfig, latencyMs);
256 sessionStats->onStreamConfigured(operatingMode, internalConfig, latencyMs);
257}
258
Austin Borger4a870a32022-02-25 01:48:41 +0000259void CameraServiceProxyWrapper::logActive(const String8& id, float maxPreviewFps) {
Shuzhen Wang316781a2020-08-18 18:11:01 -0700260 std::shared_ptr<CameraSessionStatsWrapper> sessionStats;
261 {
262 Mutex::Autolock l(mLock);
Shuzhen Wang25152e42023-06-13 22:18:58 +0000263 if (mSessionStatsMap.count(id) == 0) {
Shuzhen Wang316781a2020-08-18 18:11:01 -0700264 ALOGE("%s: SessionStatsMap should contain camera %s when logActive is called",
265 __FUNCTION__, id.c_str());
266 return;
267 }
Shuzhen Wang25152e42023-06-13 22:18:58 +0000268 sessionStats = mSessionStatsMap[id];
Shuzhen Wang316781a2020-08-18 18:11:01 -0700269 }
270
271 ALOGV("%s: id %s", __FUNCTION__, id.c_str());
Austin Borger74fca042022-05-23 12:41:21 -0700272 sp<hardware::ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
273 sessionStats->onActive(proxyBinder, maxPreviewFps);
Shuzhen Wang316781a2020-08-18 18:11:01 -0700274}
275
276void CameraServiceProxyWrapper::logIdle(const String8& id,
277 int64_t requestCount, int64_t resultErrorCount, bool deviceError,
Shuzhen Wang9372b0b2022-05-11 18:55:19 -0700278 const std::string& userTag, int32_t videoStabilizationMode,
Shuzhen Wang316781a2020-08-18 18:11:01 -0700279 const std::vector<hardware::CameraStreamStats>& streamStats) {
280 std::shared_ptr<CameraSessionStatsWrapper> sessionStats;
281 {
282 Mutex::Autolock l(mLock);
Shuzhen Wang25152e42023-06-13 22:18:58 +0000283 if (mSessionStatsMap.count(id) == 0) {
284 ALOGE("%s: SessionStatsMap should contain camera %s when logIdle is called",
Shuzhen Wang316781a2020-08-18 18:11:01 -0700285 __FUNCTION__, id.c_str());
Shuzhen Wang25152e42023-06-13 22:18:58 +0000286 return;
287 }
288 sessionStats = mSessionStatsMap[id];
Shuzhen Wang316781a2020-08-18 18:11:01 -0700289 }
290
Shuzhen Wangd26b1862022-03-07 12:05:05 -0800291 ALOGV("%s: id %s, requestCount %" PRId64 ", resultErrorCount %" PRId64 ", deviceError %d"
Shuzhen Wang9372b0b2022-05-11 18:55:19 -0700292 ", userTag %s, videoStabilizationMode %d", __FUNCTION__, id.c_str(), requestCount,
293 resultErrorCount, deviceError, userTag.c_str(), videoStabilizationMode);
Shuzhen Wang316781a2020-08-18 18:11:01 -0700294 for (size_t i = 0; i < streamStats.size(); i++) {
295 ALOGV("%s: streamStats[%zu]: w %d h %d, requestedCount %" PRId64 ", dropCount %"
296 PRId64 ", startTimeMs %d" ,
297 __FUNCTION__, i, streamStats[i].mWidth, streamStats[i].mHeight,
298 streamStats[i].mRequestCount, streamStats[i].mErrorCount,
299 streamStats[i].mStartLatencyMs);
300 }
301
Austin Borger74fca042022-05-23 12:41:21 -0700302 sp<hardware::ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
303 sessionStats->onIdle(proxyBinder, requestCount, resultErrorCount, deviceError, userTag,
Shuzhen Wang9372b0b2022-05-11 18:55:19 -0700304 videoStabilizationMode, streamStats);
Shuzhen Wang316781a2020-08-18 18:11:01 -0700305}
306
307void CameraServiceProxyWrapper::logOpen(const String8& id, int facing,
308 const String16& clientPackageName, int effectiveApiLevel, bool isNdk,
309 int32_t latencyMs) {
310 std::shared_ptr<CameraSessionStatsWrapper> sessionStats;
311 {
312 Mutex::Autolock l(mLock);
313 if (mSessionStatsMap.count(id) > 0) {
314 ALOGE("%s: SessionStatsMap shouldn't contain camera %s",
315 __FUNCTION__, id.c_str());
316 return;
317 }
318
319 int apiLevel = CameraSessionStats::CAMERA_API_LEVEL_1;
320 if (effectiveApiLevel == 2) {
321 apiLevel = CameraSessionStats::CAMERA_API_LEVEL_2;
322 }
323
Avichal Rakesh88fc5222023-03-03 15:00:59 -0800324 // Generate a new log ID for open events
325 int64_t logId = generateLogId(mRandomDevice);
326
327 sessionStats = std::make_shared<CameraSessionStatsWrapper>(
328 String16(id), facing, CameraSessionStats::CAMERA_STATE_OPEN, clientPackageName,
329 apiLevel, isNdk, latencyMs, logId);
Shuzhen Wang316781a2020-08-18 18:11:01 -0700330 mSessionStatsMap.emplace(id, sessionStats);
331 ALOGV("%s: Adding id %s", __FUNCTION__, id.c_str());
332 }
333
334 ALOGV("%s: id %s, facing %d, effectiveApiLevel %d, isNdk %d, latencyMs %d",
335 __FUNCTION__, id.c_str(), facing, effectiveApiLevel, isNdk, latencyMs);
Austin Borger74fca042022-05-23 12:41:21 -0700336 sp<hardware::ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
337 sessionStats->onOpen(proxyBinder);
Shuzhen Wang316781a2020-08-18 18:11:01 -0700338}
339
Shuzhen Wang03fe6232023-02-05 12:41:15 -0800340void CameraServiceProxyWrapper::logClose(const String8& id, int32_t latencyMs, bool deviceError) {
Shuzhen Wang316781a2020-08-18 18:11:01 -0700341 std::shared_ptr<CameraSessionStatsWrapper> sessionStats;
342 {
343 Mutex::Autolock l(mLock);
344 if (mSessionStatsMap.count(id) == 0) {
345 ALOGE("%s: SessionStatsMap should contain camera %s before it's closed",
346 __FUNCTION__, id.c_str());
347 return;
348 }
349
350 sessionStats = mSessionStatsMap[id];
351 if (sessionStats == nullptr) {
352 ALOGE("%s: SessionStatsMap should contain camera %s",
353 __FUNCTION__, id.c_str());
354 return;
355 }
Shuzhen Wang03fe6232023-02-05 12:41:15 -0800356
Shuzhen Wang316781a2020-08-18 18:11:01 -0700357 mSessionStatsMap.erase(id);
Shuzhen Wang03fe6232023-02-05 12:41:15 -0800358 ALOGV("%s: Erasing id %s, deviceError %d", __FUNCTION__, id.c_str(), deviceError);
Shuzhen Wang316781a2020-08-18 18:11:01 -0700359 }
360
Shuzhen Wang03fe6232023-02-05 12:41:15 -0800361 ALOGV("%s: id %s, latencyMs %d, deviceError %d", __FUNCTION__,
362 id.c_str(), latencyMs, deviceError);
Austin Borger74fca042022-05-23 12:41:21 -0700363 sp<hardware::ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
Shuzhen Wang03fe6232023-02-05 12:41:15 -0800364 sessionStats->onClose(proxyBinder, latencyMs, deviceError);
Shuzhen Wang316781a2020-08-18 18:11:01 -0700365}
366
Austin Borger9bfa0a72022-08-03 17:50:40 -0700367bool CameraServiceProxyWrapper::isCameraDisabled(int userId) {
Austin Borger5f7abe22022-04-26 15:55:10 -0700368 sp<ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
369 if (proxyBinder == nullptr) return true;
370 bool ret = false;
Austin Borger9bfa0a72022-08-03 17:50:40 -0700371 auto status = proxyBinder->isCameraDisabled(userId, &ret);
Austin Borger5f7abe22022-04-26 15:55:10 -0700372 if (!status.isOk()) {
373 ALOGE("%s: Failed during camera disabled query: %s", __FUNCTION__,
374 status.exceptionMessage().c_str());
375 }
376 return ret;
377}
378
Avichal Rakesh1fff2d12023-03-03 15:05:48 -0800379int64_t CameraServiceProxyWrapper::getCurrentLogIdForCamera(const String8& cameraId) {
380 std::shared_ptr<CameraSessionStatsWrapper> stats;
381 {
382 Mutex::Autolock _l(mLock);
383 if (mSessionStatsMap.count(cameraId) == 0) {
384 ALOGE("%s: SessionStatsMap should contain camera %s before asking for its logging ID.",
385 __FUNCTION__, cameraId.c_str());
386 return 0;
387 }
388
389 stats = mSessionStatsMap[cameraId];
390 }
391 return stats->getLogId();
392}
393
Avichal Rakesh88fc5222023-03-03 15:00:59 -0800394int64_t CameraServiceProxyWrapper::generateLogId(std::random_device& randomDevice) {
395 int64_t ret = 0;
396 do {
397 // std::random_device generates 32 bits per call, so we call it twice
398 ret = randomDevice();
399 ret = ret << 32;
400 ret = ret | randomDevice();
401 } while (ret == 0); // 0 is not a valid identifier
402
403 return ret;
404}
405
Avichal Rakesh6e57a2b2023-05-01 17:53:37 -0700406String16 CameraServiceProxyWrapper::updateExtensionStats(
407 const hardware::CameraExtensionSessionStats& extStats) {
408 std::shared_ptr<CameraSessionStatsWrapper> stats;
409 String8 cameraId = String8(extStats.cameraId);
410 {
411 Mutex::Autolock _l(mLock);
412 if (mSessionStatsMap.count(cameraId) == 0) {
413 ALOGE("%s CameraExtensionSessionStats reported for camera id that isn't open: %s",
414 __FUNCTION__, cameraId.c_str());
415 return {};
416 }
417
418 stats = mSessionStatsMap[cameraId];
419 return stats->updateExtensionSessionStats(extStats);
420 }
421}
422
Avichal Rakesh88fc5222023-03-03 15:00:59 -0800423} // namespace android