Resolve the android screenshot problem

  • 2020-05-17 06:26:36
  • OfStack

I am based on the android2.3.3 system, you should know that there is a file called screencap under the android source code, frameworks\base\services\ screencap\ screencap\ screencap.cpp, you directly compile under linux (save in /system/bin/ test-screencap), Then push mobile phone through the computer to the command again test - screencap/mnt/sdcard/scapxx png can realize the screenshots.

/* 
  * Copyright (C) 2010 The Android Open Source Project 
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); 
  * you may not use this file except in compliance with the License. 
  * You may obtain a copy of the License at 
  * 
  *      http://www.apache.org/licenses/LICENSE-2.0 
  * 
  * Unless required by applicable law or agreed to in writing, software 
  * distributed under the License is distributed on an "AS IS" BASIS, 
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  * See the License for the specific language governing permissions and 
  * limitations under the License. 
  */ 

 #include <utils/Log.h> 

 #include <binder/IPCThreadState.h> 
 #include <binder/ProcessState.h> 
 #include <binder/IServiceManager.h> 

 #include <binder/IMemory.h> 
 #include <surfaceflinger/ISurfaceComposer.h> 

 #include <SkImageEncoder.h> 
 #include <SkBitmap.h> 

 using namespace android; 

 int main(int argc, char** argv) 
 { 
     if (argc != 2) { 
         printf("usage: %s path\n", argv[0]); 
         exit(0); 
     } 

     const String16 name("SurfaceFlinger"); 
     sp<ISurfaceComposer> composer; 
     getService(name, &composer); 

     sp<IMemoryHeap> heap; 
     uint32_t w, h; 
     PixelFormat f; 
     status_t err = composer->captureScreen(0, &heap, &w, &h, &f, 0, 0); 
     if (err != NO_ERROR) { 
         fprintf(stderr, "screen capture failed: %s\n", strerror(-err)); 
         exit(0); 
     } 

     printf("screen capture success: w=%u, h=%u, pixels=%p\n", 
             w, h, heap->getBase()); 

     printf("saving file as PNG in %s ...\n", argv[1]); 

     SkBitmap b; 
     b.setConfig(SkBitmap::kARGB_8888_Config, w, h); 
     b.setPixels(heap->getBase()); 
     SkImageEncoder::EncodeFile(argv[1], b, 
             SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality); 

     return 0; 
 } 

What this program actually USES is a function called capturescreen, and capturescreen calls captureScreenImplLocked
Here's the code:

