반응형

[Snippet]

"애플리케이션 정보" 보기로 이동

                Intent intent = new Intent();
                intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                intent.setData(Uri.fromParts("package""package name", null));
                startActivity(intent);

 

[Sample]

 - 앱 자신의 "애플리케이션 정보" 보기로 이동

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                intent.setData(Uri.fromParts("package", getPackageName(), null));
                startActivity(intent);
            }
        });
    }
}

 

 - 현재 앱에서 TISTORY앱의 "애플리케이션 정보" 보기로 이동

public class MainActivity extends AppCompatActivity {
    private static final String TISTORY_PACKAGE_NAME = "net.daum.android.tistoryapp";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                intent.setData(Uri.fromParts("package", TISTORY_PACKAGE_NAME, null));
                startActivity(intent);
            }
        });
    }
}

 

프로젝트 화일

AppInfoTest.zip

 

[실행결과]

 

      
반응형

안녕하세요. Simple& Happy Dev입니다.

최근에 Google Mobile Ads(AdMob) API 버전이 17.0.0으로 업데이트되었습니다.

기존 15.0.1에서 17.0.0으로 업데이트하려고 하는데 에러가 발생했고, 해결방법에 대해서 적어봅니다.

build.gradle (Module) 파일에 가보면 15.0.1 버전이 하이라이트 되어서 새 버전이 존재한다는 것을 알려주고 있습니다.
(Android Lint의 Obsolete Gradle Dependency Detector가 체크된 경우에 해당)

 

"com.google.android.gms:play-services-ads:17.0.0"으로 수정 후 Sync를 해줍니다.

그런데, 아래와 같이 Sync 에러 메시지가 나왔습니다.

일단 Dependency tree를 확인해봅니다.

Terminal 실행 후 아래와 같이 명령을 입력합니다.

저장된 dependencies.txt 파일을 열어서 보면 dependency 확인이 가능합니다.

Sync 에러 메시지에 나왔던 "com.google.android.gms:play-services-basement"는 com.google.android.gms:play-services-ads 뿐만 아니라 다른 곳에서도 Dependency가 있는 것을 확인할 수 있습니다.

[해결방법]

com.google.android.gms:play-services-ads 외에 Dependency가 확인된 다른 곳의 API 라이브러리도 같이 업데이트를 해줍니다.

보통 구글에서는 Dependency를 고려서 관련된 API 라이브러리를 같이 업데이트해주기 때문에 위의 경우 문제가 된 firebase-core도 하이라이트 되어서 새 버전이 존재한다는 것을 알려주고 있습니다.

이것도 새 버전인 "com.google.firebase:firebase-core:16.0.4"로 수정 후 Sync해주면 Sync error가 발생하지 않습니다.

다시 빌드를 하면 정상적으로 빌드됩니다.

하지만, 앱이 실행되면 AdMob이 초기화될 때 아래와 같이 RuntimeException으로 Force Close 처리됩니다.

