Android डिवाइस सेट अप करते समय, कई उपयोगकर्ता अब भी अपने क्रेडेंशियल खुद मैनेज करते हैं. मैन्युअल प्रोसेस मुश्किल हो सकती है. साथ ही, इससे अक्सर उपयोगकर्ता अनुभव खराब हो जाता है. Block Store API, Google Play services की मदद से काम करने वाली एक लाइब्रेरी है. यह इस समस्या को हल करने के लिए, ऐप्लिकेशन को उपयोगकर्ता के क्रेडेंशियल सेव करने का एक तरीका उपलब्ध कराती है. इससे उपयोगकर्ता के पासवर्ड सेव करने से जुड़ी जटिलता या सुरक्षा जोखिम नहीं होता.
Block Store API की मदद से, आपका ऐप्लिकेशन ऐसा डेटा सेव कर सकता है जिसे बाद में नए डिवाइस पर उपयोगकर्ताओं की फिर से पुष्टि करने के लिए वापस पाया जा सकता है. इससे उपयोगकर्ता को बेहतर अनुभव मिलता है. ऐसा इसलिए, क्योंकि नए डिवाइस पर पहली बार आपका ऐप्लिकेशन लॉन्च करते समय, उन्हें साइन-इन स्क्रीन नहीं दिखती.
Block Store का इस्तेमाल करने के ये फ़ायदे हैं:
- डेवलपर के लिए, क्रेडेंशियल को एन्क्रिप्ट (सुरक्षित) करके सेव करने की सुविधा. जब भी संभव होता है, क्रेडेंशियल को पूरी तरह सुरक्षित (ई2ईई) किया जाता है.
- उपयोगकर्ता नाम और पासवर्ड के बजाय टोकन सेव करता है.
- साइन-इन फ़्लो में आने वाली समस्याओं को कम करें.
- उपयोगकर्ताओं को मुश्किल पासवर्ड मैनेज करने की झंझट से बचाएं.
- Google, उपयोगकर्ता की पहचान की पुष्टि करता है.
शुरू करने से पहले
अपने ऐप्लिकेशन को तैयार करने के लिए, यहां दिए गए सेक्शन में बताया गया तरीका अपनाएं.
अपने ऐप्लिकेशन को कॉन्फ़िगर करना
अपने प्रोजेक्ट-लेवल की build.gradle
फ़ाइल में, buildscript
और allprojects
, दोनों सेक्शन में Google का Maven रिपॉज़िटरी शामिल करें:
buildscript {
repositories {
google()
mavenCentral()
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
Block Store API के लिए, Google Play services डिपेंडेंसी को अपनी मॉड्यूल की Gradle बिल्ड फ़ाइल में जोड़ें. यह फ़ाइल आम तौर पर app/build.gradle
होती है:
dependencies {
implementation 'com.google.android.gms:play-services-auth-blockstore:16.4.0'
}
यह कैसे काम करता है
ब्लॉक स्टोर की मदद से डेवलपर, 16 बाइट ऐरे तक सेव और रीस्टोर कर सकते हैं. इससे आपको मौजूदा उपयोगकर्ता सेशन के बारे में अहम जानकारी सेव करने में मदद मिलती है. साथ ही, यह जानकारी को अपनी पसंद के मुताबिक सेव करने की सुविधा देता है. इस डेटा को पूरी तरह से एन्क्रिप्ट (सुरक्षित) किया जा सकता है. साथ ही, Block Store की सुविधा देने वाला इन्फ़्रास्ट्रक्चर, बैकअप लेने और डेटा वापस पाने की सुविधा देने वाले इन्फ़्रास्ट्रक्चर पर बनाया गया है.
इस गाइड में, Block Store में उपयोगकर्ता के टोकन को सेव करने के इस्तेमाल के उदाहरण के बारे में बताया गया है. यहां बताया गया है कि Block Store का इस्तेमाल करने वाला ऐप्लिकेशन कैसे काम करेगा:
- ऐप्लिकेशन में पुष्टि करने की प्रोसेस के दौरान या इसके बाद कभी भी, उपयोगकर्ता के पुष्टि करने वाले टोकन को Block Store में सेव किया जा सकता है, ताकि बाद में इसे वापस पाया जा सके.
- टोकन को डिवाइस में सेव किया जाएगा. इसे क्लाउड पर भी बैक अप लिया जा सकता है. साथ ही, जब भी मुमकिन होगा, इसे पूरी तरह सुरक्षित (एंड-टू-एंड एन्क्रिप्ट) किया जाएगा.
- डेटा तब ट्रांसफ़र होता है, जब उपयोगकर्ता किसी नए डिवाइस पर डेटा वापस लाने की प्रोसेस शुरू करता है.
- अगर उपयोगकर्ता, डेटा वापस लाने की प्रोसेस के दौरान आपके ऐप्लिकेशन को वापस लाता है, तो आपका ऐप्लिकेशन नए डिवाइस पर Block Store से सेव किया गया टोकन वापस पा सकता है.
टोकन सेव किया जा रहा है
जब कोई उपयोगकर्ता आपके ऐप्लिकेशन में साइन इन करता है, तब उसके लिए जनरेट किए गए पुष्टि करने वाले टोकन को Block Store में सेव किया जा सकता है. इस टोकन को यूनीक कुंजी-वैल्यू पेयर का इस्तेमाल करके सेव किया जा सकता है. हर एंट्री के लिए, ज़्यादा से ज़्यादा 4 केबी का इस्तेमाल किया जा सकता है. टोकन को सेव करने के लिए, StoreBytesData.Builder
के इंस्टेंस पर setBytes()
और setKey()
को कॉल करें. इससे उपयोगकर्ता के क्रेडेंशियल को सोर्स डिवाइस पर सेव किया जा सकेगा. Block Store में टोकन सेव करने के बाद, उसे एन्क्रिप्ट (सुरक्षित) किया जाता है. साथ ही, उसे डिवाइस की मेमोरी या स्टोरेज में सेव किया जाता है.
यहां दिए गए सैंपल में, पुष्टि करने वाले टोकन को लोकल डिवाइस में सेव करने का तरीका बताया गया है:
Java
BlockstoreClient client = Blockstore.getClient(this); byte[] bytes1 = new byte[] { 1, 2, 3, 4 }; // Store one data block. String key1 = "com.example.app.key1"; StoreBytesData storeRequest1 = StoreBytesData.Builder() .setBytes(bytes1) // Call this method to set the key value pair the data should be associated with. .setKeys(Arrays.asList(key1)) .build(); client.storeBytes(storeRequest1) .addOnSuccessListener(result -> Log.d(TAG, "stored " + result + " bytes")) .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));
Kotlin
val client = Blockstore.getClient(this) val bytes1 = byteArrayOf(1, 2, 3, 4) // Store one data block. val key1 = "com.example.app.key1" val storeRequest1 = StoreBytesData.Builder() .setBytes(bytes1) // Call this method to set the key value with which the data should be associated with. .setKeys(Arrays.asList(key1)) .build() client.storeBytes(storeRequest1) .addOnSuccessListener { result: Int -> Log.d(TAG, "Stored $result bytes") } .addOnFailureListener { e -> Log.e(TAG, "Failed to store bytes", e) }
डिफ़ॉल्ट टोकन का इस्तेमाल करना
StoreBytes का इस्तेमाल करके बिना किसी कुंजी के सेव किए गए डेटा के लिए, डिफ़ॉल्ट कुंजी
BlockstoreClient.DEFAULT_BYTES_DATA_KEY
का इस्तेमाल किया जाता है.
Java
BlockstoreClient client = Blockstore.getClient(this); // The default key BlockstoreClient.DEFAULT_BYTES_DATA_KEY. byte[] bytes = new byte[] { 9, 10 }; StoreBytesData storeRequest = StoreBytesData.Builder() .setBytes(bytes) .build(); client.storeBytes(storeRequest) .addOnSuccessListener(result -> Log.d(TAG, "stored " + result + " bytes")) .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));
Kotlin
val client = Blockstore.getClient(this); // the default key BlockstoreClient.DEFAULT_BYTES_DATA_KEY. val bytes = byteArrayOf(1, 2, 3, 4) val storeRequest = StoreBytesData.Builder() .setBytes(bytes) .build(); client.storeBytes(storeRequest) .addOnSuccessListener { result: Int -> Log.d(TAG, "stored $result bytes") } .addOnFailureListener { e -> Log.e(TAG, "Failed to store bytes", e) }
टोकन वापस पाया जा रहा है
इसके बाद, जब कोई उपयोगकर्ता नए डिवाइस पर डेटा वापस लाने की प्रोसेस पूरी करता है, तो Google Play services पहले उपयोगकर्ता की पुष्टि करती है. इसके बाद, आपके ब्लॉक किए गए स्टोर का डेटा वापस लाती है. उपयोगकर्ता ने डेटा वापस लाने की प्रोसेस के तहत, आपके ऐप्लिकेशन का डेटा वापस लाने के लिए पहले ही सहमति दे दी है. इसलिए, अतिरिक्त सहमति की ज़रूरत नहीं है. जब उपयोगकर्ता आपका ऐप्लिकेशन खोलता है, तब retrieveBytes()
को कॉल करके, Block Store से टोकन का अनुरोध किया जा सकता है. इसके बाद, वापस पाए गए टोकन का इस्तेमाल करके, उपयोगकर्ता को नए डिवाइस पर साइन इन रखा जा सकता है.
यहां दिए गए सैंपल में, कुछ कुंजियों के आधार पर कई टोकन वापस पाने का तरीका बताया गया है.
Java
BlockstoreClient client = Blockstore.getClient(this); // Retrieve data associated with certain keys. String key1 = "com.example.app.key1"; String key2 = "com.example.app.key2"; String key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY; // Used to retrieve data stored without a key ListrequestedKeys = Arrays.asList(key1, key2, key3); // Add keys to array RetrieveBytesRequest retrieveRequest = new RetrieveBytesRequest.Builder() .setKeys(requestedKeys) .build(); client.retrieveBytes(retrieveRequest) .addOnSuccessListener( result -> { Map<String, BlockstoreData> blockstoreDataMap = result.getBlockstoreDataMap(); for (Map.Entry<String, BlockstoreData> entry : blockstoreDataMap.entrySet()) { Log.d(TAG, String.format( "Retrieved bytes %s associated with key %s.", new String(entry.getValue().getBytes()), entry.getKey())); } }) .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));
Kotlin
val client = Blockstore.getClient(this) // Retrieve data associated with certain keys. val key1 = "com.example.app.key1" val key2 = "com.example.app.key2" val key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY // Used to retrieve data stored without a key val requestedKeys = Arrays.asList(key1, key2, key3) // Add keys to array val retrieveRequest = RetrieveBytesRequest.Builder() .setKeys(requestedKeys) .build() client.retrieveBytes(retrieveRequest) .addOnSuccessListener { result: RetrieveBytesResponse -> val blockstoreDataMap = result.blockstoreDataMap for ((key, value) in blockstoreDataMap) { Log.d(ContentValues.TAG, String.format( "Retrieved bytes %s associated with key %s.", String(value.bytes), key)) } } .addOnFailureListener { e: Exception? -> Log.e(ContentValues.TAG, "Failed to store bytes", e) }
सभी टोकन वापस लाए जा रहे हैं.
यहां दिए गए उदाहरण में, BlockStore में सेव किए गए सभी टोकन वापस पाने का तरीका बताया गया है.
Java
BlockstoreClient client = Blockstore.getClient(this) // Retrieve all data. RetrieveBytesRequest retrieveRequest = new RetrieveBytesRequest.Builder() .setRetrieveAll(true) .build(); client.retrieveBytes(retrieveRequest) .addOnSuccessListener( result -> { Map<String, BlockstoreData> blockstoreDataMap = result.getBlockstoreDataMap(); for (Map.Entry<String, BlockstoreData> entry : blockstoreDataMap.entrySet()) { Log.d(TAG, String.format( "Retrieved bytes %s associated with key %s.", new String(entry.getValue().getBytes()), entry.getKey())); } }) .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));
Kotlin
val client = Blockstore.getClient(this) val retrieveRequest = RetrieveBytesRequest.Builder() .setRetrieveAll(true) .build() client.retrieveBytes(retrieveRequest) .addOnSuccessListener { result: RetrieveBytesResponse -> val blockstoreDataMap = result.blockstoreDataMap for ((key, value) in blockstoreDataMap) { Log.d(ContentValues.TAG, String.format( "Retrieved bytes %s associated with key %s.", String(value.bytes), key)) } } .addOnFailureListener { e: Exception? -> Log.e(ContentValues.TAG, "Failed to store bytes", e) }
यहां डिफ़ॉल्ट कुंजी को वापस पाने का तरीका बताया गया है.
Java
BlockStoreClient client = Blockstore.getClient(this); RetrieveBytesRequest retrieveRequest = new RetrieveBytesRequest.Builder() .setKeys(Arrays.asList(BlockstoreClient.DEFAULT_BYTES_DATA_KEY)) .build(); client.retrieveBytes(retrieveRequest);
Kotlin
val client = Blockstore.getClient(this) val retrieveRequest = RetrieveBytesRequest.Builder() .setKeys(Arrays.asList(BlockstoreClient.DEFAULT_BYTES_DATA_KEY)) .build() client.retrieveBytes(retrieveRequest)
टोकन मिटाना
BlockStore से टोकन मिटाने की ज़रूरत इन वजहों से पड़ सकती है:
- उपयोगकर्ता, साइन आउट करने के यूज़र फ़्लो से गुज़रता है.
- टोकन रद्द कर दिया गया है या अमान्य है.
टोकन वापस पाने की तरह ही, यह तय किया जा सकता है कि किन टोकन को मिटाना है. इसके लिए, उन कुंजियों का एक कलेक्शन सेट करें जिन्हें मिटाना है.
यहां दिए गए उदाहरण में, कुछ कुंजियों को मिटाने का तरीका बताया गया है:
Java
BlockstoreClient client = Blockstore.getClient(this); // Delete data associated with certain keys. String key1 = "com.example.app.key1"; String key2 = "com.example.app.key2"; String key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY; // Used to delete data stored without key ListrequestedKeys = Arrays.asList(key1, key2, key3) // Add keys to array DeleteBytesRequest deleteRequest = new DeleteBytesRequest.Builder() .setKeys(requestedKeys) .build(); client.deleteBytes(deleteRequest)
Kotlin
val client = Blockstore.getClient(this) // Retrieve data associated with certain keys. val key1 = "com.example.app.key1" val key2 = "com.example.app.key2" val key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY // Used to retrieve data stored without a key val requestedKeys = Arrays.asList(key1, key2, key3) // Add keys to array val retrieveRequest = DeleteBytesRequest.Builder() .setKeys(requestedKeys) .build() client.deleteBytes(retrieveRequest)
सभी टोकन मिटाएं
यहां दिए गए उदाहरण में, BlockStore में सेव किए गए सभी टोकन मिटाने का तरीका बताया गया है:
Java
// Delete all data. DeleteBytesRequest deleteAllRequest = new DeleteBytesRequest.Builder() .setDeleteAll(true) .build(); client.deleteBytes(deleteAllRequest) .addOnSuccessListener(result -> Log.d(TAG, "Any data found and deleted? " + result));
Kotlin
val deleteAllRequest = DeleteBytesRequest.Builder() .setDeleteAll(true) .build() retrieve bytes, the keyBlockstoreClient.DEFAULT_BYTES_DATA_KEY
can be used in theRetrieveBytesRequest
instance in order to get your saved data
The following example shows how to retrieve the default key.
Java
End-to-end encryption
In order for end-to-end encryption to be made available, the device must be
running Android 9 or higher, and the user must have set a screen lock
(PIN, pattern, or password) for their device. You can verify if encryption will
be available on the device by calling isEndToEndEncryptionAvailable()
.
The following sample shows how to verify if encryption will be available during cloud backup:
client.isEndToEndEncryptionAvailable()
.addOnSuccessListener { result ->
Log.d(TAG, "Will Block Store cloud backup be end-to-end encrypted? $result")
}
क्लाउड बैकअप की सुविधा चालू करना
क्लाउड बैकअप की सुविधा चालू करने के लिए, अपने StoreBytesData
ऑब्जेक्ट में setShouldBackupToCloud()
तरीका जोड़ें. जब setShouldBackupToCloud()
को 'सही है' के तौर पर सेट किया जाता है, तब Block Store, क्लाउड में सेव किए गए बाइट का समय-समय पर बैकअप लेता है.
यहां दिए गए उदाहरण में, क्लाउड बैकअप की सुविधा को सिर्फ़ तब चालू करने का तरीका बताया गया है, जब क्लाउड बैकअप पूरी तरह सुरक्षित (E2EE) हो:
val client = Blockstore.getClient(this)
val storeBytesDataBuilder = StoreBytesData.Builder()
.setBytes(/* BYTE_ARRAY */)
client.isEndToEndEncryptionAvailable()
.addOnSuccessListener { isE2EEAvailable ->
if (isE2EEAvailable) {
storeBytesDataBuilder.setShouldBackupToCloud(true)
Log.d(TAG, "E2EE is available, enable backing up bytes to the cloud.")
client.storeBytes(storeBytesDataBuilder.build())
.addOnSuccessListener { result ->
Log.d(TAG, "stored: ${result.getBytesStored()}")
}.addOnFailureListener { e ->
Log.e(TAG, “Failed to store bytes”, e)
}
} else {
Log.d(TAG, "E2EE is not available, only store bytes for D2D restore.")
}
}
जांच करने का तरीका
डवलपमेंट के दौरान, डेटा वापस लाने की प्रोसेस की जांच करने के लिए, यहां दिए गए तरीकों का इस्तेमाल करें.
उसी डिवाइस पर ऐप्लिकेशन को अनइंस्टॉल/फिर से इंस्टॉल करना
अगर उपयोगकर्ता बैकअप सेवाओं को चालू करता है, तो ऐप्लिकेशन को अनइंस्टॉल/फिर से इंस्टॉल करने पर भी, Block Store का डेटा बना रहता है. इसे सेटिंग > Google > बैकअप में जाकर देखा जा सकता है.
जांच करने के लिए, यह तरीका अपनाएं:
- Block Store API को अपने टेस्ट ऐप्लिकेशन के साथ इंटिग्रेट करें.
- अपने डेटा को सेव करने के लिए, Block Store API को चालू करने के लिए टेस्ट ऐप्लिकेशन का इस्तेमाल करें.
- टेस्ट ऐप्लिकेशन को अनइंस्टॉल करें. इसके बाद, उसी डिवाइस पर अपना ऐप्लिकेशन फिर से इंस्टॉल करें.
- अपना डेटा वापस पाने के लिए, Block Store API को चालू करने के लिए टेस्ट ऐप्लिकेशन का इस्तेमाल करें.
- पुष्टि करें कि वापस लाए गए बाइट, अनइंस्टॉल करने से पहले सेव किए गए बाइट के बराबर हैं.
एक डिवाइस से दूसरे डिवाइस पर
ज़्यादातर मामलों में, इसके लिए टारगेट डिवाइस को फ़ैक्ट्री रीसेट करना होगा. इसके बाद, Android डिवाइस पर वायरलेस तरीके से डेटा वापस लाने की प्रोसेस या Google केबल की मदद से डेटा वापस लाने की प्रोसेस शुरू करें. यह प्रोसेस, सिर्फ़ उन डिवाइसों के लिए उपलब्ध है जिन पर यह काम करती है.
क्लाउड से वापस लाना
- अपने टेस्ट ऐप्लिकेशन में Block Store API को इंटिग्रेट करें. टेस्ट ऐप्लिकेशन को Play Store पर सबमिट करना ज़रूरी है.
- सोर्स डिवाइस पर, Block Store API को चालू करने के लिए टेस्ट ऐप्लिकेशन का इस्तेमाल करें, ताकि आपका डेटा सेव किया जा सके. इसके लिए,
shouldBackUpToCloud
कोtrue
पर सेट करें. - O और इससे ऊपर के वर्शन वाले डिवाइसों के लिए, Block Store के क्लाउड बैकअप को मैन्युअल तरीके से ट्रिगर किया जा सकता है:
सेटिंग > Google > बैकअप पर जाएं. इसके बाद, “अभी बैकअप लें” बटन पर क्लिक करें.
- यह पुष्टि करने के लिए कि Block Store का क्लाउड बैकअप सही तरीके से लिया गया है, यह तरीका अपनाएं:
- बैकअप पूरा होने के बाद, “CloudSyncBpTkSvc” टैग वाली लॉग लाइनें खोजें.
- आपको इस तरह की लाइनें दिखनी चाहिए: “......, CloudSyncBpTkSvc: sync result: SUCCESS, ..., uploaded size: XXX bytes ...”
- ब्लॉक स्टोर के क्लाउड बैकअप के बाद, पांच मिनट का “कूल डाउन” पीरियड होता है. इन पांच मिनट के दौरान, “अभी बैकअप लें” बटन पर क्लिक करने से, Block Store के क्लाउड में मौजूद डेटा का दूसरा बैकअप नहीं लिया जाएगा.
- यह पुष्टि करने के लिए कि Block Store का क्लाउड बैकअप सही तरीके से लिया गया है, यह तरीका अपनाएं:
- टारगेट डिवाइस को फ़ैक्ट्री रीसेट करें और क्लाउड से डेटा वापस लाने की प्रोसेस पूरी करें. डेटा वापस लाने की प्रोसेस के दौरान, टेस्ट ऐप्लिकेशन को वापस लाने के लिए चुनें. Cloud से डेटा वापस पाने के तरीकों के बारे में ज़्यादा जानने के लिए, Cloud से डेटा वापस पाने के तरीके लेख पढ़ें.
- टारगेट डिवाइस पर, टेस्ट ऐप्लिकेशन का इस्तेमाल करके Block store API को कॉल करें, ताकि आपका डेटा वापस मिल सके.
- पुष्टि करें कि सोर्स डिवाइस में सेव किए गए बाइट और वापस लाए गए बाइट एक जैसे हैं.
डिवाइस से जुड़ी ज़रूरी शर्तें
एंड-टू-एंड एन्क्रिप्शन (E2EE)
- एंड-टू-एंड एन्क्रिप्शन की सुविधा, Android 9 (एपीआई 29) और उसके बाद के वर्शन वाले डिवाइसों पर काम करती है.
- डिवाइस में स्क्रीन लॉक सेट होना चाहिए. इसके लिए, पिन, पैटर्न या पासवर्ड का इस्तेमाल किया जाना चाहिए. इससे एंड-टू-एंड एन्क्रिप्शन की सुविधा चालू हो जाती है और उपयोगकर्ता के डेटा को सही तरीके से एन्क्रिप्ट (सुरक्षित) किया जा सकता है.
एक डिवाइस से दूसरे डिवाइस में डेटा वापस लाने का तरीका
एक डिवाइस से दूसरे डिवाइस पर डेटा वापस लाने के लिए, आपके पास सोर्स डिवाइस और टारगेट डिवाइस होना चाहिए. ये वे दो डिवाइस होंगे जिनसे डेटा ट्रांसफ़र किया जा रहा है.
बैकअप लेने के लिए, सोर्स डिवाइसों पर Android 6 (एपीआई 23) या उसके बाद का वर्शन होना चाहिए.
Android 9 (एपीआई 29) और इसके बाद के वर्शन वाले टारगेट डिवाइसों पर, डेटा वापस पाने की सुविधा उपलब्ध होनी चाहिए.
एक डिवाइस से दूसरे डिवाइस पर डेटा वापस लाने के तरीके के बारे में ज़्यादा जानकारी यहां मिल सकती है.
क्लाउड बैकअप और वापस लाने की सुविधा
क्लाउड बैकअप और वापस लाने की सुविधा के लिए, सोर्स डिवाइस और टारगेट डिवाइस की ज़रूरत होगी.
बैकअप लेने के लिए, सोर्स डिवाइसों पर Android 6 (एपीआई 23) या उसके बाद का वर्शन होना चाहिए.
टारगेट किए गए डिवाइसों पर, उनके वेंडर के हिसाब से सहायता मिलती है. Pixel डिवाइसों पर, Android 9 (एपीआई 29) से इस सुविधा का इस्तेमाल किया जा सकता है. साथ ही, अन्य सभी डिवाइसों पर Android 12 (एपीआई 31) या उसके बाद का वर्शन होना चाहिए.