پروفایل های کاری

پلتفرم Android به دستگاه‌ها اجازه می‌دهد تا نمایه‌های کاری داشته باشند (گاهی اوقات به عنوان نمایه‌های مدیریت‌شده نامیده می‌شود). یک نمایه کاری توسط یک سرپرست فناوری اطلاعات کنترل می شود و عملکردهای موجود برای آن جدا از عملکرد نمایه اصلی کاربر تنظیم می شود. این رویکرد به سازمان‌ها اجازه می‌دهد محیطی را کنترل کنند که در آن برنامه‌ها و داده‌های خاص شرکت در دستگاه کاربر اجرا می‌شود، در حالی که همچنان به کاربران اجازه می‌دهد از برنامه‌ها و نمایه‌های شخصی خود استفاده کنند.

این درس به شما نشان می دهد که چگونه برنامه خود را تغییر دهید تا به طور قابل اعتماد در دستگاهی با نمایه کاری کار کند. شما نیازی به انجام هیچ کاری غیر از بهترین شیوه های معمول توسعه برنامه ندارید. با این حال، برخی از این بهترین شیوه‌ها به ویژه در دستگاه‌های دارای نمایه کاری مهم می‌شوند. این سند مواردی را که باید از آنها آگاه باشید برجسته می کند.

نمای کلی

کاربران اغلب می خواهند از دستگاه های شخصی خود در یک محیط سازمانی استفاده کنند. این وضعیت می تواند سازمان ها را با دوراهی مواجه کند. اگر کاربر بتواند از دستگاه خود استفاده کند، سازمان باید نگران باشد که اطلاعات محرمانه (مانند ایمیل‌های کارکنان و مخاطبین) روی دستگاهی باشد که سازمان کنترل نمی‌کند.

برای رسیدگی به این وضعیت، Android 5.0 (سطح API 21) به سازمان‌ها اجازه می‌دهد تا نمایه‌های کاری را تنظیم کنند. اگر دستگاهی نمایه کاری دارد، تنظیمات نمایه تحت کنترل سرپرست فناوری اطلاعات است. سرپرست فناوری اطلاعات می‌تواند برنامه‌هایی را که برای آن نمایه مجاز است انتخاب کند و می‌تواند ویژگی‌های دستگاهی را که برای نمایه در دسترس است، کنترل کند.

اگر دستگاهی دارای نمایه کاری باشد، برنامه‌هایی که در دستگاه اجرا می‌شوند، مهم نیست که برنامه تحت کدام نمایه اجرا می‌شود، پیامدهایی وجود دارد:

  • به طور پیش‌فرض، بیشتر intent‌ها از یک نمایه به نمایه دیگر عبور نمی‌کنند. اگر برنامه‌ای که روی نمایه اجرا می‌شود، یک intent را اجرا می‌کند، هیچ کنترل‌کننده‌ای برای intent در آن نمایه وجود ندارد و به دلیل محدودیت‌های نمایه، intent اجازه ندارد به نمایه دیگر منتقل شود، درخواست با شکست مواجه می‌شود و ممکن است برنامه به‌طور غیرمنتظره‌ای خاموش شود.
  • سرپرست IT نمایه می‌تواند برنامه‌های سیستمی موجود در نمایه کاری را محدود کند. این محدودیت همچنین می تواند منجر به عدم وجود کنترل کننده برای برخی از مقاصد رایج در نمایه کاری شود.
  • از آنجایی که نمایه‌های شخصی و کاری دارای مناطق ذخیره‌سازی مجزا هستند، URI فایلی که در یک نمایه معتبر است، در نمایه دیگر معتبر نیست. هر هدفی که روی یک نمایه شلیک می‌شود، ممکن است روی دیگری (بسته به تنظیمات نمایه) مدیریت شود، بنابراین پیوست کردن URI فایل به intent ها ایمن نیست.

جلوگیری از اهداف ناموفق

در دستگاهی با نمایه کاری، محدودیت‌هایی وجود دارد که آیا می‌توانند از یک نمایه به نمایه دیگر عبور کنند یا خیر. در بیشتر موارد، هنگامی که یک هدف شلیک می شود، در همان نمایه ای که شلیک شده است، مدیریت می شود. اگر کنترل‌کننده‌ای برای intent در آن نمایه وجود نداشته باشد، intent مدیریت نمی‌شود و برنامه‌ای که آن را اجرا کرده است ممکن است به‌طور غیرمنتظره‌ای خاموش شود — حتی اگر کنترل‌کننده‌ای برای intent در نمایه دیگر وجود داشته باشد.