로그에 보면 AndroidManifest에 App ID를 추가해야 한다고 되어 있습니다.
(https://developers.google.com/admob/android/quick-start#update_your_androidmanifestxml)

이번에 업데이트된 17.0.0 버전부터는 AdMob UI에서 App ID를 찾을 수 있게 하기 위해서 추가해줘야 합니다.

[해결방법]

사이트에 가보면 아래와 같이 추가해줘야 한다고 나옵니다. 

<from Google AdMob site>

여기서 [ADMOB_APP_ID] 부분에 ca-app-pub-3940256099942544~3347511713 와 같은 형식의 App ID를 넣어줍니다.

※App ID는 "AdMob(https://apps.admob.com/) - 앱 - 앱 설정"에 가면 확인할 수 있습니다.


<manifest>
   
<application>
       
<!-- Sample AdMob App ID: ca-app-pub-3940256099942544~3347511713 -->
       
<meta-data
           
android:name="com.google.android.gms.ads.APPLICATION_ID"
           
android:value="[ADMOB_APP_ID]"/>

   
</application>
</manifest>

위에 추가만 해주시면 앱에서 오류 없이 AdMob이 정상적으로 동작하는 것을 확인하실 수 있습니다.

감사합니다.

조금이나마 도움이 되셨으면 아래 공감 버튼을 눌러주세요.
(If this article helps you, please press the button below.)

반응형

안녕하세요. Simple& Happy Dev입니다.

최신 SDK Tool 몇 개 업데이트하고 업그레이드된 라이브러리 몇 개도 업데이트해서 테스트한 후 다시 이전 상태로 돌리고, 클린 빌드를 했는데, Unresolved dependencies (2 errors) 오류가 발생했습니다.

에러 내용은 아래처럼 Failed to resolve: play-services-basement 라고 나오는데, com.google.android.gms:play-services-basement에 문제가 있어서 발생한 현상입니다.

Gradle window(View - Tool windows - Gradle 또는 Tool Buttons가 활성화 되어 있는 경우 오른편 위의 Gradle 아이콘 클릭)에서 androidDependencies를 실행하면 아래와 같이 오류를 확인할 수 있었습니다.

:app:androidDependencies FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:androidDependencies'.
> Could not resolve all artifacts for configuration ':app:debugCompileClasspath'.
   > Could not find play-services-basement.aar (com.google.android.gms:play-services-basement:15.0.1).
     Searched in the following locations:
         https://jcenter.bintray.com/com/google/android/gms/play-services-basement/15.0.1/play-services-basement-15.0.1.aar

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 0s

이 오류는 jcenter Repository에서 "com.google.android.gms:play-services-basement:15.0.1@aar"를 못 가져와서 오류를 발생시킨 것입니다.

몇 가지 테스트를 해보니 두 가지 방법으로 해결할 수 있었습니다.

#1 gms 상위버전으로 업데이트  

최근에 릴리즈된 gms의 상위 버전으로 라이브러리 업데이트 후 Sync Project with Gradle Files (툴바의 3번째 버튼 or File 메뉴에서 선택)를 실행하고 빌드하면 오류가 발생하지 않습니다.

여기서 주의하실 점은 androidDependencies를 실행하셔서 "com.google.android.gms:play-services-basement:15.0.1@aar"가 존재하는 라이브러리는 모두 상위 버전으로 업그레이드해 주셔야 합니다. 

#2 Repository 조정

gms 상위 버전에서는 문제없는 것으로 보아서 jcenter Repository의 15.0.1을 가져오지 못하는 것으로 보입니다.

Gradle 빌드시 제일 위의 Repository부터 체크하기 때문에 jcenter Repository를 뒤쪽으로 이동하게 해서 Google의 Maven Repository에서 "com.google.android.gms:play-services-basement:15.0.1@aar"를 가져오게 하면 됩니다.

build.gradle (Project) 파일에서 아래 두 가지중 하나를 선택해서 수정하고 Sync Project with Gradle Files를 실행하고 빌드하면 오류가 발생하지 않습니다.


Gradle 버전 4.1 이상


Gradle 버전 4.1 미만


 

※앱에서 상위 버전으로 업데이트를 하면 문제가 될 경우에만 Repository를 조정하는 방법을 이용하세요.
   될 수 있으면 구글에서 권장하는 jcenter를 처음에 두는 것으로 Repository 순서를 유지하고 "상위 버전으로 업데이트"
   하는 방법을 권장해드립니다.

위와 유사한 문제 발생으로 고생하고 계신다면 한 번 참조하시어 테스트해보시길 바랍니다.

조금이나마 도움이 되셨으면 아래 공감 버튼을 눌러주세요.
(If this article helps you, please press the button below.)

반응형

안녕하세요. Simple& Happy Dev입니다.

Google Play에 있는 앱이 자신의 폰에서는 검색이 되는데, 다른 사람 폰에서는 검색되지 않는 경우가 있습니다.

드물지만 같은 종류 폰임에도 이런 경우가 있습니다.

이 글에서는 이런 현상이 발생하는 이유에 대해서 설명해 드리며 앱 개발자분들을 위한 솔루션을 제시하고자 합니다. 

앱 개발해서 Google Play에 등록하시려고 하는 분들은 꼭 알고 계셔야 하는 내용입니다.

만약 내 폰에 나침반 센서(Compass sensor)가 있는데, 이것을 이용해서 응용 앱을 개발하였고, 이것을 다른 사람과 공유를 하기 위해서 Google Play에 등록했습니다. 등록된 것도 확인하였고, 다른 사람 폰에서도 검색이 되고 다운로드도 가능합니다.

그런데, 아무런 문제가 없을까요?

같은 폰을 가지고 있는 경우에게는 문제가 없을 것입니다. 나침반 센서가 있는 다른 폰에서도 문제가 없을 것입니다.

하지만, 나침반 센서가 없는 폰은 어떨까요? 아마 동작을 하지 않거나, 오류가 발생할 수도 있습니다.

그러면, 어떻게 하면 될까요?

네, 나침반 센서가 있는 사람들만 다운받을 수 있게 하면 되는 겁니다. Google Play에서 나침반 센서를 가진 폰에 나침반 센서 기능이 있는 앱을 매칭시켜주면 되는 것이죠.

정리하면 앱 매칭을 위해서 단말기 회사와 앱 개발자는 아래와 같이 해주면 됩니다.
- 단말기 회사: 해당 폰이 가진 기능(Feature)을 Google Play에 알려준다.
- 앱개발자: 해당 앱에 사용할 기능(Feature)를 Google Play에 알려준다.

Google Play에서 앱을 필터링할 수 있게 앱의 매니페스트 화일(AndroidManifest.xml)에 몇 가지 element가 존재합니다.

<uses-feature>, <uses-configuration>, <uses-sdk>, <uses-library>, <supports-screens> 등이 여기에 해당됩니다.

그럼, 위에 언급한 나침반 센서의 경우 아래와 같이 넣어주면 나침반 센서가 없는 폰에는 더 이상 검색되지 않고, 있는 폰에서만 검색됩니다.

<uses-feature android:name="android.hardware.sensor.compass" />

그러면, 특정 기기에서 검색되지 않는 이유는 앱과 단말기 간의 매칭이 되지 않았기 때문입니다.

드물지만 같은 폰인데, 검색되지 않는 경우는 어떤 경우일까요?

이런 현상이 발생한 가능성이 높은 것은 같은 폰인데 안드로이드 버전이 틀릴 경우입니다.

A폰은 안드로이드 5.0이고, B폰은 OS 업그레이드를 해서 안드로이드 6.0이라고 하면 아래와 같이 앱의 element가 추가된 경우에 이런 현상이 발생할 수 있습니다. 아래 API 레벨 23을 실행할 수 있는 최소 레벨로 정했기 때문에 API 레벨 21인 A폰에서는 검색이 되지 않습니다.

<uses-sdk
android:minSdkVersion="23"
android:targetSdkVersion="23" />

앱의 기능을 지원하지 않는 폰에서 검색되지 않는 것은 당연합니다.

하지만, 위에 있는 sdk 경우처럼 특별한 사유가 없이 필터링하는 경우에 기능을 지원함에도 검색되지 않습니다.

문제가 되는 기능이 앱 일부에 해당하는 경우에 조건에 따른 기능 동작 설정 및 UI 변경작업을 하는 것도 한가지 방법입니다.

만약 조깅앱을 만들었는데, 주기능이 걸음 카운터랑 GPS로 이동한 궤적을 보여주는 것이고, 보조 기능으로 이동한 방위를 표시해주는 것이라고 하면 나침반 센서가 없는 폰에서는 보조 기능이 나오지 않도록 UI 변경을 해주면 되는 것입니다.

if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_SENSOR_COMPASS)) {
detachCompass();
}