status_t SurfaceFlinger::captureScreenImplLocked(DisplayID dpy, 
         sp<IMemoryHeap>* heap, 
         uint32_t* w, uint32_t* h, PixelFormat* f, 
         uint32_t sw, uint32_t sh) 
 { 
    LOGI("captureScreenImplLocked"); 
     status_t result = PERMISSION_DENIED; 

     // only one display supported for now 
     if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT)) 
         return BAD_VALUE; 

     if (!GLExtensions::getInstance().haveFramebufferObject()) 
         return INVALID_OPERATION; 

     // get screen geometry 
     const DisplayHardware& hw(graphicPlane(dpy).displayHardware()); 
     const uint32_t hw_w = hw.getWidth(); 
     const uint32_t hw_h = hw.getHeight(); 

     if ((sw > hw_w) || (sh > hw_h)) 
         return BAD_VALUE; 

     sw = (!sw) ? hw_w : sw; 
     sh = (!sh) ? hw_h : sh; 
     const size_t size = sw * sh * 4; 

     // make sure to clear all GL error flags 
     while ( glGetError() != GL_NO_ERROR ) ; 

     // create a FBO 
     GLuint name, tname; 
     glGenRenderbuffersOES(1, &tname); 
     glBindRenderbufferOES(GL_RENDERBUFFER_OES, tname); 
     glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_RGBA8_OES, sw, sh); 
     glGenFramebuffersOES(1, &name); 
     glBindFramebufferOES(GL_FRAMEBUFFER_OES, name); 
     glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, 
             GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, tname); 

     GLenum status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES); 
     if (status == GL_FRAMEBUFFER_COMPLETE_OES) { 

         // invert everything, b/c glReadPixel() below will invert the FB 
         glViewport(0, 0, sw, sh); 
         glScissor(0, 0, sw, sh); 
         glMatrixMode(GL_PROJECTION); 
         glPushMatrix(); 
         glLoadIdentity(); 
         glOrthof(0, hw_w, 0, hw_h, 0, 1); 
         glMatrixMode(GL_MODELVIEW); 

         // redraw the screen entirely... 
         glClearColor(0,0,0,1); 
         glClear(GL_COLOR_BUFFER_BIT); 

         const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ); 
         const size_t count = layers.size(); 
         for (size_t i=0 ; i<count ; ++i) { 
             const sp<LayerBase>& layer(layers[i]); 
             layer->drawForSreenShot(); 
         } 

         // XXX: this is needed on tegra 
         glScissor(0, 0, sw, sh); 

         // check for errors and return screen capture 
         if (glGetError() != GL_NO_ERROR) { 
             // error while rendering 
             result = INVALID_OPERATION; 
         } else { 
             // allocate shared memory large enough to hold the 
             // screen capture 
             sp<MemoryHeapBase> base( 
                     new MemoryHeapBase(size, 0, "screen-capture") ); 
             void* const ptr = base->getBase(); 
             if (ptr) { 
                 // capture the screen with glReadPixels() 
                 glReadPixels(0, 0, sw, sh, GL_RGBA, GL_UNSIGNED_BYTE, ptr); 
                 if (glGetError() == GL_NO_ERROR) { 
                     *heap = base; 
                     *w = sw; 
                     *h = sh; 
                     *f = PIXEL_FORMAT_RGBA_8888; 
                     result = NO_ERROR; 
                 } 
             } else { 
                 result = NO_MEMORY; 
             } 
         } 
         glEnable(GL_SCISSOR_TEST); 
         glViewport(0, 0, hw_w, hw_h); 
         glMatrixMode(GL_PROJECTION); 
         glPopMatrix(); 
         glMatrixMode(GL_MODELVIEW); 

   
     } else { 
         result = BAD_VALUE; 
     } 

     // release FBO resources 
     glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0); 
     glDeleteRenderbuffersOES(1, &tname); 
     glDeleteFramebuffersOES(1, &name); 

     hw.compositionComplete(); 

     return result; 
 } 

   
 status_t SurfaceFlinger::captureScreen(DisplayID dpy, 
         sp<IMemoryHeap>* heap, 
         uint32_t* width, uint32_t* height, PixelFormat* format, 
         uint32_t sw, uint32_t sh) 
 { 
            LOGI("into captureScreen");           
     // only one display supported for now 
     if (UNLIKELY(uint32_t(dpy) >= DISPLAY_COUNT)) 
         return BAD_VALUE; 

     if (!GLExtensions::getInstance().haveFramebufferObject()) 
         return INVALID_OPERATION; 

     class MessageCaptureScreen : public MessageBase { 
         SurfaceFlinger* flinger; 
         DisplayID dpy; 
         sp<IMemoryHeap>* heap; 
         uint32_t* w; 
         uint32_t* h; 
         PixelFormat* f; 
         uint32_t sw; 
         uint32_t sh; 
         status_t result; 
     public: 
         MessageCaptureScreen(SurfaceFlinger* flinger, DisplayID dpy, 
                 sp<IMemoryHeap>* heap, uint32_t* w, uint32_t* h, PixelFormat* f, 
                 uint32_t sw, uint32_t sh) 
             : flinger(flinger), dpy(dpy), 
               heap(heap), w(w), h(h), f(f), sw(sw), sh(sh), result(PERMISSION_DENIED) 
         { 

         } 
         status_t getResult() const { 
            LOGI("getResult()");           
          return result; 
         } 
         virtual bool handler() { 

   
    LOGI("handler()"); 
             Mutex::Autolock _l(flinger->mStateLock); 

             // if we have secure windows, never allow the screen capture 
             if (flinger->mSecureFrameBuffer) 
                 return true; 

             result = flinger->captureScreenImplLocked(dpy, 
                     heap, w, h, f, sw, sh); 

             return true; 
         } 
     }; 
    LOGI("before messagecapturescreen"); 
     sp<MessageBase> msg = new MessageCaptureScreen(this, 
             dpy, heap, width, height, format, sw, sh); 
     status_t res = postMessageSync(msg); 
     if (res == NO_ERROR) { 
         res = static_cast<MessageCaptureScreen*>( msg.get() )->getResult(); 
     } 
     return res; 
 } 

The key to this function is to use several functions of opengl to get pictures, while opengl goes to read framebuffer (this is my understanding). If you use jni to call so to take a screenshot, you can modify the screencap file a little bit and make it an so file.