مدیر نمایه می‌تواند انتخاب کند که کدام هدف‌ها مجاز به عبور از یک نمایه به نمایه دیگر هستند. از آنجایی که سرپرست فناوری اطلاعات این تصمیم را می گیرد، هیچ راهی برای شما وجود ندارد که از قبل بدانید کدام اهداف مجاز به عبور از این مرز هستند. سرپرست فناوری اطلاعات این خط‌مشی را تنظیم می‌کند و می‌تواند در هر زمانی آن را تغییر دهد.

قبل از اینکه برنامه شما فعالیتی را شروع کند، باید بررسی کنید که وضوح مناسبی وجود دارد. با فراخوانی Intent.resolveActivity() می توانید تأیید کنید که وضوح قابل قبولی وجود دارد. اگر راهی برای حل کردن intent وجود نداشته باشد، متد null را برمی‌گرداند. اگر متد غیر تهی را برگرداند، حداقل یک راه برای حل و فصل اینتنت وجود دارد، و خاموش کردن intent ایمن است. در این مورد، هدف می تواند قابل حل باشد یا به این دلیل که یک کنترل کننده در نمایه فعلی وجود دارد، یا به این دلیل که هدف مجاز است به یک کنترل کننده در نمایه دیگر منتقل شود. (برای اطلاعات بیشتر در مورد حل و فصل مقاصد، به مقاصد مشترک مراجعه کنید.)

برای مثال، اگر برنامه شما نیاز به تنظیم تایمر دارد، باید بررسی کند که یک کنترل کننده معتبر برای هدف ACTION_SET_TIMER وجود دارد. اگر برنامه نتواند هدف را حل کند، باید یک اقدام مناسب (مانند نمایش یک پیام خطا) انجام دهد.

کاتلین

fun startTimer(message: String, seconds: Int) {

    // Build the "set timer" intent
    val timerIntent = Intent(AlarmClock.ACTION_SET_TIMER).apply {
        putExtra(AlarmClock.EXTRA_MESSAGE, message)
        putExtra(AlarmClock.EXTRA_LENGTH, seconds)
        putExtra(AlarmClock.EXTRA_SKIP_UI, true)
    }

    // Check if there's a handler for the intent
    if (timerIntent.resolveActivity(packageManager) == null) {

        // Can't resolve the intent! Fail this operation cleanly
        // (perhaps by showing an error message)

    } else {
        // Intent resolves, it's safe to fire it off
        startActivity(timerIntent)

    }
}

جاوا

public void startTimer(String message, int seconds) {

    // Build the "set timer" intent
    Intent timerIntent = new Intent(AlarmClock.ACTION_SET_TIMER)
            .putExtra(AlarmClock.EXTRA_MESSAGE, message)
            .putExtra(AlarmClock.EXTRA_LENGTH, seconds)
            .putExtra(AlarmClock.EXTRA_SKIP_UI, true);

    // Check if there's a handler for the intent
    if (timerIntent.resolveActivity(getPackageManager()) == null) {

        // Can't resolve the intent! Fail this operation cleanly
        // (perhaps by showing an error message)

    } else {
        // Intent resolves, it's safe to fire it off
        startActivity(timerIntent);

    }
}

به اشتراک گذاری فایل ها در پروفایل ها

گاهی اوقات یک برنامه نیاز دارد تا برنامه های دیگر را با دسترسی به فایل های خود فراهم کند. به عنوان مثال، یک برنامه گالری تصاویر ممکن است بخواهد تصاویر خود را با ویرایشگرهای تصویر به اشتراک بگذارد. معمولاً دو راه برای اشتراک گذاری فایل وجود دارد: با URI فایل یا محتوای URI .

یک URI فایل با پیشوند file: شروع می شود و به دنبال آن مسیر مطلق فایل در حافظه دستگاه قرار می گیرد. با این حال، از آنجایی که نمایه کاری و نمایه شخصی از مناطق ذخیره‌سازی جداگانه استفاده می‌کنند، URI فایلی که در یک نمایه معتبر است، در نمایه دیگر معتبر نیست. این وضعیت به این معنی است که اگر URI فایلی را به یک intent پیوست کنید و intent در نمایه دیگر مدیریت شود، کنترل کننده قادر به دسترسی به فایل نیست.