그리고, 아래와 같이 해주면 나침반 센서가 없는 폰에서도 검색이 됩니다.

<uses-feature android:name="android.hardware.sensor.compass" android:required="false" />

여기서 질문이 나올 수 있는 게 "이렇게 feature 여부를 앱에서 처리하는 거라면 위에 android:required="false"를 하지 않고, 위의 <uses-feature> element를 삭제해도 되지 않는가?"라는 것인데요.
맞습니다. 이럴 경우에는 삭제해도 무방합니다.

하지만, Permission이 필요한 feature는 삭제할 경우(예: 카메라)에 검색에서 나오지 않아서 꼭 명시를 해줘야 합니다.

위의 나침반 센서의 경우 Permission이 필요하지 않기 때문에 저렇게 하더라도 검색되어서 나옵니다.


필터링 element를 많이 사용하게 되면 앱이 요구하는 기능과 그것을 지원하는 폰의 매칭이 더 잘 맞게 될 겁니다.

내가 만든 앱의 화면이 원하는 모양대로 보이는 폰에서만 검색되게 하려고 <supports-screens>를 사용하거나 <compatible-screens>를 사용할 수도 있습니다.

아이폰과 달리 안드로이드 폰은 여러 해상도 및 화면크기에 따른 화면비율이 차이가 나고 이를 고려한 해상도별 이미지 추가 및 Layout 처리를 해줘야 합니다.

이로 인하여 UI 구성상 저해상도는 힘들 것 같다고 생각되면, 저해상도를 가진 단말에서는 검색 안 되게 필터링하면 됩니다.

<supports-screens
android:smallScreens="false"
android:normalScreens="true"
android:largeScreens="true"
android:xlargeScreens="true"

android:anyDensity="true" />

주의하셔야 할 것은 매칭을 위해서 필터링 element를 여러 개 사용하다보면 특정 기기에서 검색이 되지 않는 경우가 발생합니다. 특히, screen 관련한 element를 잘못하게 되면 기능을 지원하는데도 검색이 안 되는 경우가 있으니 주의가 필요합니다.


특정 기기에서 검색되지 않는 것을 해결하는 방법은 지원하지 않는 폰을 PC에 연결 후 아래와 같은 명령을 실행해 봅니다.

adb shell dumpsys package f > features.txt

adb shell dumpsys package l > libraries.txt

그러면, 그 폰에서 지원하는 Feature와 Library 목록이 나옵니다. 아래는 가지고 있는 폰에서 위의 명령을 실행한 결과입니다.

Features:
  android.hardware.sensor.proximity
  com.samsung.android.sdk.camera.processor
  com.sec.feature.motionrecognition_service
  android.hardware.sensor.accelerometer
  android.hardware.faketouch
  com.samsung.feature.virtualscreen
  android.hardware.usb.accessory
  android.software.backup
  android.hardware.touchscreen
  android.hardware.touchscreen.multitouch
  android.software.print
  com.samsung.feature.device_category_phone_low_end
  android.software.voice_recognizers
  android.hardware.fingerprint
  com.samsung.android.knox.knoxsdk
  com.sec.feature.slocation version=3
  com.samsung.android.sdk.camera.processor.haze
  android.hardware.opengles.aep
  com.sec.feature.fingerprint_manager_service
  com.sec.feature.overlaymagnifier
  android.hardware.bluetooth
  android.hardware.camera.autofocus
  android.hardware.telephony.gsm
  android.software.sip.voip
  com.samsung.feature.samsung_experience_mobile
  android.hardware.usb.host
  android.hardware.audio.output
  android.software.verified_boot
  android.hardware.camera.flash
  android.hardware.camera.front
  android.hardware.screen.portrait
  android.hardware.nfc
  com.sec.android.mdm
  android.software.home_screen
  com.samsung.android.sdk.camera.processor.panorama
  android.hardware.microphone
  com.samsung.android.sdk.camera.processor.effect
  android.software.vr.mode
  android.hardware.bluetooth_le
  android.hardware.touchscreen.multitouch.jazzhand
  android.software.app_widgets
  android.software.input_methods
  android.hardware.vulkan.version version=4194307
  android.software.device_admin
  android.hardware.camera
  android.hardware.screen.landscape
  com.samsung.android.authfw
  com.samsung.android.api.version.2402
  com.samsung.android.api.version.2403
  com.sec.feature.cover
  com.sec.feature.findo
  android.software.managed_users
  android.software.webview
  android.hardware.camera.any
  com.samsung.android.sdk.camera.processor.dof
  com.samsung.android.sdk.camera.processor.gif
  com.samsung.android.sdk.camera.processor.hdr
  com.samsung.android.sdk.camera.processor.lls
  android.software.connectionservice
  android.hardware.touchscreen.multitouch.distinct
  android.hardware.location.network
  com.sec.android.secimaging
  android.software.sip
  android.hardware.wifi.direct
  android.software.live_wallpaper
  android.software.freeform_window_management
  android.hardware.nfc.hcef
  android.hardware.location.gps
  com.sec.feature.fingerprint_ui version=3
  android.software.midi
  android.hardware.nfc.hce
  android.hardware.wifi
  android.hardware.location
  android.hardware.vulkan.level version=1
  android.hardware.telephony

Libraries:
  smatlib -> (jar) /system/framework/smatlib.jar
  sec_platform_library -> (jar) /system/framework/sec_platform_library.jar
  com.samsung.device -> (jar) /system/framework/com.samsung.device.jar
  SemAudioThumbnail -> (jar) /system/framework/SemAudioThumbnail.jar
  com.sec.esecomm -> (jar) /system/framework/esecomm.jar
  com.sec.android.mdm.gearpolicymanager -> (jar) /system/framework/sagearpolicymanager.jar
  vsimmanager -> (jar) /system/framework/vsimmanager.jar
  sechardware -> (jar) /system/framework/sechardware.jar
  com.google.android.media.effects -> (jar) /system/framework/com.google.android.media.effects.jar
  com.samsung.android.knox.knoxsdk -> (jar) /system/framework/knoxsdk.jar
  com.sec.android.app.multiwindow -> (jar) /system/framework/com.sec.android.app.multiwindow.jar
  seccamera -> (jar) /system/framework/seccamera.jar
  com.gsma.services.nfc -> (jar) /system/framework/com.gsma.services.nfc.jar
  secdmb -> (jar) /system/framework/secdmb.jar
  semcamera -> (jar) /system/framework/semcamera.jar
  com.samsung.bbc -> (jar) /system/framework/com.samsung.bbccommon.jar
  touchwiz -> (jar) /system/framework/touchwiz.jar
  com.android.location.provider -> (jar) /system/framework/com.android.location.provider.jar
  secimaging -> (jar) /system/framework/secimaging.jar
  secvision -> (jar) /system/framework/secvision.jar
  semextendedformat -> (jar) /system/framework/semextendedformat.jar
  sec_feature -> (jar) /system/framework/sec_feature.jar
  com.sec.android.mdm -> (jar) /system/framework/sec_edm.jar
  com.samsung.android.nfc.mpos -> (jar) /system/framework/com.samsung.android.nfc.mpos.jar
  com.android.future.usb.accessory -> (jar) /system/framework/com.android.future.usb.accessory.jar
  sws -> (jar) /system/framework/sws.jar
  saiv -> (jar) /system/framework/saiv.jar
  android.ext.shared -> (apk) com.google.android.ext.shared
  javax.obex -> (jar) /system/framework/javax.obex.jar
  secmediarecorder -> (jar) /system/framework/secmediarecorder.jar
  com.google.android.gms -> (apk) com.google.android.gms
  android.ext.services -> (apk) com.google.android.ext.services
  simageis -> (jar) /system/framework/simageis.jar
  seclvbmanager -> (jar) /system/framework/seclvbmanager.jar
  com.sec.android.app.minimode -> (jar) /system/framework/com.sec.android.app.minimode.jar
  device_api -> (jar) /system/framework/device_api.jar
  imsmanager -> (jar) /system/framework/imsmanager.jar
  scamera_sdk_util -> (jar) /system/framework/scamera_sdk_util.jar
  com.publicnfc -> (jar) /system/framework/com.publicnfc.jar
  com.dsi.ant.antradio_library -> (jar) /system/framework/com.dsi.ant.antradio_library.jar
  svemanager -> (jar) /system/framework/svemanager.jar
  org.simalliance.openmobileapi -> (jar) /system/framework/org.simalliance.openmobileapi.jar
  android.test.runner -> (jar) /system/framework/android.test.runner.jar
  sercsapi -> (jar) /system/framework/sercsapi.jar
  com.google.android.maps -> (jar) /system/framework/com.google.android.maps.jar
  com.sec.android.visualeffect -> (jar) /system/framework/com.sec.android.visualeffect.jar
  com.broadcom.nfc -> (jar) /system/framework/com.broadcom.nfc.jar
  org.apache.http.legacy -> (jar) /system/framework/org.apache.http.legacy.jar
  com.android.nfc_extras -> (jar) /system/framework/com.android.nfc_extras.jar
  com.android.media.remotedisplay -> (jar) /system/framework/com.android.media.remotedisplay.jar
  allshare -> (jar) /system/framework/allshare.jar
  com.sec.smartcard.auth -> (jar) /system/framework/secsmartcard.jar
  secimshttpclient -> (jar) /system/framework/secimshttpclient.jar
  com.android.mediadrm.signer -> (jar) /system/framework/com.android.mediadrm.signer.jar
  libvtmanagerjar -> (jar) /system/framework/libvtmanagerjar.jar
  rcsopenapi -> (jar) /system/framework/rcsopenapi.jar