It is mainly to supplement 1 how to store files and compile it. Of course, the method I said is just the method I did not mean it is very easy to use.

Storage: Create a new android project in eclipse, save it, find the android project (e.g. screencap) location and put the file in android source code development file, and then create a new folder in your project file called jni (this folder is parallel to src, screencap/jni), The C + + blog mentioned above with mk (screencap/jni/com_android_ScreenCap_ScreenCapNative cpp and screencap/jni/Android mk) file, the last in the compilation of mk files in screencap directory (screencap/Android. mk);

Compiling: compiling is a great project that requires a lot of your time and energy. Directly in the terminal into the engineering storage location, is my Administrator/Android 2.3.3 / development, then mm (Builds all of the modules in the current directory), if successful, you good luck, the terminal back to the position of the tip you APK save. Try push on the phone. But it is often unsuccessful. You may encounter some problems, such as android.permission.ACCESS_SURFACE_FLINGER, android.permission.READ_FRAME_BUFFER (because capturescrren is a function inside surfaceflinger, while surfaceflinger is a screenshot function inside surfaceflinger to read framebuffer), the relevant source code is:

status_t SurfaceFlinger::onTransact( 
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) 
 { 

     switch (code) { 
         case CREATE_CONNECTION: 
         case OPEN_GLOBAL_TRANSACTION: 
         case CLOSE_GLOBAL_TRANSACTION: 
         case SET_ORIENTATION: 
         case FREEZE_DISPLAY: 
         case UNFREEZE_DISPLAY: 
         case BOOT_FINISHED: 
         case TURN_ELECTRON_BEAM_OFF: 
         case TURN_ELECTRON_BEAM_ON: 
         { 
             // codes that require permission check 
             IPCThreadState* ipc = IPCThreadState::self(); 
             const int pid = ipc->getCallingPid(); 
             const int uid = ipc->getCallingUid(); 
             if ((uid != AID_GRAPHICS) && !mAccessSurfaceFlinger.check(pid, uid)) { 
                 LOGE("Permission Denial: " 
                         "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); 
                 return PERMISSION_DENIED; 
             } 
             break; 
         } 
         case CAPTURE_SCREEN: 
         { 
             // codes that require permission check 
             IPCThreadState* ipc = IPCThreadState::self(); 
             const int pid = ipc->getCallingPid(); 
             const int uid = ipc->getCallingUid(); 
             if ((uid != AID_GRAPHICS) && !mReadFramebuffer.check(pid, uid)) { 

                 LOGE("Permission Denial: " 
                         "can't read framebuffer pid=%d, uid=%d", pid, uid); 
                 return PERMISSION_DENIED; 
             } 

             break; 
         } 
     } 

     status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags); 
     if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) { 
         CHECK_INTERFACE(ISurfaceComposer, data, reply); 
         if (UNLIKELY(!mHardwareTest.checkCalling())) { 
             IPCThreadState* ipc = IPCThreadState::self(); 
             const int pid = ipc->getCallingPid(); 
             const int uid = ipc->getCallingUid(); 

    LOGI("err"); 
             LOGE("Permission Denial: " 
                     "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); 
             return PERMISSION_DENIED; 
         } 
         int n; 
         switch (code) { 
             case 1000: // SHOW_CPU, NOT SUPPORTED ANYMORE 
             case 1001: // SHOW_FPS, NOT SUPPORTED ANYMORE 
                 return NO_ERROR; 
             case 1002:  // SHOW_UPDATES 
                 n = data.readInt32(); 
                 mDebugRegion = n ? n : (mDebugRegion ? 0 : 1); 
                 return NO_ERROR; 
             case 1003:  // SHOW_BACKGROUND 
                 n = data.readInt32(); 
                 mDebugBackground = n ? 1 : 0; 
                 return NO_ERROR; 
             case 1004:{ // repaint everything 
                 Mutex::Autolock _l(mStateLock); 
                 const DisplayHardware& hw(graphicPlane(0).displayHardware()); 
                 mDirtyRegion.set(hw.bounds()); // careful that's not thread-safe 
                 signalEvent(); 
                 return NO_ERROR; 
             } 
             case 1005:{ // force transaction 
                 setTransactionFlags(eTransactionNeeded|eTraversalNeeded); 
                 return NO_ERROR; 
             } 
             case 1006:{ // enable/disable GraphicLog 
                 int enabled = data.readInt32(); 
                 GraphicLog::getInstance().setEnabled(enabled); 
                 return NO_ERROR; 
             } 
             case 1007: // set mFreezeCount 
                 mFreezeCount = data.readInt32(); 
                 mFreezeDisplayTime = 0; 
                 return NO_ERROR; 
             case 1010:  // interrogate. 
                 reply->writeInt32(0); 
                 reply->writeInt32(0); 
                 reply->writeInt32(mDebugRegion); 
                 reply->writeInt32(mDebugBackground); 
                 return NO_ERROR; 
             case 1013: { 
                 Mutex::Autolock _l(mStateLock); 
                 const DisplayHardware& hw(graphicPlane(0).displayHardware()); 
                 reply->writeInt32(hw.getPageFlipCount()); 
             } 
             return NO_ERROR; 
         } 
     } 
     return err; 
 } 