در عوض، باید فایل‌ها را با URI محتوا به اشتراک بگذارید. URI های محتوا فایل را به شیوه ای امن تر و قابل اشتراک گذاری شناسایی می کنند. URI محتوا حاوی مسیر فایل، و همچنین مرجعی است که فایل را ارائه می دهد، و یک شماره شناسه شناسایی فایل. با استفاده از FileProvider می توانید یک شناسه محتوا برای هر فایلی ایجاد کنید. سپس می توانید آن شناسه محتوا را با سایر برنامه ها (حتی در نمایه دیگر) به اشتراک بگذارید. گیرنده می تواند از شناسه محتوا برای دسترسی به فایل واقعی استفاده کند.

به عنوان مثال، در اینجا نحوه دریافت URI محتوا برای یک فایل URI مشخص است:

کاتلین

// Open File object from its file URI
val fileToShare = File(fileUriToShare)

val contentUriToShare: Uri = FileProvider.getUriForFile(
        context,
        "com.example.myapp.fileprovider",
        fileToShare
)

جاوا

// Open File object from its file URI
File fileToShare = new File(fileUriToShare);

Uri contentUriToShare = FileProvider.getUriForFile(getContext(),
        "com.example.myapp.fileprovider", fileToShare);

هنگامی که متد getUriForFile() را فرا می‌خوانید، باید مجوز ارائه‌دهنده فایل (در این مثال، "com.example.myapp.fileprovider" ) را که در عنصر <provider> مانیفست برنامه شما مشخص شده است، درج کنید. برای اطلاعات بیشتر درباره اشتراک‌گذاری فایل‌ها با URI محتوا، به اشتراک‌گذاری فایل‌ها مراجعه کنید.

برای اطلاعیه ها گوش کنید

یک برنامه معمولاً یک زیرکلاس NotificationListenerService را برای دریافت تماس از سیستم در مورد تغییرات در اعلان ها ارائه می دهد. ممکن است دستگاه‌های دارای نمایه کاری بر نحوه عملکرد NotificationListenerService با برنامه شما تأثیر بگذارند.

در پروفایل کاری

نمی‌توانید از NotificationListenerService از برنامه‌ای که در نمایه کاری اجرا می‌شود استفاده کنید. وقتی برنامه شما در نمایه کاری اجرا می شود، سیستم NotificationListenerService برنامه شما را نادیده می گیرد. با این حال، برنامه هایی که در نمایه شخصی اجرا می شوند می توانند به اعلان ها گوش دهند.

در پروفایل شخصی

وقتی برنامه شما در نمایه شخصی اجرا می شود، ممکن است برای برنامه های در حال اجرا در نمایه کاری اعلان دریافت نکنید. به‌طور پیش‌فرض، همه برنامه‌های نمایه شخصی تماس‌های برگشتی دریافت می‌کنند، اما یک سرپرست فناوری اطلاعات می‌تواند یک یا چند برنامه نمایه شخصی را که اجازه می‌دهد برای تغییرات اعلان‌ها گوش دهد، فهرست کند. سپس سیستم برنامه های غیر مجاز را مسدود می کند. در Android 8.0 (سطح API 26) یا جدیدتر، یک کنترل‌کننده خط‌مشی دستگاه (DPC) که نمایه کاری را مدیریت می‌کند ممکن است برنامه شما را از گوش دادن به اعلان‌های نمایه کاری با استفاده از روش DevicePolicyManager setPermittedCrossProfileNotificationListeners() مسدود کند. برنامه شما همچنان درباره اعلان‌های ارسال شده در نمایه شخصی تماس‌های پاسخ دریافت می‌کند.

برنامه خود را برای سازگاری با نمایه های کاری آزمایش کنید

باید برنامه خود را در یک محیط نمایه کاری آزمایش کنید تا مشکلاتی را که باعث از کار افتادن برنامه شما در دستگاهی با نمایه های کاری می شود، پیدا کنید. به طور خاص، آزمایش بر روی یک دستگاه با نمایه کاری راه خوبی برای اطمینان از اینکه برنامه شما به درستی از مقاصد استفاده می کند است: شلیک نکردن مقاصد غیرقابل کنترل، عدم پیوست کردن URI هایی که به صورت متقاطع کار نمی کنند و غیره.

ما یک نمونه برنامه به نام TestDPC ارائه کرده‌ایم که می‌توانید از آن برای راه‌اندازی یک نمایه کاری در دستگاه Android که دارای Android نسخه 5.0 (سطح API 21) و بالاتر است، استفاده کنید. این برنامه یک راه ساده برای آزمایش برنامه خود در یک محیط نمایه کاری به شما ارائه می دهد. همچنین می توانید از این برنامه برای پیکربندی نمایه کاری به صورت زیر استفاده کنید:

  • مشخص کنید کدام برنامه های پیش فرض در نمایه مدیریت شده در دسترس هستند
  • پیکربندی کنید که کدام اهداف مجاز به عبور از یک نمایه به نمایه دیگر هستند

اگر برنامه‌ای را به‌صورت دستی از طریق کابل USB روی دستگاهی که نمایه کاری دارد نصب کنید، برنامه هم در نمایه شخصی و هم در نمایه کاری نصب می‌شود. پس از نصب برنامه، می توانید برنامه را تحت شرایط زیر تست کنید:

  • اگر یک هدف معمولاً توسط یک برنامه پیش‌فرض (مثلاً برنامه دوربین) مدیریت می‌شود، سعی کنید آن برنامه پیش‌فرض را در نمایه کاری غیرفعال کنید و بررسی کنید که برنامه به‌درستی این کار را انجام می‌دهد.
  • اگر قصدی را اجرا می کنید که انتظار دارید توسط برنامه دیگری مدیریت شود، سعی کنید مجوز آن هدف برای عبور از یک نمایه به نمایه دیگر را فعال و غیرفعال کنید. بررسی کنید که برنامه تحت هر دو شرایط به درستی عمل می کند. اگر هدف اجازه عبور بین نمایه‌ها را ندارد، رفتار برنامه را هم زمانی که کنترل‌کننده مناسبی در نمایه برنامه وجود دارد و هم زمانی که وجود ندارد بررسی کنید. برای مثال، اگر برنامه شما یک هدف مرتبط با نقشه را اجرا می کند، هر یک از سناریوهای زیر را امتحان کنید:
    • این دستگاه اجازه می دهد تا اهداف نقشه از یک نمایه به نمایه دیگر عبور کنند و یک کنترل کننده مناسب در نمایه دیگر وجود دارد (نمایه ای که برنامه روی آن اجرا نمی شود)
    • دستگاه اجازه نمی دهد اهداف نقشه بین نمایه ها عبور کنند، اما یک کنترل کننده مناسب در نمایه برنامه وجود دارد.
    • دستگاه اجازه نمی دهد که اهداف نقشه بین پروفایل ها عبور کنند و کنترل کننده مناسبی برای اهداف نقشه در نمایه دستگاه وجود ندارد.
  • اگر محتوا را به یک intent پیوست می‌کنید، بررسی کنید که این هدف هم هنگام استفاده در نمایه برنامه و هم زمانی که بین نمایه‌ها قرار می‌گیرد به درستی رفتار می‌کند.

تست روی پروفایل های کاری: نکات و ترفندها

چند ترفند وجود دارد که ممکن است در آزمایش روی یک دستگاه نمایه کاری برای شما مفید باشد.

  • همانطور که اشاره شد، وقتی یک برنامه را روی یک دستگاه نمایه کاری بارگذاری جانبی می‌کنید، روی هر دو نمایه نصب می‌شود. در صورت تمایل، می توانید برنامه را از یک نمایه حذف کنید و آن را در نمایه دیگر بگذارید.
  • اکثر دستورات مدیریت فعالیت موجود در پوسته Android Debug Bridge (adb) از پرچم --user پشتیبانی می کنند که به شما امکان می دهد مشخص کنید که کدام کاربر را اجرا کنید. با تعیین یک کاربر، می توانید انتخاب کنید که به عنوان کاربر اصلی مدیریت نشده اجرا شود یا نمایه کاری. برای اطلاعات بیشتر، به دستورات پوسته ADB مراجعه کنید.
  • برای پیدا کردن کاربران فعال در یک دستگاه، از دستور list users مدیر بسته adb استفاده کنید. اولین عدد در رشته خروجی شناسه کاربر است که می توانید از آن با پرچم --user استفاده کنید. برای اطلاعات بیشتر، به دستورات پوسته ADB مراجعه کنید.

به عنوان مثال، برای پیدا کردن کاربران در یک دستگاه، این دستور را اجرا کنید:

$ adb shell pm list users
UserInfo{0:Drew:13} running
UserInfo{10:Work profile:30} running

در این مورد، کاربر اصلی ("Drew") دارای شناسه کاربری 0 و نمایه کاری دارای شناسه کاربری 10 است. برای اجرای یک برنامه در نمایه کاری، از دستوری مانند این استفاده می کنید:

$ adb shell am start --user 10 \
-n "com.example.myapp/com.example.myapp.testactivity" \
-a android.intent.action.MAIN -c android.intent.category.LAUNCHER