Feature의 경우 android로 시작하는 것을 확인하시면 되고, Library의 경우 폰 내부적으로만 사용하는 것도 포함되어 있습니다.

앱에서 필터링한 기능이 위의 목록에 존재하는지 확인 후 해당 element를 제거 또는 수정을 해주시면 됩니다.

위에 해당하지 않는 경우에는 screen 관련한 element의 영향일 가능성이 큽니다.

특별한 경우가 아니면 compatible-screens는 사용하지 말고, supports-screens의 경우 requiresSmallestWidthDp, compatibleWidthLimitDp, largestWidthLimitDp 속성 사용 시 필터링되어서 검색되지 않는 경우가 있으니 제외하고 확인해보시길 바랍니다.

앱이 검색되지 않는 폰을 구하기 어려우면 "Google Play Console - 출시 관리 - 기기 카탈로그"에 들어가서 해당하는 기기를 찾아서 Feature와 Library를 확인할 수 있지만, 실제 기기에서 확인하는 것을 권장해 드립니다.

참고로 Google Play Console에서 폰과 앱이 매칭되지만, 검색되지 않고 제외하도록 만들 수도 있습니다.
기기 카탈로그에 해당 폰을 선택해서 "제외"를 누르시면 됩니다.

이상으로 Google Play에 등록한 앱이 특정 기기에서 검색되지 않는 이유 및 해결방법에 대해서 알아보았습니다. 

조금이나마 도움이 되셨으면 아래 공감 버튼을 눌러주세요.
(If this article helps you, please press the button below.)

반응형

안녕하세요. Simple& Happy Dev입니다.

새벽에 자기 전에 휴대폰 잠금 해제를 했는데, 인터넷 브라우저에 제가 띄운 적이 없는 화면(광고)이 나왔는데, 저번에도 비슷한 현상이 발생한 기억이 있어서 이번에 꼭 어떤 앱이 웹 브라우저에 광고를 띄우는지 찾아보려고 Bug report 실행해서 로그를 저장해두었습니다.

※Bug report 생성 방법
  설정(Settings)-개발자 옵션(Developer options)-버그 보고서 작성(Submit bug report)-대화형 보고서(Interactive report)누르면 알림창에 진행되고 있는걸 확인하실 수 있습니다.
  진행하는 도중에 문제를 일으킨 화면으로 가서 해당 알림창의 스크린샷(Screenshot)을 누르면 현재 화면이 저장됩니다.
  또 알림창의 세부정보(Details)를 누르면 "버그 제목", "버그 요약"을 입력하실 수 있습니다.
  알림창에서 진행이 완료되면, 알림창 화면을 누르면 공유할 수 있다는 메시지가 나옵니다.
  이메일 또는 클라우드에 저장하면 됩니다.

※개발자 옵션 활성화 방법
   설정(Settings)-휴대전화 정보(About phone)-소프트웨어정보(Software Information)-빌드번호(Build number) 항목을 7번 정도 클릭하면 활성화됩니다.

먼저 광고가 띄워진 인터넷 브라우저를 확인해 보니깐 아래처럼 "현대캐피탈 인증중고차"라는 제목의 광고가 있었습니다.

 


분석의 시작은 그 광고의 주소를 찾아서 로그에 존재하는지 여부였습니다.
로그를 확인해보니, 다행히 아래와 같이 Back stack에 해당 주소의 흔적을 확인하였습니다.

ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
...
Task id #17929
* TaskRecord{e7f0c35d0 #17929 I=com.sec.android.app.sbrowser/.SBrowserLauncherActivity U=0 StackId=1 sz=1}
...
      intent={act=android.intent.action.VIEW dat=https://googleads.g.doubleclick.net/aclk?sa=l&ai=CZTB1k
...
      &client=ca-app-pub4273715512220857&nx=79
...aU0&adurl=https://mcertifiedcar.hyundaicapital.com/hcsfront/ms/carList%3Futm_source%3Dgoogle%26utm_medium%3Dda%26utm_campaign%3Dinfosearch_bizapp%26utm_content%3Ddiy_brand_03_oct%26_AT%3D023E0F3A010E025FCBE9 flg=0x10400000 cmp=com.sec.android.app.sbrowser/.SBrowserLauncherActivity}
...
    * Hist #0: ActivityRecord{b617a81 u0 com.sec.android.app.sbrowser/.SBrowserLauncherActivity t17929}
            packageName=com.sec.android.app.sbrowser processName=com.sec.android.app.sbrowser
            launchedFromUid=10220 launchedFromPackage=com.lunatouch.eyefilter.pro userId=0
            ...
            Intent { act=android.intent.action.VIEW dat=https://googleads.g.doubleclick.net/aclk?sa=l&ai=CZTB1k
            ...
            adurl=https://mcertifiedcar.hyundaicapital.com/...}

삼성 인터넷 브라우저로 구글 ads 광고가 띄워진 것을 확인할 수 있습니다. 

그리고, launchedFromUid=10220 launchedFromPackage=com.lunatouch.eyefilter.pro 라고 되어있습니다.
이는 UID가 10220인 패키지 이름이 "com.lunatouch.eyefilter.pro"인 앱으로부터 호출되었다는 것을 알 수 있습니다.

그럼, 실제 잠금해제 되면서 해당앱에서 웹 브라우저에 광고를 띄운게 맞는지 이벤트 로그를 살펴봅니다.


폰이 잠금해제가 됩니다. 음, 거의 새벽2시네요. :)

10-17 01:57:52.928 10267  4196  4196 I screen_toggled: 3

잠시 후 아래처럼 브라우저로 구글 ads 광고가 만들어지는 것을 확인할 수 있습니다.

10-17 01:57:53.817  1000  3680  4320 I am_create_activity: [0,193338709,17927,com.lunatouch.eyefilter.pro/.MB,
                                                                                    NULL,NULL,NULL,343965696,com.lunatouch.eyefilter.pro]
10-17 01:57:58.707  1000  3680  6368 I am_create_activity: [0,157296164,17927,com.lunatouch.eyefilter.pro/
                                                                                    com.google.android.gms.ads.AdActivity,NULL,NULL,NULL,
                                                                                    276824064,com.lunatouch.eyefilter.pro]
10-17 01:57:58.815  1000  3680  6368 I am_create_activity: [0,162907047,17928,com.sec.android.app.sbrowser/
                                                                                    .SBrowserLauncherActivity,android.intent.action.VIEW,NULL,
                                                                                    https://googleads.g.doubleclick.net/...,268435456,
                                                                                    com.lunatouch.eyefilter.pro]
10-17 01:57:58.981  1000  3680  5436 I am_create_activity: [0,209693346,17927,com.lunatouch.eyefilter.pro/
                                                                                    com.google.android.gms.ads.AdActivity,
                                                                                    NULL,NULL,NULL,281018368,com.lunatouch.eyefilter.pro]
10-17 01:57:59.364  1000  3680  6368 I am_create_activity: [0,221988778,17929,com.sec.android.app.sbrowser/
                                                                                    .SBrowserLauncherActivity,android.intent.action.VIEW,NULL,
                                                                                    https://googleads.g.doubleclick.net/...,268435456,
                                                                                    com.lunatouch.eyefilter.pro]
10-17 01:57:59.516  1000  3680  5486 I am_create_activity: [0,166871407,17927,com.lunatouch.eyefilter.pro/
                                                                                    com.google.android.gms.ads.AdActivity
                                                                                    ,NULL,NULL,NULL,281018368,com.lunatouch.eyefilter.pro]
10-17 01:57:59.614  1000  3680  4320 I am_create_activity: [0,190937729,17929,com.sec.android.app.sbrowser/
                                                                                    .SBrowserLauncherActivity,android.intent.action.VIEW,NULL,
                                                                                    https://googleads.g.doubleclick.net/...,272629760,
                                                                                    com.lunatouch.eyefilter.pro]

그럼, 여기서 패키지 이름이 com.lunatouch.eyefilter.pro인 앱의 정보를 확인해봅니다.
위에서 Back stack 정보 중에 UID 10220 에서 브라우저를 호출(launchedFromUid=10220)했다고 말씀드렸습니다.
아래에 userId=10220이 com.lunatouch.eyefilter.pro라는 것을 확인할 수 있습니다.
현재 설치된 버전은 2.2.1입니다. 그리고, 앱에서 인터넷 접근 권한도 가지고 있습니다.

  Package [com.lunatouch.eyefilter.pro] (2881ad0):
    userId=10220
    pkg=Package{8fefac9 com.lunatouch.eyefilter.pro}
    codePath=/data/app/com.lunatouch.eyefilter.pro-6hXKiw1cg0E0Xn1IsjvHAw==
    resourcePath=/data/app/com.lunatouch.eyefilter.pro-6hXKiw1cg0E0Xn1IsjvHAw==
    legacyNativeLibraryDir=/data/app/com.lunatouch.eyefilter.pro-6hXKiw1cg0E0Xn1IsjvHAw==/lib
    primaryCpuAbi=null
    secondaryCpuAbi=null
    dexMode=success
    dexTimeStamp=2018-08-18 03:27:07
    versionCode=20180528 minSdk=17 targetSdk=25
    versionName=2.2.1
    splits=[base]
    apkSigningVersion=2
    applicationInfo=ApplicationInfo{2e3581a com.lunatouch.eyefilter.pro}
    flags=[ HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ]
    privateFlags=[ PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION ]
    dataDir=/data/user/0/com.lunatouch.eyefilter.pro
    supportsScreens=[small, medium, large, xlarge, resizeable, anyDensity]
    timeStamp=2018-05-29 11:43:46
    firstInstallTime=2017-11-22 08:07:35
    lastUpdateTime=2018-05-29 11:45:43
    installerPackageName=com.android.vending
    signatures=PackageSignatures{57035ce [62abd161]}
    installPermissionsFixed=true installStatus=1
    pkgFlags=[ HAS_CODE ALLOW_CLEAR_USER_DATA ALLOW_BACKUP ]
    install permissions:
      android.permission.SYSTEM_ALERT_WINDOW: granted=true
      android.permission.RECEIVE_BOOT_COMPLETED: granted=true
      android.permission.EXPAND_STATUS_BAR: granted=true
      android.permission.GET_TASKS: granted=true
      android.permission.INTERNET: granted=true
      android.permission.FLASHLIGHT: granted=true
      android.permission.ACCESS_NETWORK_STATE: granted=true
      android.permission.DISABLE_KEYGUARD: granted=true
      android.permission.VIBRATE: granted=true
      com.android.launcher.permission.INSTALL_SHORTCUT: granted=true
      android.permission.WAKE_LOCK: granted=true
    User 0: ceDataInode=958824 installed=true hidden=false suspended=false stopped=false notLaunched=false enabled=0 instant=false
      gids=[3003]
      runtime permissions:
        android.permission.CAMERA: granted=true
    User 150: ceDataInode=0 installed=false hidden=false suspended=false stopped=false notLaunched=false enabled=0 instant=false
      gids=[3003]


어떤 앱인지 확인할려면, 패키지 이름을 구글플레이 검색창에 입력하면 바로 나옵니다.

검색해보니 "아이필터 PRO - 블루라이트 차단"이라는 앱이었습니다.

이미 버그가 보고되었는지, 아래와 같은 공지가 올라와 있었습니다.

그런데, 구글 플레이에 올라온 최신 버전이 현재 2.2.1인데, 위의 패키지정보에서 버전(versionName=2.2.1)과 동일한 것으로 보아서 공지와 달리 최신버전에도 계속 문제가 발생하는 것으로 보입니다.

아무래도 이 앱은 재수정하거나 수정사항을 Rollback 해서 새로 apk를 등록해야 할 것 같습니다.


결론

갑자기 이상한 광고가 웹 브라우저에 나온다는 생각이 들면, 귀찮더라도 로그를 떠서 여기서 확인한 것처럼 원인이 되는 앱을 찾을 수 있으실 겁니다.

이상으로 웹 브라우저에 광고 띄우는 앱 찾는 방법에 대해서 알아보았습니다.


조금이나마 도움이 되셨으면 아래 공감 버튼을 눌러주세요.
(If this article helps you, please press the button below.)

 

 

반응형

안녕하세요. Simple& Happy Dev입니다.

앞선 글에서 손전등 앱을 개발하기 위해 Camera2 API를 이용해서 카메라 플래시를 제어하는 방법을 적어보았는데, 쉽지 않으셨을 겁니다.
손전등 앱으로는 잘 사용하지 않는 방법이니 Camera2에 대해서 공부했다고 위안으로 삼으셔도 됩니다.

이번에 알려드리는 방법은 이전 방법들 대비해서 매우 간단한  Flashlight API를 이용하는 방법입니다.

Android 6.0 이전까지 플래시는 카메라 디바이스의 보조 장치이고, 손전등에 대해서 고려되지 않았습니다.
그로 인하여 Camera/Camera2 API로 구현 시 비효율적인 부분이 많이 있었습니다.

Android 6.0(Marshmello, API level 23)부터는 손전등을 고려해서 Flashlight API가 추가되었고, 또한 안드로이드 시스템 UI에서 손전등이 자체 내장이 되기 시작했습니다.

아래와 같이 퀵세팅(QuickSetting)에 가면 손전등 타일이 추가되어 있습니다.

Flashlight API는 매우 간단하면서 카메라 권한이 필요하지 않다는 장점이 있습니다. 반면에 Android 6.0 이상의 단말기에만 동작한다는 제약성도 있습니다.

[Flashlight API 이용한 손전등 앱]

Camera API와 Camera2 API를 이용한 손전등 앱에서는 AndroidManifest.xml에 카메라 권한(android.permission.CAMERA)이 명시되어 있지 않으면 Exception이 발생합니다.

Camera / Camera2 API의 openCamera()를 호출하면, 내부에서 permission check하는 루틴에서 에러를 리턴서 Exception을 발생하게 합니다.

특히, Camera2에서는 Annotation(@RequiresPermission)으로 인하여 빌드 전에 미리 오류에 대한 확인이 가능합니다. 

@RequiresPermission(android.Manifest.permission.CAMERA)
public void openCamera(@NonNull String cameraId,
@NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)
throws CameraAccessException {

openCameraForUid(cameraId, callback, handler, USE_CALLING_UID);
}

그런데, Flashlight API의 경우 Camera2에 추가된 것이지만, 사전에 openCamera() 하지 않고 사용할 수 있습니다.
그 결과 카메라 권한이 없어도 된 것입니다.

Flashlight API(setTorchMode) 이용한 손전등 앱에서 플래시가 동작하는 과정은 아래와 같습니다.

준비작업: 플래시 존재 여부 체크 - CameraManager 객체의 인스턴스 생성

플래시 켜기: setTorchMode(cameraId, true)

플래시 끄기: setTorchMode(cameraId, false)

매우 간단하기에 바로 소스로 들어가 보겠습니다.

[소스 설명]

레이아웃 (activity_main.xml) 
 
화면 한가운데 별 모양 버튼을 하나 추가 후 이걸 누르면 플래시가 켜지도록 할 예정입니다. 그리고, 한 번 더 누르면 버튼이 토글되어서 꺼지도록 하겠습니다. 별 모양 버튼은 SDK에 내장된 리소스를 사용하시면 됩니다. (앞쪽에 있는 글들과 동일함)
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<ImageButton
android:id="@+id/ibFlashOnOff"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/btn_star_big_off"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>


자바 (MainActivity.java)

onCreate에서는 기기에 플래시가 지원하지 않으면 메시지 출력하고 3초 후 종료하도록 처리(delayedFinish())하도록 만들어 줍니다.
또한 별 모양 버튼에 대한 클릭 리스너를 만들어서 클릭 시 플래시를 켜주거나 꺼주고(flashlight()), 플래시 켜짐 상태에 따른 별 모양 이미지(켜짐: 노란색 별, 꺼짐: 흰색 별)를 변경하도록 처리해줍니다. (여기까지는 앞에 있는 부분과 공통)

Flashlight API(setTorchMode)를 사용하기 위해서 CameraManager 객체의 인스턴스를 획득합니다.

package com.example.help.nopermissionflashlight;

import android.content.pm.PackageManager;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageButton;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
private ImageButton mImageButtonFlashOnOff;
private boolean mFlashOn;

private CameraManager mCameraManager;
private String mCameraId;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)) {
Toast.makeText(getApplicationContext(), "There is no camera flash.\n The app will finish!", Toast.LENGTH_LONG).show();

delayedFinish();
return;
}