This is just the beginning! You will find that you can still have this problem even if you add the appropriate permissions in xml. Why? Find the relevant code in the packageManger file:

 int checkSignaturesLP(Signature[] s1, Signature[] s2) { 
        if (s1 == null) { 
            return s2 == null 
                    ? PackageManager.SIGNATURE_NEITHER_SIGNED 
                    : PackageManager.SIGNATURE_FIRST_NOT_SIGNED; 
        } 
        if (s2 == null) { 
            return PackageManager.SIGNATURE_SECOND_NOT_SIGNED; 
        } 
        HashSet<Signature> set1 = new HashSet<Signature>(); 
        for (Signature sig : s1) { 
            set1.add(sig); 
        } 
        HashSet<Signature> set2 = new HashSet<Signature>(); 
        for (Signature sig : s2) { 
            set2.add(sig); 
        } 
        // Make sure s2 contains all signatures in s1. 
        if (set1.equals(set2)) { 
            return PackageManager.SIGNATURE_MATCH; 
        } 
        return PackageManager.SIGNATURE_NO_MATCH; 
    } 

   

 // Check for shared user signatures 
        if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) { 
            if (checkSignaturesLP(pkgSetting.sharedUser.signatures.mSignatures, 
                    pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) { 
                Slog.e(TAG, "Package " + pkg.packageName 
                        + " has no signatures that match those in shared user " 
                        + pkgSetting.sharedUser.name + "; ignoring!"); 
                mLastScanError = PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; 
                return false; 
            } 
        } 
        return true; 

   
    private boolean verifySignaturesLP(PackageSetting pkgSetting, 
            PackageParser.Package pkg) { 

        // Check for shared user signatures 
        if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) { 

        if (checkSignaturesLP(pkgSetting.sharedUser.signatures.mSignatures, 
                    pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) { 

            Slog.e(TAG, "Package " + pkg.packageName 
                        + " has no signatures that match those in shared user " 
                        + pkgSetting.sharedUser.name + "; ignoring!"); 
                mLastScanError = PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; 
                return false; 

            } 
        } 
        return true; 
    } 

You enter adb logcat | grep PackageManager at the terminal and you will find that these two permissions are not given to you at all. My understanding is that the program needs permissions, and then apk still needs permissions. So how do you give permission to apk? Two methods, one is to add platform1 to screencap/ Android.mk, and then go back to mm. Another way is through sign. Both of these methods give apk system permissions, but I've tried both of them, and both of them have a problem. It shows the signature is incompatible with adb install, and if you look at the source code, uid does not match gid. These are the problems that I have found during this period of time. You can communicate with each other if you have any questions.
Let's talk about a few simple screenshots of the application layer. It's very simple, just a few function calls

View view = getWindow().getDecorView();   
       Display display = this.getWindowManager().getDefaultDisplay();   
       view.layout(0, 0, display.getWidth(), display.getHeight());   
       view.setDrawingCacheEnabled(true);// Allows the current window to save the cache information, so   getDrawingCache() The method returns 1 a Bitmap   
       Bitmap bmp = Bitmap.createBitmap(view.getDrawingCache()); 

My understanding of this program is that it only intercepts the current activity, which means that if you run this program it intercepts the current screen of your program. Let's say you make a button and call this method 5 seconds after the button is clicked (let's say your activity is called screen). When you click on the button, then you click on the home or the back button, and five seconds later, which program will intercept your current screen? No! It just intercepts the screen that's running in the background and the activity.

These are just my 1 point small summary, and there must be something wrong, I hope you 1 up to solve the problem of screenshots!

Related articles: