OWASP বিভাগ: MASVS-CODE: কোডের গুণমান
সংক্ষিপ্ত বিবরণ
রেডিও ফ্রিকোয়েন্সি (RF) কমিউনিকেশন বা তারযুক্ত সংযোগ ব্যবহার করে ডেটা স্থানান্তর বা অন্যান্য ডিভাইসের সাথে যোগাযোগ করার সুবিধা প্রদানকারী অ্যাপ্লিকেশন দেখা বিরল নয়। এই উদ্দেশ্যে অ্যান্ড্রয়েডে ব্যবহৃত সবচেয়ে সাধারণ প্রযুক্তিগুলো হলো ক্লাসিক ব্লুটুথ (ব্লুটুথ BR/EDR), ব্লুটুথ লো এনার্জি (BLE), ওয়াইফাই P2P, NFC এবং USB।
এই প্রযুক্তিগুলো সাধারণত এমন সব অ্যাপ্লিকেশনে প্রয়োগ করা হয়, যেগুলো স্মার্ট হোম অ্যাকসেসরিজ, স্বাস্থ্য পর্যবেক্ষণকারী ডিভাইস, গণপরিবহন কিয়স্ক, পেমেন্ট টার্মিনাল এবং অন্যান্য অ্যান্ড্রয়েড-চালিত ডিভাইসের সাথে যোগাযোগ করবে বলে আশা করা হয়।
অন্যান্য যেকোনো চ্যানেলের মতোই, মেশিন-টু-মেশিন যোগাযোগও এমন সব আক্রমণের শিকার হতে পারে, যার লক্ষ্য হলো দুই বা ততোধিক ডিভাইসের মধ্যে স্থাপিত বিশ্বাসের সীমানাকে লঙ্ঘন করা। ডিভাইস ইমপার্সোনেশনের মতো কৌশল ব্যবহার করে দূষিত ব্যবহারকারীরা এই যোগাযোগ চ্যানেলের বিরুদ্ধে বিভিন্ন ধরনের আক্রমণ চালাতে পারে।
অ্যান্ড্রয়েড ডেভেলপারদের জন্য মেশিন-টু-মেশিন যোগাযোগ কনফিগার করার নির্দিষ্ট এপিআই উপলব্ধ করে।
এই এপিআইগুলো সতর্কতার সাথে ব্যবহার করা উচিত, কারণ কমিউনিকেশন প্রোটোকল বাস্তবায়নে ত্রুটির ফলে ব্যবহারকারী বা ডিভাইসের ডেটা অননুমোদিত তৃতীয় পক্ষের কাছে প্রকাশ হয়ে যেতে পারে। সবচেয়ে খারাপ পরিস্থিতিতে, আক্রমণকারীরা দূর থেকে এক বা একাধিক ডিভাইসের নিয়ন্ত্রণ নিতে সক্ষম হতে পারে, যার ফলে তারা ডিভাইসের সমস্ত কন্টেন্টে সম্পূর্ণ অ্যাক্সেস পেয়ে যাবে।
প্রভাব
অ্যাপ্লিকেশনে ব্যবহৃত ডিভাইস-ভিত্তিক প্রযুক্তির ওপর নির্ভর করে এর প্রভাব ভিন্ন হতে পারে।
মেশিন-টু-মেশিন যোগাযোগ চ্যানেলের ভুল ব্যবহার বা কনফিগারেশনের কারণে ব্যবহারকারীর ডিভাইসটি অবিশ্বস্ত যোগাযোগের প্রচেষ্টার সম্মুখীন হতে পারে। এর ফলে ডিভাইসটি ম্যান-ইন-দ্য-মিডল (MiTM), কমান্ড ইনজেকশন, DoS, বা ইমপার্সোনেশন অ্যাটাকের মতো অতিরিক্ত আক্রমণের ঝুঁকিতে পড়তে পারে।
ঝুঁকি: ওয়্যারলেস চ্যানেলের মাধ্যমে সংবেদনশীল তথ্য আড়িপাতা।
When implementing machine-to-machine communication mechanisms, careful consideration should be given to both the used technology and the type of data that should be transmitted. While cabled connections are in practice more secure for such tasks, as they require a physical link between the involved devices, communications protocols using radio frequencies such as classic Bluetooth, BLE, NFC, and Wifi P2P can be intercepted. An attacker may be able to impersonate one of the terminals or access points involved in the data exchange, intercepting the communication over the air, consequently gaining access to sensitive user data. Additionally, malicious applications installed on the device, if granted the communication-specific runtime permissions , may be able to retrieve data exchanged between the devices by reading system message buffers.
প্রশমন
যদি অ্যাপ্লিকেশনটির ওয়্যারলেস চ্যানেলের মাধ্যমে মেশিন-টু-মেশিন সংবেদনশীল ডেটা আদান-প্রদানের প্রয়োজন হয়, তাহলে অ্যাপ্লিকেশনটির কোডে এনক্রিপশনের মতো অ্যাপ্লিকেশন-লেয়ার নিরাপত্তা সমাধান প্রয়োগ করা উচিত। এটি আক্রমণকারীদের কমিউনিকেশন চ্যানেলে আড়ি পাতা এবং আদান-প্রদান করা ডেটা ক্লিয়ার-টেক্সটে পুনরুদ্ধার করা থেকে বিরত রাখবে। অতিরিক্ত তথ্যের জন্য, ক্রিপ্টোগ্রাফি ডকুমেন্টেশন দেখুন।
ঝুঁকি: ওয়্যারলেস ক্ষতিকারক ডেটা অনুপ্রবেশ
ক্ষতিকারক ডেটা ব্যবহার করে ওয়্যারলেস মেশিন-টু-মেশিন কমিউনিকেশন চ্যানেলগুলোতে (যেমন ক্লাসিক ব্লুটুথ, বিএলই, এনএফসি, ওয়াইফাই পিটুপি) হস্তক্ষেপ করা যেতে পারে। যথেষ্ট দক্ষ আক্রমণকারীরা ব্যবহৃত কমিউনিকেশন প্রোটোকল শনাক্ত করতে পারে এবং ডেটা আদান-প্রদানের প্রবাহে হস্তক্ষেপ করতে পারে; উদাহরণস্বরূপ, এন্ডপয়েন্টগুলোর কোনো একটির ছদ্মবেশ ধারণ করে বিশেষভাবে তৈরি পেলোড পাঠানোর মাধ্যমে। এই ধরনের ক্ষতিকারক ট্র্যাফিক অ্যাপ্লিকেশনটির কার্যকারিতা হ্রাস করতে পারে এবং সবচেয়ে খারাপ পরিস্থিতিতে, অ্যাপ্লিকেশন ও ডিভাইসের অপ্রত্যাশিত আচরণের কারণ হতে পারে, অথবা ডস (DoS), কমান্ড ইনজেকশন বা ডিভাইস দখলের মতো আক্রমণের জন্ম দিতে পারে।
প্রশমন
অ্যান্ড্রয়েড ডেভেলপারদের মেশিন-টু-মেশিন যোগাযোগ পরিচালনার জন্য শক্তিশালী এপিআই (API) প্রদান করে, যেমন ক্লাসিক ব্লুটুথ, বিএলই (BLE), এনএফসি (NFC), এবং ওয়াইফাই পিটুপি (Wifi P2P)। দুটি ডিভাইসের মধ্যে আদান-প্রদান করা যেকোনো ডেটাকে পরিশুদ্ধ করতে এগুলোর সাথে সতর্কতার সাথে প্রয়োগ করা ডেটা ভ্যালিডেশন লজিক যুক্ত করা উচিত।
এই সমাধানটি অ্যাপ্লিকেশন পর্যায়ে প্রয়োগ করা উচিত এবং এতে এমন যাচাই ব্যবস্থা অন্তর্ভুক্ত থাকা উচিত যা যাচাই করবে যে ডেটার দৈর্ঘ্য ও ফরম্যাট প্রত্যাশিত কিনা এবং এতে একটি বৈধ পেলোড রয়েছে যা অ্যাপ্লিকেশন দ্বারা ব্যাখ্যা করা যেতে পারে।
নিম্নলিখিত কোড স্নিপেটটি ডেটা যাচাইকরণ লজিকের একটি উদাহরণ দেখায়। এটি অ্যান্ড্রয়েড ডেভেলপারদের ব্লুটুথ ডেটা ট্রান্সফার বাস্তবায়নের উদাহরণের উপর ভিত্তি করে তৈরি করা হয়েছে:
কোটলিন
class MyThread(private val mmInStream: InputStream, private val handler: Handler) : Thread() {
private val mmBuffer = ByteArray(1024)
override fun run() {
while (true) {
try {
val numBytes = mmInStream.read(mmBuffer)
if (numBytes > 0) {
val data = mmBuffer.copyOf(numBytes)
if (isValidBinaryData(data)) {
val readMsg = handler.obtainMessage(
MessageConstants.MESSAGE_READ, numBytes, -1, data
)
readMsg.sendToTarget()
} else {
Log.w(TAG, "Invalid data received: $data")
}
}
} catch (e: IOException) {
Log.d(TAG, "Input stream was disconnected", e)
break
}
}
}
private fun isValidBinaryData(data: ByteArray): Boolean {
if (// Implement data validation rules here) {
return false
} else {
// Data is in the expected format
return true
}
}
}
জাভা
public void run() {
mmBuffer = new byte[1024];
int numBytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs.
while (true) {
try {
// Read from the InputStream.
numBytes = mmInStream.read(mmBuffer);
if (numBytes > 0) {
// Handle raw data directly
byte[] data = Arrays.copyOf(mmBuffer, numBytes);
// Validate the data before sending it to the UI activity
if (isValidBinaryData(data)) {
// Data is valid, send it to the UI activity
Message readMsg = handler.obtainMessage(
MessageConstants.MESSAGE_READ, numBytes, -1,
data);
readMsg.sendToTarget();
} else {
// Data is invalid
Log.w(TAG, "Invalid data received: " + data);
}
}
} catch (IOException e) {
Log.d(TAG, "Input stream was disconnected", e);
break;
}
}
}
private boolean isValidBinaryData(byte[] data) {
if (// Implement data validation rules here) {
return false;
} else {
// Data is in the expected format
return true;
}
}
ঝুঁকি: ইউএসবি দ্বারা ক্ষতিকারক ডেটা প্রবেশ
যোগাযোগে আড়ি পাততে আগ্রহী কোনো বিদ্বেষী ব্যবহারকারী দুটি ডিভাইসের মধ্যেকার ইউএসবি সংযোগকে লক্ষ্যবস্তু করতে পারে। এক্ষেত্রে, প্রয়োজনীয় ভৌত সংযোগটি একটি অতিরিক্ত নিরাপত্তা স্তর হিসেবে কাজ করে, কারণ যেকোনো বার্তা আড়ি পেতে শোনার জন্য আক্রমণকারীকে টার্মিনাল দুটিকে সংযোগকারী কেবলটিতে প্রবেশাধিকার পেতে হয়। আক্রমণের আরেকটি মাধ্যম হলো অবিশ্বস্ত ইউএসবি ডিভাইস, যা ইচ্ছাকৃত বা অনিচ্ছাকৃতভাবে মূল ডিভাইসে প্লাগ করা হয়।
যদি অ্যাপ্লিকেশনটি নির্দিষ্ট ইন-অ্যাপ কার্যকারিতা চালু করার জন্য পিআইডি/ভিআইডি ব্যবহার করে ইউএসবি ডিভাইস ফিল্টার করে, তবে আক্রমণকারীরা আসল ডিভাইসটির ছদ্মবেশ ধারণ করে ইউএসবি চ্যানেলের মাধ্যমে প্রেরিত ডেটা বিকৃত করতে সক্ষম হতে পারে। এই ধরনের আক্রমণের ফলে ক্ষতিকারক ব্যবহারকারীরা ডিভাইসটিতে কীস্ট্রোক পাঠাতে বা অ্যাপ্লিকেশনের এমন কার্যকলাপ সম্পাদন করতে পারে, যা সবচেয়ে খারাপ ক্ষেত্রে, রিমোট কোড এক্সিকিউশন বা অনাকাঙ্ক্ষিত সফটওয়্যার ডাউনলোডের কারণ হতে পারে।
প্রশমন
একটি অ্যাপ্লিকেশন-স্তরের যাচাইকরণ লজিক প্রয়োগ করা উচিত। এই লজিকটি ইউএসবি-র মাধ্যমে প্রেরিত ডেটা ফিল্টার করবে এবং পরীক্ষা করে দেখবে যে এর দৈর্ঘ্য, ফরম্যাট এবং বিষয়বস্তু অ্যাপ্লিকেশনটির ব্যবহারের ক্ষেত্রের সাথে মেলে কিনা। উদাহরণস্বরূপ, একটি হার্টবিট মনিটরের কীস্ট্রোক কমান্ড পাঠানোর ক্ষমতা থাকা উচিত নয়।
এছাড়াও, সম্ভব হলে, অ্যাপ্লিকেশনটি ইউএসবি ডিভাইস থেকে যে সংখ্যক ইউএসবি প্যাকেট গ্রহণ করতে পারে, তা সীমিত করার বিষয়টি বিবেচনা করা উচিত। এটি ক্ষতিকারক ডিভাইসগুলোকে রাবার ডাকির মতো আক্রমণ চালানো থেকে বিরত রাখে।
এই যাচাইকরণটি সম্পন্ন করা যেতে পারে বাফারের বিষয়বস্তু পরীক্ষা করার জন্য একটি নতুন থ্রেড তৈরি করে, উদাহরণস্বরূপ, একটি bulkTransfer সময়:
কোটলিন
fun performBulkTransfer() {
// Stores data received from a device to the host in a buffer
val bytesTransferred = connection.bulkTransfer(endpointIn, buffer, buffer.size, 5000)
if (bytesTransferred > 0) {
if (//Checks against buffer content) {
processValidData(buffer)
} else {
handleInvalidData()
}
} else {
handleTransferError()
}
}
জাভা
public void performBulkTransfer() {
//Stores data received from a device to the host in a buffer
int bytesTransferred = connection.bulkTransfer(endpointIn, buffer, buffer.length, 5000);
if (bytesTransferred > 0) {
if (//Checks against buffer content) {
processValidData(buffer);
} else {
handleInvalidData();
}
} else {
handleTransferError();
}
}
নির্দিষ্ট ঝুঁকি
এই বিভাগে এমন ঝুঁকিগুলো একত্রিত করা হয়েছে যেগুলোর জন্য অ-প্রমিত প্রশমন কৌশল প্রয়োজন অথবা যেগুলো কোনো নির্দিষ্ট SDK স্তরে প্রশমিত করা হয়েছে এবং এগুলো এখানে সম্পূর্ণতার জন্য রাখা হয়েছে।
ঝুঁকি: ব্লুটুথ – ভুল শনাক্তকরণ সময়
অ্যান্ড্রয়েড ডেভেলপারদের ব্লুটুথ ডকুমেন্টেশনে যেমনটি উল্লেখ করা হয়েছে, অ্যাপ্লিকেশনের মধ্যে ব্লুটুথ ইন্টারফেস কনফিগার করার সময়, ডিভাইস ডিসকভারেবিলিটি চালু করতে startActivityForResult(Intent, int) মেথডটি ব্যবহার করলে এবং EXTRA_DISCOVERABLE_DURATION কে শূন্যতে সেট করলে, অ্যাপ্লিকেশনটি ব্যাকগ্রাউন্ড বা ফোরগ্রাউন্ডে চালু থাকা পর্যন্ত ডিভাইসটি ডিসকভারেবল থাকবে। ক্লাসিক ব্লুটুথ স্পেসিফিকেশন অনুযায়ী, ডিসকভারেবল ডিভাইসগুলো ক্রমাগত নির্দিষ্ট ডিসকভারি মেসেজ ব্রডকাস্ট করতে থাকে, যা অন্য ডিভাইসগুলোকে ডিভাইসের ডেটা পুনরুদ্ধার করতে বা এর সাথে সংযোগ স্থাপন করতে সাহায্য করে। এমন পরিস্থিতিতে, কোনো ক্ষতিকারক তৃতীয় পক্ষ এই ধরনের মেসেজ ইন্টারসেপ্ট করে অ্যান্ড্রয়েড-চালিত ডিভাইসটির সাথে সংযোগ স্থাপন করতে পারে। একবার সংযুক্ত হয়ে গেলে, একজন আক্রমণকারী ডেটা চুরি, DoS বা কমান্ড ইনজেকশনের মতো আরও আক্রমণ চালাতে পারে।
প্রশমন
The EXTRA_DISCOVERABLE_DURATION should never be set to zero. If the EXTRA_DISCOVERABLE_DURATION parameter is not set, by default, Android makes the devices discoverable for 2 minutes. The max value that can be set for the EXTRA_DISCOVERABLE_DURATION parameter is 2 hours (7200 seconds). It is recommended to keep the discoverable duration time to the shortest time possible, according to the application use case.
ঝুঁকি: এনএফসি – ক্লোন করা ইন্টেন্ট-ফিল্টার
একটি ক্ষতিকারক অ্যাপ্লিকেশন নির্দিষ্ট এনএফসি ট্যাগ বা এনএফসি-সক্ষম ডিভাইস পড়ার জন্য ইন্টেন্ট-ফিল্টার রেজিস্টার করতে পারে। এই ফিল্টারগুলো একটি বৈধ অ্যাপ্লিকেশন দ্বারা সংজ্ঞায়িত ফিল্টারগুলোর প্রতিলিপি তৈরি করতে পারে, যার ফলে একজন আক্রমণকারীর পক্ষে আদান-প্রদান করা এনএফসি ডেটার বিষয়বস্তু পড়া সম্ভব হয়। উল্লেখ্য যে, যখন দুটি অ্যাক্টিভিটি একটি নির্দিষ্ট এনএফসি ট্যাগের জন্য একই ইন্টেন্ট-ফিল্টার নির্দিষ্ট করে, তখন অ্যাক্টিভিটি চুজার প্রদর্শিত হয়, তাই আক্রমণটি সফল হওয়ার জন্য ব্যবহারকারীকে ক্ষতিকারক অ্যাপ্লিকেশনটিই বেছে নিতে হবে। তা সত্ত্বেও, ইন্টেন্ট-ফিল্টারের সাথে ক্লোকিং-এর সমন্বয়ে এই পরিস্থিতি এখনও সম্ভব। এই আক্রমণটি কেবল সেইসব ক্ষেত্রেই তাৎপর্যপূর্ণ যেখানে এনএফসি-এর মাধ্যমে আদান-প্রদান করা ডেটা অত্যন্ত সংবেদনশীল বলে বিবেচিত হতে পারে।
প্রশমন
কোনো অ্যাপ্লিকেশনের মধ্যে NFC রিডিং ক্ষমতা প্রয়োগ করার সময়, অ্যান্ড্রয়েড অ্যাপ্লিকেশন রেকর্ড (AAR)-এর সাথে ইন্টেন্ট-ফিল্টার ব্যবহার করা যেতে পারে। একটি NDEF মেসেজের মধ্যে AAR রেকর্ডটি এমবেড করলে এটি দৃঢ়ভাবে নিশ্চিত করে যে শুধুমাত্র বৈধ অ্যাপ্লিকেশন এবং এর সাথে যুক্ত NDEF হ্যান্ডলিং অ্যাক্টিভিটিই চালু হবে। এর ফলে অনাকাঙ্ক্ষিত অ্যাপ্লিকেশন বা অ্যাক্টিভিটিগুলো NFC-এর মাধ্যমে আদান-প্রদান করা অত্যন্ত সংবেদনশীল ট্যাগ বা ডিভাইসের ডেটা পড়তে পারবে না।
ঝুঁকি: এনএফসি – এনডিইএফ বার্তা যাচাইকরণের অভাব
যখন কোনো অ্যান্ড্রয়েড-চালিত ডিভাইস একটি এনএফসি ট্যাগ বা এনএফসি-সক্ষম ডিভাইস থেকে ডেটা গ্রহণ করে, তখন সিস্টেমটি স্বয়ংক্রিয়ভাবে সেই অ্যাপ্লিকেশন বা নির্দিষ্ট অ্যাক্টিভিটি চালু করে যা এর মধ্যে থাকা NDEF বার্তাটি পরিচালনা করার জন্য কনফিগার করা থাকে। অ্যাপ্লিকেশনে প্রয়োগ করা লজিক অনুসারে, ট্যাগে থাকা বা ডিভাইস থেকে প্রাপ্ত ডেটা অন্যান্য অ্যাক্টিভিটিতে সরবরাহ করা যেতে পারে, যা ওয়েব পেজ খোলার মতো পরবর্তী কার্যক্রম শুরু করে।
NDEF মেসেজের বিষয়বস্তু যাচাইকরণ ব্যবস্থা নেই এমন কোনো অ্যাপ্লিকেশন আক্রমণকারীদের NFC-সক্ষম ডিভাইস বা NFC ট্যাগ ব্যবহার করে অ্যাপ্লিকেশনটির মধ্যে ক্ষতিকারক পেলোড প্রবেশ করানোর সুযোগ দিতে পারে, যা অপ্রত্যাশিত আচরণের কারণ হয়ে দাঁড়ায় এবং এর ফলে ক্ষতিকারক ফাইল ডাউনলোড, কমান্ড ইনজেকশন বা DoS আক্রমণ ঘটতে পারে।
প্রশমন
প্রাপ্ত NDEF বার্তাটি অন্য কোনো অ্যাপ্লিকেশন কম্পোনেন্টে পাঠানোর আগে, এর ভেতরের ডেটা প্রত্যাশিত ফরম্যাটে আছে এবং এতে প্রত্যাশিত তথ্য রয়েছে কিনা তা যাচাই করা উচিত। এর ফলে ক্ষতিকারক ডেটা অপরিশোধিত অবস্থায় অন্যান্য অ্যাপ্লিকেশনের কম্পোনেন্টে চলে যাওয়া এড়ানো যায়, যা বিকৃত NFC ডেটা ব্যবহার করে অপ্রত্যাশিত আচরণ বা আক্রমণের ঝুঁকি কমিয়ে দেয়।
নিম্নলিখিত কোড স্নিপেটটি একটি মেথড হিসাবে প্রয়োগ করা ডেটা যাচাইকরণ লজিকের উদাহরণ দেখায়, যেখানে আর্গুমেন্ট হিসাবে একটি NDEF মেসেজ এবং messages অ্যারেতে তার ইনডেক্স ব্যবহার করা হয়েছে। একটি স্ক্যান করা NFC NDEF ট্যাগ থেকে ডেটা পাওয়ার জন্য এটি অ্যান্ড্রয়েড ডেভেলপারদের উদাহরণের উপর ভিত্তি করে প্রয়োগ করা হয়েছিল:
কোটলিন
//The method takes as input an element from the received NDEF messages array
fun isValidNDEFMessage(messages: Array<NdefMessage>, index: Int): Boolean {
// Checks if the index is out of bounds
if (index < 0 || index >= messages.size) {
return false
}
val ndefMessage = messages[index]
// Retrieves the record from the NDEF message
for (record in ndefMessage.records) {
// Checks if the TNF is TNF_ABSOLUTE_URI (0x03), if the Length Type is 1
if (record.tnf == NdefRecord.TNF_ABSOLUTE_URI && record.type.size == 1) {
// Loads payload in a byte array
val payload = record.payload
// Declares the Magic Number that should be matched inside the payload
val gifMagicNumber = byteArrayOf(0x47, 0x49, 0x46, 0x38, 0x39, 0x61) // GIF89a
// Checks the Payload for the Magic Number
for (i in gifMagicNumber.indices) {
if (payload[i] != gifMagicNumber[i]) {
return false
}
}
// Checks that the Payload length is, at least, the length of the Magic Number + The Descriptor
if (payload.size == 13) {
return true
}
}
}
return false
}
জাভা
//The method takes as input an element from the received NDEF messages array
public boolean isValidNDEFMessage(NdefMessage[] messages, int index) {
//Checks if the index is out of bounds
if (index < 0 || index >= messages.length) {
return false;
}
NdefMessage ndefMessage = messages[index];
//Retrieve the record from the NDEF message
for (NdefRecord record : ndefMessage.getRecords()) {
//Check if the TNF is TNF_ABSOLUTE_URI (0x03), if the Length Type is 1
if ((record.getTnf() == NdefRecord.TNF_ABSOLUTE_URI) && (record.getType().length == 1)) {
//Loads payload in a byte array
byte[] payload = record.getPayload();
//Declares the Magic Number that should be matched inside the payload
byte[] gifMagicNumber = {0x47, 0x49, 0x46, 0x38, 0x39, 0x61}; // GIF89a
//Checks the Payload for the Magic Number
for (int i = 0; i < gifMagicNumber.length; i++) {
if (payload[i] != gifMagicNumber[i]) {
return false;
}
}
//Checks that the Payload length is, at least, the length of the Magic Number + The Descriptor
if (payload.length == 13) {
return true;
}
}
}
return false;
}
সম্পদ
- রানটাইম অনুমতি
- সংযোগ নির্দেশিকা
- উদাহরণ
- বাল্কট্রান্সফার
- ক্রিপ্টোগ্রাফি
- ব্লুটুথ সেট আপ করুন
- এনএফসি ভিত্তিতে
- অ্যান্ড্রয়েড অ্যাপ্লিকেশন রেকর্ড
- ক্লাসিক ব্লুটুথ স্পেসিফিকেশন