mCameraManager = (CameraManager) getSystemService(CAMERA_SERVICE);

mImageButtonFlashOnOff = findViewById(R.id.ibFlashOnOff);
mImageButtonFlashOnOff.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
flashlight();
mImageButtonFlashOnOff.setImageResource(mFlashOn ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off);
}
});
}

private void delayedFinish() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
finish();
}
}, 3500);
}

버튼을 눌러 flashlight()가 호출되면, CameraManager를 통해 카메라 id들을 가져온 후 필요한 카메라 특성을 구하고(getCameraCharacteristics), 원하는 조건(플래시 사용이 가능 & 화면 기준으로 뒤쪽에 있는 카메라)에 해당하는 카메라 id(주로 "0")를 구합니다.

이후 플래시 상태를 반전하기 위해서 mFlashOn을 토글시켜주고, setTorch()에 위에서 구한 카메라 id와 mFlashOn을 argument로 넘겨줍니다. setTroch()가 호출이 되면 mFlashOn 상태에 따라서 플래시가 켜지거나 꺼지게 됩니다.

void flashlight() {
if (mCameraId == null) {
try {
for (String id : mCameraManager.getCameraIdList()) {
CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id);
Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
if (flashAvailable != null && flashAvailable
&& lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
mCameraId = id;
break;
}
}
} catch (CameraAccessException e) {
mCameraId = null;
e.printStackTrace();
return;
}
}

mFlashOn = !mFlashOn;

try {
mCameraManager.setTorchMode(mCameraId, mFlashOn);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}

프로젝트 압축화일
NoPermissionFlashlight.zip


앱 동작 영상

 


기타정보

Flashlight API를 이용하게 되면 Flashlight를 독점적으로 소유할 수 없습니다.
이것의 의미는 Flashlight API를 사용하는 다른 앱에서 똑같이 Flashlight API를 호출하게 되면 그쪽으로 소유가 넘어간다는 의미입니다.

아래 테스트 영상과 함께 설명드리겠습니다. 영상에는 세 가지 손전등앱이 있습니다.
1. 시스템 UI인 QuickSetting의 손전등
2. Camera API로 만든 PermissionFlashlight
3. Flashlight API로 만든 NoPermissionFlashlight

[case 1]
PermissionFlashlight 앱에서 Flash를 켜고, QuickSetting의 손전등을 확인하면 아이콘이 On이지만 Dimming 된 상태입니다.
이것의 의미는 QuickSetting의 손전등에서 Flashlight를 소유해서 제어할 수 없다는 의미입니다.
그리고, QuickSetting에서 Dimming 된 손전등의 On 아이콘을 누르면, "카메라 앱에서 카메라 플래시를 사용 중이어서 손전등을 켤 수 없습니다."라는 토스트가 나옵니다. 이는 QuickSetting의 손전등에서 PermissionFlashlight 앱이 Camera API로 만들어져서 카메라 앱으로 간주하고 있습니다.

[case 2]
NoPermissionFlashlight 앱에서 Flash를 켜고, QuickSetting의 손전등을 확인하면 아이콘이 On 되어 있습니다.
이는 QuickSetting의 손전등에서 다른 앱에서 Flashlight API를 사용해서 On 시킨 것을 인지한 상태입니다.
그리고, QuickSetting에서 손전등의 On 아이콘을 누르면 Off 상태로 아이콘이 변경되면서 플래시가 꺼집니다.
이는 QuickSetting의 손전등으로 Flashlight 소유가 넘어가서 제어되고 있다는 것을 의미합니다.

그런데, NoPermissionFlashlight 앱으로 돌아오면 가운데 별 모양은 계속 On 상태로 남아있죠?
이건 왜 그럴까요? 정답은 NoPermisionFlashlight 앱에서는 다른 앱에서 Flashlight API를 사용해서 On 시킨 것을 인지하는 리스너가 구현되어 있지 않기 때문입니다. QuickSetting의 손전등처럼 리스너를 구현해주면 플래시가 꺼지면 콜백되어서 Off 상태 인지가 가능해서 아이콘 모양 변경이 가능합니다. 


이상으로 "손전등 앱 만들기 세 번째 - Flashlight API 이용방법"에 대해서 설명해 드렸습니다.
상황에 따라서 세 가지 방법을 적절히 이용하시면 손전등 앱을 구현하실 수 있을 겁니다.

조금이나마 도움이 되셨으면 아래 공감 버튼을 눌러주세요.
(If this article helps you, please press the button below.)

+ Recent posts